Listbox - Itemtext verändern

Fragen zu Tkinter.
Antworten
hokuspokus
User
Beiträge: 2
Registriert: Samstag 12. Juni 2010, 00:05

Guten Tag allerseits.

Ich habe eine Applikation, in welcher ich Informationen in einem Listbox-widget anzeige. Nun werden allerdings periodisch kleine Teile des GUIs up-ge-dated und ich habe nicht herausgefunden, wie ich den Text der einzelnen Zeilen auslesen und verändern kann....

Im Moment regeneriere ich die Liste bei jedem Callback-Aufruf, was einerseits ineffizient ist (ich möcht ja nur ein paar Elemente verändern), und anderseits ja alle Status-Informationen (selection, item-config etc) verloren gehen...

Weiss jemand, wie man auf den Text der einzelnen Items zugreifen kann um diesen zu verändern ?

MfG

hier ein kleiner Beispiel-Code um das Problem zu illustrieren

Code: Alles auswählen

import Tkinter as tk,tkFont,random

class win (tk.Tk):

	def __init__(self):
		tk.Tk.__init__(self)
		self.m_font= tkFont.Font(family='Courier New',size=10)
		self.m_list= tk.Listbox(self,width=30,height=30,font=self.m_font)
		self.m_list.pack()
		
		self.m_texts=[]
		self.m_counters=[]
		cols=['red','green','blue','yellow']
		for i,blah in enumerate(['ett','tva','tre','fyra','fem','sex','sjuu']):
			space= ' '.join(['' for j in range(15-len(blah))])
			num= random.randint(10,100)
			self.m_list.insert(tk.END,'%s%d'% (blah+space,num) )
			self.m_list.itemconfigure(i,background=cols[i%len(cols)])
			
			self.m_texts.append( blah+space )
			self.m_counters.append( num )

		self.after(1000,self.cb)
		
	def cb(self):
		for i in range(len(self.m_texts)):
			if i%2==0: continue
			self.m_list.delete(i)
			self.m_counters[i]+=1
			self.m_list.insert(i,'%s%d'% (self.m_texts[i],self.m_counters[i]) )
		self.after(1000,self.cb)

w=win()
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Wenn ich nichts übersehe, dann ist ein direktes Ändern (im Sinne von "Überschreiben des Inhalts") einzelner Listeneinträge nicht möglich. Was bleibt ist - wie performant das ist, habe ich nicht getestet - über die delete()- und insert()-Methoden mit entsprechenden Indizes nur einzelne Einträge zu entfernen und dann an dieser Stelle neu einzufügen. Evtl. lässt sich das durch Verwendung von StringVars noch beschleunigen.

Mehr dazu hier:
http://effbot.org/tkinterbook/listbox.htm
http://infohost.nmt.edu/tcc/help/pubs/t ... stbox.html

Ansonsten könnte eine Lösung so aussehen, dass du dir aus in einer Liste geführten Labels ein eigenes Listbox-Widget bastelst und da dann direkt auf einzelne Einträge zugreifen kannst.
BlackJack

@hokuspokus: Ein paar Anmerkungen zum Quelltext: Die Namensgebung entspricht nicht dem Style-Guide und könnte in einigen Fällen auch inhaltlich besser sein. `blah` und `cb` zum Beispiel. Für Klassennamen wird normalerweise "MixedCase" verwendet, also in diesem Fall `Win` statt `win`.

Der Präfix `m_` ist bei einigen Programmiersprachen Konvention um Attribute von lokalen Namen besser unterscheiden zu können. In Python muss man zwingend über `self` auf Attribute zugreifen, damit ist `m_` *zusätzlich* ziemlich sinnfrei.

Du benutzt Tabs zu Einrücken -- Konvention sind vier Leerzeichen pro Ebene.

Der Ausdruck bei `space` ist die bisher umständlichste Art, die mir bis jetzt untergekommen ist um ``' ' * (15 - len(blah))`` auszudrücken. Wenn ich mir die spätere Verwendung von `space` anschaue, möchtest Du hier aber vielleicht `str.ljust()` verwenden oder Dich mit den Möglichkeiten der Zeichenkettenformatierung mittels ``%`` näher auseinandersetzen:

Code: Alles auswählen

In [1380]: '%-15s' % 'spam'
Out[1380]: 'spam           '

In [1381]: 'spam'.ljust(15)
Out[1381]: 'spam           '
Ich persönlich versuche immer unnötige Indexe zu vermeiden. Das wird durch Python gefördert weil die ``for``-Schleife keine Zählschleife ist, sondern über Elemente von "iterierbaren" Objekten läuft und es einige nützliche Funktionen gibt, mit solchen Objekten zu arbeiten. Das `i` beim Befüllen der Liste wird letztendlich nur für den Index in die Farben benötigt. Mit Hilfe von `cycle()` und `izip()` aus dem `itertools`-Modul wird man das `i` los und der Quelltext ist IMHO etwas verständlicher.

