Seite 1 von 1
Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 14:33
von Arp
Hallo,
Ich möchte gern auf meinem TKinter Bildschirm mehrere Buttons erstellen. Deren Anzahl hängt davon ab, wieviele Elemente eine Liste hat, und die Buttons kriegen auch den Namen dieser Liste. Allerdings sollten das alles StringVar() sein, da ich die dynamisch ändern möchte.
Meine erste Idee war, das mit einer Liste zu machen die ich mit Buttons fülle.
z.b
Code: Alles auswählen
import Tkinter as Tk
liste1 = ['a','b','c']
root = Tk.Tk()
buttons = []
btext = []
for i in liste1:
btext.append(Tk.StringVar())
btext[-1].set(i)
buttons.append(Tk.Button(root,text=btext[-1].get()))
buttons[-1].pack()
Tk.mainloop()
An sich funktioniert das, es erstellt automatisch 3 buttons.
Wenn ich dann aber noch sowas wie btext[0].set('test') mache, ist die Idee das sich der Text auf dem ersten Button ändert. Das passiert jedoch nicht.
Könnt ihr mir sagen warum, oder einen besseren Ansatz liefern?
Thx.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 14:44
von BlackJack
@Arp: Warum sollte sich der Text auf dem Button denn auch ändern. Der Button bekommt von Dir eine ganz normale Zeichenkette und hat überhaupt keine Ahnung von dem `StringVar`-Exemplar. Du musst dem Button schon das `StringVar`-Exemplar über das dazu gehörende Schlüsselwort-Argument geben.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 14:48
von deets
Dein Ansatz ist an sich ganz gut. Du bist etwas umstaendlich mit dem ganzen negativen indizieren - warum bindest du die StringVar und den Button nicht erstmal an lokale Namen, und fuegst die dann am Ende hinzu?
Zu deinem Update-Problem: AFAIK kann ein Button-text nicht mit einem StringVar verbunden werden. Generell, aber erst recht nicht so wie du das tust, denn du holst ja den *normalen* string aus der Variablen um in an das text-attribut zu binden.
Ich vermute, du musst ein callback fuer die Variable anlegen, in welchem dann wiederum der Button re-konfiguriert wird.
http://effbot.org/tkinterbook/variable.htm
Ungefaehr so (ungetestet, kann sein, dass du im callback was uebergeben bekommst, dann muss der angepasst werden:
Code: Alles auswählen
for i in liste1:
v = StringVar()
v.set(i)
b = Button(root, text=i)
v.trace("w", lambda b=b, v=v: b.configure(text=v.get())
Sowas in der Art.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 14:49
von deets
BlackJack hat geschrieben:@Arp: Warum sollte sich der Text auf dem Button denn auch ändern. Der Button bekommt von Dir eine ganz normale Zeichenkette und hat überhaupt keine Ahnung von dem `StringVar`-Exemplar. Du musst dem Button schon das `StringVar`-Exemplar über das dazu gehörende Schlüsselwort-Argument geben.
Geht das? Ich meine die Doku so verstanden zu haben, dass das nur bei Labels geht, oder bei Checkboxen zB und ein paar anderen Widgets.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 14:49
von Arp
Ah, ich sehs. Aber wenn ich das zu text = btext[-1] umänder, lauten die buttonnamen PY_VAR0, PY_VAR1 und PY_VAR2.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 14:54
von Arp
Kommando zurück.
Das geht bei Buttons offenbar auch ohne Stringvar.
Wenn man irgendwo im code sowas schreibt wie button['text']='bla', dann ändert sich direkt der Text auf dem Button.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 15:16
von BlackJack
@deets: Buttons kennen neben `text` noch `textvariable`, damit geht das.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 15:24
von Arp
Hm, neues problem.
Die Buttons sollen natürlich auch eine Funktion ausführen. Z.b. den Text des Buttons in die Konsole schreiben:
Code: Alles auswählen
def printbuttonname(name):
print name
for i in liste1:
buttons.append(Tk.Button(root, text=i, command=lambda: printbuttonname(i)))
buttons[-1].pack()
Das erstellt zwar wie oben die Buttons mit dem korrekten Namen, aber der übergibt scheinbar immer nur das letzte i. Egal welchen Button ich drücke, die Funktion schreibt immer nur den Namen des letzten Buttons in die Konsole.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 15:36
von deets
@Arp: schau dir mal mein Code-Beispiel genau an, dann siehst du, was ich anders gemacht habe beim lambda. Alternativ functools.partial benutzen.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 15:47
von Arp
sorry deets, den post muss ich übersehen haben.
Aber der bringt die buttons gar nicht auf die Fläche. Und, was genau machen die b=b und v=v? So gut ist jetzt meine lambda Kentniss nicht
Aber das Stringvar problem hat sich ja eh erledigt. Nun zickt ja command rum.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 15:54
von deets
Die Argumente machen genau das, was du willst: den Wert i zum Zeitpunkt der Erstellung des Callback zu binden, statt aus dem Closure der Funktion/Methode drum rum, die dann den letzten Wert enthaelt. Das loest dein Problem.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 16:00
von Arp
Hmm, also wenn ich das richtig verstehe, sollte ich das dann so schreiben:
Code: Alles auswählen
button.append(Tk.Button(root,text=i,command=lambda name=i: printbuttonnam(name))
Oder hab ich das falsch verstanden? Denn das zeigt auch immer nur den letzten an.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 16:48
von deets
Bezweifele ich.
Code: Alles auswählen
a, b = [], []
def p(arg):
print arg
for i in xrange(10):
a.append(lambda : p(i))
b.append(lambda i=i: p(i))
for cb in (a + b):
cb()
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 21:30
von wuf
Hi Arp
Hier auch noch was zum herumspielen:
Code: Alles auswählen
from functools import partial
import Tkinter as tk
def button_callback(button_text_var):
print button_text_var.get()
if button_text_var.get() == 'c':
buttons[1].set('Button-2')
button_names = ['a','b','c']
buttons = list()
root = tk.Tk()
for button_name in button_names:
button_text_var = tk.StringVar()
button_text_var.set(button_name)
tk.Button(root, textvariable=button_text_var,
command=partial(button_callback, button_text_var)).pack(fill='x')
buttons.append(button_text_var)
buttons[0].set('Button-1')
root.mainloop()
Gruß wuf

Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 21:33
von deets
@wuf
Die Verwendung von StringVar an dieser Stelle ist voellig sinnfrei. Stattdessen kannst du gleich button_name uebergeben. Der Sinn von den *Var-Objekten liegt in ihrer Eigenschaft als Observable. Den nutzt du nicht aus.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 22:09
von wuf
OK deets
Wie würdest du mein Code-Snippet abändern, dass es ohne StringVar das genau gleiche macht. Die Bezeichnung Observable ist neu für mich. Verstehe ich nicht. Danke für deine Bemühung.
Gruß wuf

Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 22:38
von deets
Code: Alles auswählen
from functools import partial
import Tkinter as tk
def button_callback(button_text):
if button_text == 'c':
buttons[0]["text"] = 'Button-2'
button_names = ['a','b','c']
buttons = list()
root = tk.Tk()
for button_name in button_names:
button = tk.Button(root, text=button_name,
command=partial(button_callback, button_name))
button.pack(fill='x')
buttons.append(button)
buttons[0]['text'] = 'Button-1'
root.mainloop()
Ein Observable ist ein Wert, der Nachrichten verschickt, wenn er veraendert wird. Das ist genau das, was eine StringVar und Co. sind.
Der Punkt den ich machen wollte: fuer deinen illustrierten Zweck kommt man ohne extra Komplexitaet aus. Aber natuerlich sind observables sehr hilfreich: wenn man zb ein Datenmodell hat, das sich durch irgendwas anderes aendert, und dadurch dann *automatisch* die GUI upgedated wird.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Mittwoch 8. August 2012, 22:55
von wuf
@deets
Super! Besten Dank für deine hilfreiche Antwort mit Erklärung. Man hat eben nie ausgelernt. Dein modifizierte Code-Snippet verhält sich genau gleich wie mein aufgeblähtes Snippet.
Gruß

Noch eine gute Nacht.
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Donnerstag 9. August 2012, 11:37
von Arp
@deets
Ich hab meinen Fehler gefunden. Ich hatte bei mir in der callback funktion blöderweise die laufvariable, und nicht die übergebene ausgegeben, daher hab ich natürlich immer nur den letzten Wert ausgegeben. Funktioniert jetzt. Danke!
Re: Automatische erstellung von mehreren Buttons?
Verfasst: Donnerstag 9. August 2012, 13:22
von wuf
Hi deets
Habe doch noch einen kleinen Unterschied zwischen meinem und dem modifizierten Snippet festgestellt. Bei meine Snippet ist auch der Name eines modifizierten Button verfügbar. Aber aufbauend auf dem modifizierten Snippet ist dies auch ohne den Einsatz von StringVar() realisierbar. Hier noch eine Anpassung des modifizierten Snippet um dieses Verhalten zu erzielen:
Code: Alles auswählen
from functools import partial
import Tkinter as tk
def button_callback(button_obj):
if button_obj['text'] == 'c':
buttons[0]["text"] = 'Button-2'
print button_obj['text']
button_names = ['a','b','c']
buttons = list()
root = tk.Tk()
for button_name in button_names:
button = tk.Button(root, text=button_name)
button.pack(fill='x')
button.config(command=partial(button_callback, button))
buttons.append(button)
buttons[0]['text'] = 'Button-1'
root.mainloop()
Gruß wuf
