我有多个tk.checkbutton,单击(或更改)时应该共享同一个回调。复选框的变量(状态)应在状态更改时触发的回调中可用(并用于所有复选按钮)。有很多这样的文章,大多数都是关于状态变量的并行列表。因为我想避免使用单独的变量列表,因为checkboxex的数量可能会因数据更改而更改,所以我在两个版本中创建了自己的Checkbutton类,但这两个版本都有缺点,所以我需要一些建议
这两个类都包含一个state变量实例,用于保存状态(避免管理单独的状态变量列表),并在单击时触发操作。所以我的问题与回调有关
第一个类显示在这个最小化的运行示例中:
#!/usr/bin/env python3
from tkinter import *
import tkinter as tk
#-------------------------------------------------------------------------------
# myCheckButton: CheckButton containing state, that can be identified
# in action(command) procedure
# Parameters:
# userdata: User defined, for example index of a database table row related to the CB
# action : command to execute when clicked. Replaces command variable because
# command does not provide the event information when called. Event
# information is needed to get the checkbox data within action.
class myCheckButton(tk.Checkbutton):
def __init__(self, parent, userdata, action, *args, **kwargs):
# state holds the state of the CB (False = unchecked, True = checked)
self.state = BooleanVar(value=False)
# add the state variable to tk.Ckeckbutton args
kwargs['variable'] = self.state
# init tk.Checkbutton
super().__init__(parent, *args, **kwargs)
# bind action to myCheckButton using <Button-1> (left mouse button)
self.bind('<Button-1>', action)
# store userdata for usage in the action procedure
self.userdata = userdata
#-------------------------------------------------------------------------------
def onCbClicked(event):
# get the calling widget containing all data we need to know
sender = event.widget
# get the status of CB. "not" because action runs before status change
status = not sender.state.get()
# do something by using text, status and/or user defined variable
if status == True:
print("CB(" + sender["text"] + "): Data " + str(sender.userdata) + " is checked")
else:
print("CB(" + sender["text"] + "): Data " + str(sender.userdata) + " is unchecked")
#-------------------------------------------------------------------------------
# Main window defs
root = tk.Tk()
root.title("myCheckButton")
root.geometry('300x60')
# 1st instance of myCheckButton
mycb1 = myCheckButton(root, text="Test A", userdata=1, action=onCbClicked)
mycb1.grid(row=0, column=0)
# 2nd instance of myCheckButton
mycb2 = myCheckButton(root, text="Test B", userdata=2, action=onCbClicked)
mycb2.grid(row=1, column=0)
# just for example: Set state of mycb2 to true
mycb2.state.set(True)
root.mainloop()
这里的缺点是只在单击时调用回调,而不是在调用.state.set(bool)以更改状态时调用回调(请参见最后一行)。除了我的第二个使用trace的解决方案之外,还有什么方法可以解决这个问题吗?优点是我可以使用传递给它的事件在回调中清楚地识别调用实例
第二种解决方案与此类似,其优点是当状态变量更改时也会调用回调,而无需单击小部件。缺点是我必须传递Checkbutton的实例名,以便通过将跟踪变量命名为实例来在回调中识别它。此外,我不确定这种识别调用实例的方法是否是save
我的第二个解决方案的示例:
#!/usr/bin/env python3
from tkinter import *
import tkinter as tk
#-------------------------------------------------------------------------------
# myCheckButton: CheckButton containing state, that can be identified
# in action(command) procedure
# Parameters:
# instName: Name of the CB instance to use in onCbChange
# userdata: User defined, for example index of a database table row related to the CB
# action : command to execute when status changes.
class myCheckButton(tk.Checkbutton):
def __init__(self, parent, instName, userdata, action, *args, **kwargs):
# state holds the state of the CB (False = unchecked, True = checked)
# the name of the internal variable is set to the instance name of this class.
self.state = BooleanVar(value=False, name=instName)
# Trace the state variable to call action procedure when it changes.
self.state.trace("w", action)
# add the state variable to tk.Ckeckbutton args
kwargs['variable'] = self.state
# init tk.Checkbutton
super().__init__(parent, *args, **kwargs)
# store userdata for usage in the action procedure
self.userdata = userdata
#-------------------------------------------------------------------------------
def onCbChange(*args):
# get the calling widget containing all data we need to know from *args.
# This requires the name of the traced variable is the same as name of the widget instance.
sender = eval(args[0])
# get the status of CB.
status = sender.state.get()
# do something by using text, status and/or user defined variable
if status == True:
print("CB(" + sender["text"] + "): Data " + str(sender.userdata) + " is checked")
else:
print("CB(" + sender["text"] + "): Data " + str(sender.userdata) + " is unchecked")
#-------------------------------------------------------------------------------
# Main window defs
root = tk.Tk()
root.title("myCheckButton")
root.geometry('300x60')
# 1st instance of myCheckButton
mycb1 = myCheckButton(root, text="Test A", instName="mycb1", userdata=1, action=onCbChange)
mycb1.grid(row=0, column=0)
# 2nd instance of myCheckButton
mycb2 = myCheckButton(root, text="Test B", instName="mycb2", userdata=2, action=onCbChange)
mycb2.grid(row=1, column=0)
# just for example: Set state of mycb2 to true
mycb2.state.set(True)
root.mainloop()
即使这是我的首选解决方案: 通过对传递给跟踪回调的第一个参数进行eval来确定调用实例是否保存(假定传递给构造函数的是正确的名称)?有没有更好的方法来识别来电者?例如,通过以某种方式传递事件,以便能够识别与第一个解决方案类似的调用方
提前感谢您的帮助
编辑:
根据acw1668的提示,我将myCheckButton类从第一个解决方案更改为:
class myCheckButton(tk.Checkbutton):
def __init__(self, parent, userdata, action, *args, **kwargs):
# state holds the state of the CB (False = unchecked, True = checked)
self.state = BooleanVar(value=False)
# add the state variable to tk.Ckeckbutton args
kwargs['variable'] = self.state
# init tk.Checkbutton
super().__init__(parent, *args, **kwargs)
# bind action to myCheckButton using <Button-1> (left mouse button)
self.bind('<Button-1>', action)
# store userdata for usage in the action procedure
self.userdata = userdata
# store action
self.action = action
# add widget property
self.widget = self
def set(self, status):
# only when status changes:
if status != self.state.get():
# callback before status change
self.action(self)
# change status
self.state.set(status)
我不确定从set过程将“事件”传递到回调的方法是否是最好的,但它在调用.set(bool)时起作用
最终解决方案
下面是定制checkbutton的完整最终解决方案。感谢Matiiss和acw1668:
#!/usr/bin/env python3
from tkinter import *
import tkinter as tk
#-------------------------------------------------------------------------------
# myCheckButton: CheckButton containing state, that can be identified
# in action(command) procedure
# Parameters:
# userdata: User defined, for example index of a database table row related to the CB
class myCheckButton(tk.Checkbutton):
def __init__(self, parent, userdata, *args, **kwargs):
# state holds the state of the CB (False = unchecked, True = checked)
self.state = BooleanVar(value=False)
# add the state variable to tk.Ckeckbutton args
kwargs['variable'] = self.state
# init tk.Checkbutton
super().__init__(parent, *args, **kwargs)
# store userdata for usage in the action procedure
self.userdata = userdata
def set(self, status):
# only when status changes:
if status != self.state.get():
if status == True: self.deselect()
else: self.select()
self.invoke()
#-------------------------------------------------------------------------------
def onCbClicked(sender):
# get the status of CB.
status = sender.state.get()
# do something by using text, status and/or user defined variable
if status == True:
print("CB(" + sender["text"] + "): Data " + str(sender.userdata) + " is checked")
else:
print("CB(" + sender["text"] + "): Data " + str(sender.userdata) + " is unchecked")
#-------------------------------------------------------------------------------
# Main window defs
root = tk.Tk()
root.title("myCheckButton")
root.geometry('300x60')
# 1st instance of myCheckButton
mycb1 = myCheckButton(root, text="Test A", userdata=1, command=lambda: onCbClicked(mycb1))
mycb1.grid(row=0, column=0)
# 2nd instance of myCheckButton
mycb2 = myCheckButton(root, text="Test B", userdata=2, command=lambda: onCbClicked(mycb2))
mycb2.grid(row=1, column=0)
# just for example: Set state of mycb2 to test status change callback
mycb2.set(True)
root.mainloop()
我还是有点困惑,但这是你想要的吗:
使用类似的函数模拟用户与复选框的交互(选中用于选择,取消选中用于取消选择)
你只需要找到一种调用这些函数的方法
完整的代码示例:
请注意,仅使用
.select
/.deselect
时,使用按钮会触发checkbox命令,但不会这样做相关问题 更多 >
编程相关推荐