In der `callback()`-Methode sollte man das "iterable" schon so wählen, dass nur ungerade Zahlen an `i` gebunden werden. Und das vielleicht mit `xrange()` statt `range()`.

Die Farben könnte man beim Ändern noch "retten".

Code: Alles auswählen

import random
import tkFont
import Tkinter as tk
from itertools import cycle, izip


class Win(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        font = tkFont.Font(family='Courier New', size=10)
        self.list = tk.Listbox(self, width=30, height=30, font=font)
        self.list.pack()
        
        self.texts = list()
        self.counters = list()
        
        colors = ['red', 'green', 'blue', 'yellow']
        blahs = ['ett', 'tva', 'tre', 'fyra', 'fem', 'sex', 'sjuu']
        for text, color in izip((t.ljust(15) for t in blahs), cycle(colors)):
            num = random.randint(10, 100)
            self.list.insert(tk.END, '%s%d' % (text, num))
            self.list.itemconfigure(tk.END, background=color)
            
            self.texts.append(text)
            self.counters.append(num)

        self.after(1000, self.callback)
    
    def callback(self):
        for i in xrange(1, len(self.texts), 2):
            self.counters[i] += 1
            old_color = self.list.itemcget(i, 'background')
            self.list.delete(i)
            self.list.insert(i, '%s%d' % (self.texts[i], self.counters[i]))
            self.list.itemconfigure(i, background=old_color)
        self.after(1000, self.callback)


def main():
    win = Win()
    win.mainloop()


if __name__ == '__main__':
    main()
hokuspokus
User
Beiträge: 2
Registriert: Samstag 12. Juni 2010, 00:05

danke für die tipps !

Code: Alles auswählen

itertools
wird definitiv zu einem meiner neuen lieblings-packages

allerdings ist mir nicht ganz klar, wie man bei python "richtigerweise" neue objekt-variabeln zuweist, ohne jedesmal abzuchecken, ob der vergebene identifier nicht bereits vom parent-objekt implementiert ist... ? daran zu denken ist ja nicht nur sinnfrei ;)


die farben waren natuerlich nur zur illustration; im konkreten beispiel ist die liste viel laenger und zusaetzlich zu den item-eigenschaften ist dann ja noch die scroll-position, selektion etc. darum waere es halt viel praktischer, den text direkt zu veraendern.

also werde ich mich wohl dranmachen, eine einfache Listbox selbst zu implementieren... .. .
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo hokuspokus

Hier etwas zum experimentieren. Das hinzufügen von Ziehleisten überlasse ich dir als Übung:

Code: Alles auswählen

# wuf_ref: listbox_with_icons_01.py

import Tkinter as tk

TRUE_ICON =\
"""
R0lGODlhGAAYAPf/ACd5Jyd9Jyh6KCh+KCiBKCiFKCiJKCmNKSmRKSmVKS6R
LiqZKiqdKi2aLS6cLjmOOTGfMSqhKiulKyuqKyutKyypLCytLCyxLCy1LCy5
LDSkNDGqMTqwOj2wPUGSQUSSREeZR0+RT0+VT1CSUFCWUFCZUFCdUEejR0K1
QkS0RES5RFChUFGlUVGpUVKtUlatVl6oXlKyUlK1UlK5UlK9Umu+a3CmcHKu
cnKwcnG2cX21fX67fn28fVPBU1PFU1TDVFTGVGjEaGzCbHPEc3PRc4S7hIfG
h4fJh4TOhInAiY7EjojNiIbQhpLDkpLEkpHQkZrVmp7TnqDKoKbPpqPQo6LZ
oqPco6XZpavRq63Tra/Ur7bitsDcwMXkxdTm1Nbq1tbs1trs2t/u39vw29/x
3+rz6u727vL58vX69fr8+v7+/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAD/ACH5BAEKAP8ALAAAAAAYABgAAAj/AIkAGUhwYA8g
PRIq7EGjIY0YQ4BkmEix4sQLGC9U0FihgoQYEi1WzIjxypYOHiV8DCmSJMYU
atQcUakSpMgMLjsyiflE5QIJLXpYdMlxA5mYQST8XNAi5AWcJDt2vBJTDQSl
C5gKfZqz4waqMaFkzZogKFeMUjsGCVO1C4SxCRKw6JFRRZAgKTpcOVpVDRo0
asycSHBgLkYagP1C6cs4Zo4DhWlg3FkVDOMwVDJTUUL4gAkaOvtabtxXiQHP
oCscEU26L5cCBT6rXF25bxgtuLVMmeLhQOwYHpGwbo0FNmwSMZQKr93aC+wB
A5CrVDJcTRgnToro0OHheQERMbLSWo7ppXGZBwMKQAcAAHzWFqVJf1BfAAD0
EC0WxGWxYweLAzDkcMOAN5ig3noDtNdCXIR15ptxz7EnIXv4xQXZhRAeiOCE
IeTAwocsmCDiiCSUKIKJJYZQog0BAQA7"""

