habe versucht ein Minispiel zu erstellen, bei dem zwei Toggle-Buttons, vertauscht werden, wenn sie beide gedrueckt sind. Leiter klappt das nicht. Es tritt folgender Fehler auf:
Klickt man auf Button A und dann auf Button B, dann vertauschen sie wie gewollt. Klickt man danach erneut auf Button A und Button B oder umgekehrt, dann ist der zweite Button immer reaktionslos??? Von Vertauschen auch keine Spur.
Habe schon viel probiert (mit copy falls Referenzen Problemen machen, Button.update(); Button.update_idletasks(), ...) - irgendwie komme ich nicht auf die richtige Lösung.
Habt ihr eine Idee?
Hier der Code:
Code: Alles auswählen
import logging
import ttk
tk = ttk.Tkinter
Logger = logging.getLogger("stundenplan.gui.toggle")
class ToggleError(Exception): pass
class ToggleButton(tk.Label):
MINWIDTH = 10
MINHEIGHT = 1
UP = 1
DOWN = -1
def __init__(self, master, text=""):
"""
:param master: {Tkinter-parent}
:param text: {str|unicode}
"""
Logger.debug("ToggleButton init starts with text='%s'", text)
self.stati = {self.UP: {}, self.DOWN: {}}
self.order = (self.UP, self.DOWN)
tk.Label.__init__(self, master)
self.__status__ = self.order[0]
self.__setup__(self.UP, text, "raised")
self.__setup__(self.DOWN, text, "sunken")
# Hooks, die ausgefuehrt werden, wenn der Button gedrueckt wird.
# Dabei ist der Zustand relevant.
self.hook_up = None
self.hook_down = None
self.bind("<Button-1>", self.toggle)
Logger.debug("ToggleButton init done")
# privates
def __setup__(self, status, text, relief):
self.stati[status].update({"relief": relief,
"text": text,
"width": self.MINWIDTH,
"height": self.MINHEIGHT,
"fg": self.cget("fg"),
"bg": self.cget("bg"),
"font": self.cget("font")})
def __run_hooks__(self, event=None):
"""Fuehrt den Hook aus, bevor der Zustand gewechselt wird, dh.
ist der Status UP, dann wird beim Klicken der ``hook_up``
ausgefuehrt.
"""
Logger.debug("__run_hooks__ starts")
if self.is_up():
if callable(self.hook_up):
Logger.debug("up-hook starts")
self.hook_up(event)
elif self.is_down():
if callable(self.hook_down):
Logger.debug("down-hook starts")
self.hook_down(event)
Logger.debug("__run_hooks__ done")
# ui
def reverse_order(self):
"""Vertauscht die Reihenfolge der Stati
"""
Logger.debug("reverse_order starts")
try:
self.order = (self.order[1], self.order[0])
except ToggleError:
raise
Logger.debug("change_order done")
def __config__(self):
Logger.debug("config starts")
d = {}
# in self.stati[x] werden die aktuellen Einstellungen
# gehalten. Darin koennen sich ein paar mit nicht
# gesetztem Wert befinden, die in Tkinter einen
# Fehler ausloesen wuerden => diese werden extrahiert.
for k, v in self.stati[self.__status__].items():
if v:
d.update({k: v})
Logger.debug("real config options are %s", d)
tk.Label.config(self, **d)
Logger.debug("config done")
def config(self):
"""Das Label hat nun zwei Zustaende => config nicht eindeutig!
:return:
"""
raise NotImplementedError()
configure = config
# Konfigurationen einstellen
def config_up(self, **configs):
"""Die Einstellungen des ToggleButtons sollen nur ueber die
beiden Methoden ``config_up`` und ``config_down`` vorgenommen
werden.
"""
Logger.debug("config_up starts with configs=%s", configs)
try:
if "hook" in configs:
self.hook_up = configs.pop("hook")
self.stati[self.UP].update(configs)
except ToggleError:
raise
self.__config__()
Logger.debug("config_up done")
def config_down(self, **configs):
"""Die Einstellungen des ToggleButtons sollen nur ueber die
beiden Methoden ``config_up`` und ``config_down`` vorgenommen
werden.
"""
Logger.debug("config_down starts with configs=%s", configs)
try:
if "hook" in configs:
self.hook_down = configs.pop("hook")
self.stati[self.DOWN].update(configs)
except ToggleError:
raise
self.__config__()
Logger.debug("config_down done")
def config_all(self, **configs):
"""
Alle gemeinsamen Optionen koennen hier in einem Schritt
eingestellt werden.
"""
self.config_up(**configs)
self.config_down(**configs)
def toggle(self, event=None):
"""Holt naechsten Status und stellt das Toggle darauf ein.
"""
Logger.debug("toggle starts")
self.__run_hooks__(event)
if self.__status__ == self.order[0]:
self.__status__ = self.order[1]
else:
self.__status__ = self.order[0]
self.__config__() # tk.Label wird angewiesen neues anzuzeigen
Logger.debug("toggle done")
def is_up(self):
return self.__status__ == self.UP
def is_down(self):
return self.__status__ == self.DOWN
def test():
Logger.debug("####################################")
window = tk.Tk()
window.title("Toggle")
window.geometry("200x200")
class SpielButton(ToggleButton):
def __init__(self, master, **kwargs):
ToggleButton.__init__(self, master, **kwargs)
self.pos = None, None
class Spiel(tk.Frame):
def __init__(self, master, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
t1 = ToggleButton(window)
t1.config_all(text="A", height=2)
t1.config_up(bg="#666")
t1.config_down(bg="#888")
t1.grid(column=0, row=0)
t1.pos = 0, 0
t1.bind("<Button-1>", self.add_to_list, add='+')
t2 = ToggleButton(window)
t2.config_all(text="B", bg="#CF11AA", fg="#CCC", height=2)
t2.grid(column=1, row=0)
t2.pos = 1, 0
t2.bind("<Button-1>", self.add_to_list, add='+')
t3 = ToggleButton(window)
t3.config_all(text="C", bg="#00FFAA", fg="#CCC", height=2)
t3.grid(column=0, row=1)
t3.pos = 0, 1
t3.bind("<Button-1>", self.add_to_list, add='+')
t4 = ToggleButton(window)
t4.config_all(text="D", bg="#0011AA", fg="#CCC", height=2)
t4.grid(column=1, row=1)
t4.pos = 1, 1
t4.bind("<Button-1>", self.add_to_list, add='+')
self.grid()
self.to_move = []
def add_to_list(self, event):
if len(self.to_move) < 2:
Logger.debug("liste ist %s", self.to_move)
self.to_move.append(event.widget)
if len(self.to_move) == 2:
Logger.debug("liste ist %s", self.to_move)
self.to_move[0].toggle()
spalte0, zeile0 = self.to_move[0].pos
#spalte0, zeile0 = copy(self.to_move[0].pos)
#self.to_move[0].grid_forget()
self.to_move[1].toggle()
spalte1, zeile1 = self.to_move[1].pos
#spalte1, zeile1 = copy(self.to_move[1].pos)
#self.to_move[1].grid_forget()
# neu setzen
self.to_move[0].grid(column=spalte1, row=zeile1)
#self.to_move[0].update_idletasks()
self.to_move[1].grid(column=spalte0, row=zeile0)
#self.to_move[1].update_idletasks()
self.to_move = []
spiel = Spiel(window)
window.mainloop()
LOGFMT = '[%(lineno)4d,Logger=%(name)s,Module=%(module)s,Func=%(funcName)s]\n'
LOGFMT += '%(message)s'
Logger = logging.getLogger("")
logging.basicConfig(format=LOGFMT)
Logger.setLevel(10)
test()