Button-Text dynamisch ändern

Fragen zu Tkinter.
Antworten
monkeyman
User
Beiträge: 2
Registriert: Sonntag 2. April 2017, 08:44

Hallo Leute,

Erstmal ein großes Lob an dieses Forum! Hier findet man wirklich sehr viel Informationen.

Ich möchte in meiner GUI einen Button haben, der beim anklicken seinen Text ändert (soll etwas an bzw. ausschalten).
Eine Lösung dazu habe ich auch schon (ich glaube sogar hier im Forum) gefunden.

Code: Alles auswählen

from Tkinter import *

def button_click(event):
	if button["text"]=="text 1":
		button["text"]="text 2"
	else:
		button["text"]="text 1"


root=Tk()
button=Button(text="text 1",width=10,height=10)
button.pack()

button.bind("<Button-1>",button_click)
root.mainloop()
Mein Problem ist nun, dass ich in meinen Code die Erstellung der GUI in eine Funktion (buildGUI() ) ausgelagert habe und nun nicht weiß,
wie ich den button von der Funktion "buildGUI" in die Funktion "button_click" bekomme.
Zumindest würde ich die Fehlermeldung so interpretieren:
Exception in Tkinter callback Traceback (most recent call last): File "C:\ProgramData\Anaconda2\lib\lib-tk\Tkinter.py", line 1542, in __call__ return self.func(*args) File "ButtonToggle.py", line 23, in button_click if button["text"]=="text 1": NameError: global name 'button' is not defined

Code: Alles auswählen


from Tkinter import *

def button_click(event):
	if button["text"]=="text 1":
		button["text"]="text 2"
	else:
		button["text"]="text 1"

def buildGUI():
	
	root=Tk()
	button=Button(text="text 1",width=10,height=10)
	button.pack()

	button.bind("<Button-1>",button_click)
	root.mainloop()

buildGUI()
Hat jemand eine gute Idee dazu? Habe schon ziemlich viel rumprobiert, aber kam zu keiner Lösung.
BlackJack

@monkeyman: Objektorientierte Programmierung (OOP) ist hier die übliche Lösung. Ansonsten kann `functools.partial()` hilfreich sein die Werte in die Rückruffunktion zu bekommen, aber OOP ist bei GUIs der vorgesehene Weg.

`bind()` für `Button` zu verwenden ist übrigens falsch. Dann verhält sich die Schaltfläche nicht so wie die Benutzer das erwarten. `Button`\s haben dafür eine `command`-Option.
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@monkeyman: Für GUI-Programmierung muß man eigentlich Objekt-orientiert programmieren.
Eine Möglichkeit wäre es, einen TwoStateButton zu erstellen, der selbst weiß, wie er sich zu verhalten hat:

Code: Alles auswählen

import Tkinter as tk

class TwoStateButton(tk.Button):
    def __init__(self, states, width, height):
        tk.Button.__init__(self, text=states[0], width=width, height=height)
        self.states = states
        self.current_state = 0
        self.bind("<Button-1>", self.click)
        
    def click(self, event):
        self.current_state += 1
        if self.current_state >= len(self.states):
            self.current_state = 0
        self["text"] = self.states[self.current_state]

def main():
    root = tk.Tk()
    button = TwoStateButton(states=["text 1", "text 2"], width=10, height=10)
    button.pack()
    root.mainloop()
    
if __name__ = '__main__':
    main()
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Sirius3: Warum nennst Du die Klasse "TwoStateButton" obgleich Du ihr eine multi-state Implementierung spendierst? :wink:
monkeyman
User
Beiträge: 2
Registriert: Sonntag 2. April 2017, 08:44

Hi Leute,
Danke für die schnellen und guten Antworten.
Ich habe mit OOP noch so meine Probleme,aber ich glaube das basteln an der GUI wird mir da noch etwas Verständniss bringen.
Die Beispiele in den Tutorials sind immer verständlich und einfach, aber es ist viel schwieriger, den Kram selber anzuwenden oder Code zu verstehen, wo das jemand genutzt hat.
Der Code für den "Two state Button " lief bei mir nicht richtig (Button-Text hat sich nicht geändert), aber ich habe ihn glaube ich halbwegs verstanden und ein wenig abgewandelt (Ich benutz übrigens Python 2.7, falls das relevant ist). Jetzt funktioniert er bei mir zumindest. Vielen DANK!
Ich würde auch gerne "command" an Stelle von "bind" nutzen, aber das habe ich irgendwie nicht geschafft.

Code: Alles auswählen

import Tkinter as tk
     
class TwoStateButton(tk.Button):
	def __init__(self, states, width, height):
		tk.Button.__init__(self, text=states[0], width=width, height=height)
		self.states = states
		self.current_state = 0
		self.bind("<Button-1>", self.click)
           
	def click(self, event):
		
		self["text"] = self.states[1]
		self.current_state += 1
		
		if self.current_state >= len(self.states):
			self.current_state = 0
		
		self["text"] = self.states[self.current_state]
     
def main():
	root = tk.Tk()
	button = TwoStateButton(states=["text 1", "text 2"], width=10, height=10)
	button.pack()
	root.mainloop()
       


if __name__=='__main__':

	main()
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Da ist ein Fehler in Deinem Denkansatz.

Die GUI ist nicht nur das Aussehen von GUI Elementen, sondern natürlich auch, was passiert, wenn man auf einen Button drückt.
Deine Funktion BuildGUI sollte daher auch das, was beim Drücken des Buttons geschieht, enthalten.

Ansonsten müßtest Du Verweise auf all Deine GUI Elemente global übergeben. Das kann man tun - sollte aber eigentlich nicht sein, da dieses dem Grundsatz von OOP widerspricht.

Also würde das Beispiel dann vielleicht so aussehen:

Code: Alles auswählen

from Tkinter import *
    
root=Tk()

def buildGUI():
    button=Button(text="Schalte EIN",width=10,height=10)
    button.pack()

    def button_click(event):
        if button["text"]=="Schalte EIN":
           button["text"]="Schalte AUS"
        else:
           button["text"]="Schalte EIN"
  
    button.bind("<Button-1>",button_click)


buildGUI()
root.mainloop()
Antworten