@sirius3 Dass man mit Klassen viel machen kann, weiss ich schon. Aber eine übersichtliche Programmstruktur hast Du nicht.
Meine Vorstellung ist: Zuerst definiert man die Widgets. Dann definiert man die Callbacks dazu. Und dass eine einheitlice Callbackstruktur übersichtlicher ist als irgendwelche lambdas sollte auch einleuchtend sein. Und hier sieht man, dass man über die Callbacks sogar die Parameter von Funktionen austauschen kannn. Dazu habe ich jetzt mal zwei Module gemacht für Python3.
Modul1 callbacks.py:
Code: Alles auswählen
from tkinter import *
from tkinter import colorchooser
import action
VAR={}
root = action.init(Tk())
root.title('Callbacks')
root.geometry('492x363+191+125')
VAR["Blinklabel"] = action.Label(root,text="""Let me blink""",pady='10',padx='20',font='TkDefaultFont 37',bd='5',relief='solid')
VAR["Blinklabel"].place(y='26',x='68')
VAR["Frame"] = LabelFrame(text="""Blinkcontrol""",height='180',width='350')
VAR["Frame"].place(y='140',x='70')
Label(VAR["Frame"],text="""blinkoff_time""").place(y='80',x='100',anchor='e')
Label(VAR["Frame"],text="""blinkon_time""").place(y='50',x='100',anchor='e')
Label(VAR["Frame"],text="""blink_color""").place(y='20',x='100',anchor='e')
Label(VAR["Frame"],text="""blink""").place(y='110',x='100',anchor='e')
VAR["ColorEntry"] = action.Entry(VAR["Frame"])
VAR["ColorEntry"].place(width='150',y='20',x='110',anchor='w')
VAR["Help"] = Button(VAR["Frame"],text="""?""")
VAR["Help"].place(y='20',x='270',anchor='w')
VAR["on_scale"] = Scale(VAR["Frame"],orient='horizontal',from_='50.0',to='1000.0',showvalue='0',length='192',resolution='50.0')
VAR["on_scale"].place(y='50',x='110',anchor='w')
VAR["off_scale"] = Scale(VAR["Frame"],orient='horizontal',from_='50.0',to='1000.0',showvalue='0',length='192',resolution='50.0')
VAR["off_scale"].place(y='71',x='109')
VAR["blink_on"] = Button(VAR["Frame"],text="""Start""",bg='green')
VAR["blink_on"].place(y='110',x='235',anchor='e')
VAR["blink_off"] = Button(VAR["Frame"],text="""Stop""",bg='#ff5e00')
VAR["blink_off"].place(y='110',x='303',anchor='e')
### CODE ===================================================
VAR["on_scale"].set(500)
VAR["off_scale"].set(500)
# blink Callbacks for the Blinklabel =================
# info message 'blink_start'
def function(mewidget):
mewidget.activateAction('blink',True)
action.inform(mewidget,'blink',True)
VAR["Blinklabel"].do_action('blink_start',function,None,True)
# ================================================================================================
# We can change the parameters blinkon_color,blinkon_time,blinkoff_time for this function
# ================================================================================================
# info message 'blink'
def function(mewidget,message_blinkon,blinkon_color,blinkon_time,blinkoff_time):
if message_blinkon:
mewidget['fg'] = blinkon_color
action.informLater(blinkon_time,mewidget,'blink',False)
else:
mewidget['fg'] = mewidget['bg']
action.informLater(blinkoff_time,mewidget,'blink',True)
VAR["Blinklabel"].do_action('blink',function,('red',500,500),True,True)
# info message 'blink_stop'
def function(mewidget,color_before):
mewidget.activateAction('blink',False)
mewidget['fg'] = color_before
VAR["Blinklabel"].do_action('blink_stop',function,VAR["Blinklabel"]['fg'],True)
# start and stop buttons =================
def function(blinklabel): action.inform(blinklabel,'blink_start')
action.do_command(VAR["blink_on"],function,VAR["Blinklabel"])
def function(blinklabel): action.inform(blinklabel,'blink_stop')
action.do_command(VAR["blink_off"],function,VAR["Blinklabel"])
# scales ====================================
VAR["Blink_Callback"] = VAR["Blinklabel"].getActionCallback('blink')
# ================================================================================================
# Here we see, that we can change the parameters of a function, which is a function of a Callback
# ================================================================================================
def function(mewidget,callback): callback.parameters[1] = mewidget.get() # changing blinkon_time
action.do_command(VAR["on_scale"],function,VAR["Blink_Callback"],True)
def function(mewidget,callback): callback.parameters[2] = mewidget.get() # changing blinkoff_time
action.do_command(VAR["off_scale"],function,VAR["Blink_Callback"],True)
# entry ====================================
# ================================================================================================
# Here we see, that we can change the parameters of a function, which is a function of a Callback
# ================================================================================================
def function(mewidget,helpbutton,callback):
try: helpbutton['bg'] = mewidget.get()
except: pass
callback.parameters[0] = helpbutton['bg'] # changing blinkon_color
mewidget['bg'] = 'gray'
action.informLater(300,mewidget,'white')
action.do_event(VAR['ColorEntry'],'<Return>',function,(VAR["Help"],VAR["Blink_Callback"]),True)
def function(mewidget,helpbutton):
mewidget.delete(0,END)
mewidget.insert(0,str(helpbutton['bg']))
mewidget['bg'] = 'white'
VAR['ColorEntry'].do_action('white',function,VAR["Help"],True)
# ================================================================================================
# Here we see, that we can change the parameters of a function, which is a function of a Callback
# ================================================================================================
def function(mewidget,callback,entry):
color = colorchooser.askcolor(parent=root,initialcolor=mewidget['bg'],title="Choose color")[1]
if color != None:
callback.parameters[0] = color # changing blinkon_color
mewidget['bg'] = color
entry.delete(0,END)
entry.insert(0,color)
action.do_command(VAR['Help'],function,(VAR["Blink_Callback"],VAR['ColorEntry']),True)
### ========================================================
del function
VAR.clear()
del VAR
root.mainloop()
Und dazu wird das Modul mit den Callbacks benötigt, nämlich action.py:
Code: Alles auswählen
import tkinter as tk
Application = None
ACTORS = {}
# please call init in your module, for example:
#
# root = action.init(Tk())
#
def init(root):
global Application
global ACTORS
Application = root
ACTORS = {}
return root
def dummyfunction(par):pass
class Callback:
def __init__(self,widget,function,parameters=None,wishWidget=False,wishEvent=False,wishSelf = False):
self.widget = widget
self.event = None
self.wishWidget = wishWidget
self.wishEvent = wishEvent
self.wishSelf = wishSelf
self.mydata = None # may be used for many purposes. Accessible via self
# special case for functions =========
self.isFunction = False
if type(function) is type(dummyfunction):
self.isFunction = True
self.function = function
self.parameters = []
if type(parameters) is tuple:
for e in parameters: self.parameters.append(e)
elif type(parameters) is list: self.parameters = parameters
elif parameters != None: self.parameters = [parameters]
else: # other cases ============
print("Callback: please feel free to implement, what you need")
# for execution later =======
def execute(self):
# special case for functions =========
if self.isFunction:
par = []
if self.wishWidget: par = [self.widget]
if self.wishEvent: par.append(self.event)
if self.wishSelf: par.append(self)
par += self.parameters
return self.function(*par)
else: # other cases ============
print("Callback: please feel free to implement, what you need")
def setEvent(self,event = None):
self.event = event
return self.execute
# for execution immediate =======
def receive(self,event = None): return self.setEvent(event)()
# for using the Callback as funcion =======
def call(self,*args):
if self.isFunction: return self.function(*args) # a function cannot be copied, but a Callback can. Using different mydata, the functions can behave different.
else: print("Please, call only functions.")
# if you don't like to use the Callback class, please feel free to implement this in another way, e.g. by using lambda
def do_command(widget,function,parameters=None,wishWidget=False,wishEvent=False,wishSelf=False):
cmd = Callback(widget,function,parameters,wishWidget,wishEvent,wishSelf).receive
widget.config(command = cmd)
def do_event(widget,eventkey,function,parameters=None,wishWidget=False,wishEvent=False,wishSelf=False):
cmd = Callback(widget,function,parameters,wishWidget,wishEvent,wishSelf).receive
widget.bind(eventkey,cmd)
class InformAndReact:
def __init__(self):
self.actions = {}
def do_action(self,actionid,function,parameters=None,wishWidget=False,wishMessage=False,wishSelf=False):
ACTORS[self] = self
self.actions[actionid] = [True,Callback(self,function,parameters,wishWidget,wishMessage,wishSelf)]
def activateAction(self,actionid,flag):
if actionid in self.actions: self.actions[actionid][0] = flag
def undo_action(self,actionid):
self.actions.pop(actionid,None)
if len(self.actions) == 0: ACTORS.pop(self,None)
def destroyActions(self):
self.actions.clear()
ACTORS.pop(self,None)
def getActionCallback(self,actionid): return self.actions[actionid][1]
def destroy(self):
self.destroyActions()
self.tkClass.destroy(self)
class Label(InformAndReact,tk.Label):
def __init__(self,master,**kwargs):
self.tkClass = tk.Label
tk.Label.__init__(self,master,**kwargs)
InformAndReact.__init__(self)
class Entry(InformAndReact,tk.Entry):
def __init__(self,master,**kwargs):
self.tkClass = tk.Entry
tk.Entry.__init__(self,master,**kwargs)
InformAndReact.__init__(self)
def informImmediate(widget,actionid,msg=None):
if widget in ACTORS:
if actionid in widget.actions:
if widget.actions[actionid][0] == True: # if action is activated
widget.actions[actionid][1].receive(msg) # receive message
# sending via queue currently not available, so:
def inform(widget,actionid,msg=None):
informImmediate(widget,actionid,msg)
def _informLater(parameters):
inform(*parameters)
def informLater(ms,widget,actionid,msg=None):
Application.after(ms,_informLater,(widget,actionid,msg))