FALSE_ICON =\
"""
R0lGODlhGAAYAPf/AI0uLpEuLpUuLpkuLp0vL50xMZ40NJg8PJ04OKEvL6Uv
L6kvL6wvL6EwMKE2NqUwMKY1NakwMK0wMLEwMLUwMLkwML0wMKFFRalLS6FV
VaVVValVVa1VVbdCQrVNTb9AQLtJSb1ISLFWVrRWVrlWVr1WVrVtbbN1db51
dcExMcUxMckxMc0xMdAxMcVLS8JWVsZVVcpWVs1XV81aWtFXV9VXV9JYWNZY
WNhXV9hYWNlcXMJvb8pubsR2dthlZdJ2dtJ+ftd4eNl4eNx4eOB3d+F/f8OG
hsKLi8yNjceTk86SktaFhdqJid2Li9Gfn9uSktySktudndClpdalpdmhodul
pdylpdqpqdypqd+urtm4uOGLi+OTk+Cvr+KxseTCwuXGxu/V1fDW1vTW1vLY
2PDc3PTY2PTc3PHg4PTg4Pfu7vjr6/nz8/r19fz29v36+v7+/gAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAD/ACH5BAEKAP8ALAAAAAAYABgAAAj/AIngGEiwIA0c
NBImjEEjhsMfOVpInDhxRYsVGFWoqLCxgscYESlWxJhR48aOFV7gmJijSBEa
K3wMmQlESAyUHie8CJkDjk84XH4KDdFxglESIbcIDSPU55oPFYxKJYHDotKf
TH2WgQJlCRAgIaRGQHrxqtamS8WOsIHRLJysaOFkWbAgQoQRMFe4LSMUjZe/
WLBMAaFAwVoVK5gsjSsUjIIIHGhsVPyTL2OfSBQQGHFTBWWfcLUGDqykQAEC
HG5WiMHGJxsql33uOJ3aY4UQPHiA6DAFTdPWcNRgICCAwwvbUo0+3uHbJxjT
pwUUjzEhavLHdiNI+emEuHQBGl4kdjcaAXsEAgWm+GyD4HsADSSk0i1cmIB9
9M1NCAjAP4P4CXZhp0AB591nhE9J8NdffAsA+Nhp93l3gU9HBADAhRmQsAB9
mmlmnwDeCSCFFgdYiCEJ5UVQgIcffiedghZamEEPI9Q4Agc45qgBBxr06KMG
GfR4QkAAOw==
"""

app_win = tk.Tk()

class ListItem(tk.Label):

    TEXT_PADX = 6
    COMPOUND = 'left'

    def __init__(self, parent, item_name, callback, **keys):

        self.true_icon = tk.PhotoImage(data=TRUE_ICON)
        self.false_icon = tk.PhotoImage(data=FALSE_ICON)
        self.prev_label_bg = None

        tk.Label.__init__(self, parent, **keys)
        self.config(image=self.false_icon, text=item_name,
            compound=self.COMPOUND,  padx=self.TEXT_PADX, anchor='w')
        self.pack(fill='x',padx=2)

        self.bind('<Enter>', self.mouse_enter)
        self.bind('<Leave>', self.mouse_leave)
        self.bind('<Button-1>', callback)

    def mouse_enter(self, event):
        print 'Enter'
        self.prev_label_bg = event.widget.cget('bg')
        event.widget.config(bg='gray')

    def mouse_leave(self, event):
        print 'Leave'
        event.widget.config(bg=self.prev_label_bg)

    def update_icon(self, status=False):
        print 'Icon Update'
        if status:
            self.config(image=self.true_icon)
        else:
            self.config(image=self.false_icon)

class MyListBox(tk.Frame):

    def __init__(self, parent, list_items):
        tk.Frame.__init__(self, parent)

        self.list_item_obj = [ListItem
            (self, item_name, self.callback)
            for item_name in list_items]

    def __getitem__(self, list_item_index):

        return self.list_item_obj[list_item_index]

    def __setitem__(self, list_item_obj, value=None):

        self.list_item_obj[list_item_obj] # = value

    def callback(self, event):
        print event.widget.cget('text')

list_items = ['Question-1',
              'Question-2',
              'Question-3',
              'Question-4',
              'Question-5',
              'Question-6',
              ]

list_box = MyListBox(app_win, list_items)
list_box.pack()

list_box[1].update_icon(True)
list_box[4].update_icon(True)

#~~ Direkte Modifikation eines List-Items
list_box[5].config(text="Modified List-Item")

app_win.mainloop()
Gruß wuf :wink:
Take it easy Mates!
Antworten