将 OptionMenu 传递到回调中(或检索对使用的小部件的引用)
Passing OptionMenu into a callback (or retrieving a reference to the used widget)
我正在开发一个(顶层)GUI,它由一组 8 个 OptionMenus 组成,每个 OptionMenus 都包含相同的选项列表。目前,我正在使用 for 循环构建这些小部件,并且我 。所有 OptionMenus link 到相同的 (lambda) 回调函数。
为了实用:选项列表中的项目代表一系列处理步骤,用户可以更改处理顺序。
其中一个列表的更改将导致一个进程被执行两次,一个进程根本不执行。但是,我希望每个项目只出现一次。因此,每个用户输入都应伴随第二次 OptionMenu 更改。
例如:初始订单 1-2-3 --> 用户更改第二个流程:1-3-3,自动更正为:1-3-2,其中每个流程再次仅执行一次。
据我了解,如果我有对刚刚更改的 OptionMenu 的引用(从回调函数中),我只能让它工作。我正在调查 passing the widget into the callback。示例代码是尝试实现第二种建议的方法,但结果不是我所期望的。
问题是 OptionMenu 小部件的行为似乎与其他小部件有些不同。 OptionMenu 为命令功能重新定义。无论我通过命令函数传递什么输入,回调似乎只检索 OptionMenu 选择,这不足以让我确定我的处理顺序。
非常感谢您的建议!
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
self.create_widgets()
def create_widgets(self):
self.active_procs = ['proc 1','proc 2','proc 3','proc 4',
'proc 5','proc 6','proc 7','proc 8']
itemnr, widgets = dict(), dict()
for index in range(8):
name_construct = 'nr' + str(index)
itemnr[name_construct] = tk.StringVar(root)
itemnr[name_construct].set(self.active_procs[index])
widgets[name_construct] = tk.OptionMenu(self, itemnr[name_construct], *self.active_procs,
command=lambda widget=name_construct:
self.order_change(widget))
widgets[name_construct].grid(row=index+2, column=2, columnspan=2,
sticky="nwse", padx=10, pady=10)
def order_change(self,widget):
print(widget)
root = tk.Tk()
root.title("OptionMenu test")
app = Application(master=root)
root.mainloop()
OptionMenu
会将新值传递给回调,因此您无需执行任何操作即可获取新值。这就是为什么您的 widget
值不是 name_construct
的值——传入的值将覆盖您在 lambda 中提供的默认值。
要解决这个问题,您只需添加另一个参数,这样您就可以将 name_construct
的值与自动发送的值一起传递给回调。
看起来像这样:
widgets[name_construct] = tk.OptionMenu(..., command=lambda value, widget=name_construct: self.order_change(value, widget))
...
def order_change(self, value, widget):
print(value, widget)
注意:OptionMenu
实际上不是 tkinter 小部件。它只是一个方便的函数,可以创建一个标准 Menubutton
和一个关联的 Menu
。然后它在菜单上为每个选项创建一个项目,并用 StringVar
.
将它们全部联系在一起
您自己可以很容易地获得完全相同的行为。这样做可以更改菜单中每个项目在被选中时的作用。
对于那些感兴趣的人,您可以在下面找到我如何获得我想要的小部件行为的示例代码。我采纳了 Bryan 的建议,将 OptionMenu 替换为 Menubutton/Menu 组合。我还利用 this post 在我的流程订单列表中查找重复条目。
关于如何以更简洁或更简短的方式实现它,或者如何使用不同的界面(例如拖放)获得相同的功能的任何想法或建议,当然欢迎!
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
self.create_widgets()
def create_widgets(self):
# Assisting text
l1 = tk.Label(self, text = "Data in", font=(None, 15))
l1.grid(row=0, column=2)
l2 = tk.Label(self, text = u'\N{BLACK DOWN-POINTING TRIANGLE}', font=(None, 15))
l2.grid(row=1, column=2)
l3 = tk.Label(self, text = "Data out", font=(None, 15))
l3.grid(row=11, column=2)
l4 = tk.Label(self, text = u'\N{BLACK DOWN-POINTING TRIANGLE}', font=(None, 15))
l4.grid(row=10, column=2)
# Process list
self.active_procs = ['proc a','proc b','proc c','proc d',
'proc e','proc f','proc g','proc h']
self.the_value, self.widgets, self.topmenu = dict(), dict(), dict()
for index in range(8):
name_construct = 'nr' + str(index)
self.the_value[name_construct] = tk.StringVar(root)
self.the_value[name_construct].set(self.active_procs[index])
self.widgets[name_construct] = tk.Menubutton(self, textvariable=
self.the_value[name_construct],
indicatoron=True)
self.topmenu[name_construct] = tk.Menu(self.widgets[name_construct],
tearoff=False)
self.widgets[name_construct].configure(menu=self.topmenu[name_construct])
for proc in self.active_procs:
self.topmenu[name_construct].add_radiobutton(label=proc, variable=
self.the_value[name_construct],
command=lambda proc=proc,
widget=name_construct:
self.order_change(proc,widget))
self.widgets[name_construct].grid(row=index+2, column=2, columnspan=2,
sticky="nwse", padx=10, pady=10)
def order_change(self,proc,widget):
# Get the index of the last changed Menubutton
index_user_change = list(self.widgets.keys()).index(widget)
procs_order = [] # Current order from widgets
for index in range(8):
name_construct = 'nr' + str(index)
procs_order.append(self.widgets[name_construct].cget("text"))
# 1 change may lead to 1 double and 1 missing process
doubles = self.list_duplicates_of(procs_order,proc)
if len(doubles) == 2: # If double processes are present...
doubles.remove(index_user_change) # ...remove user input, change the other
missing_proc = str(set(self.active_procs)^set(procs_order)).strip('{"\'}')
index_change_along = int(doubles[0])
# Update references
self.active_procs[index_user_change] = proc
self.active_procs[index_change_along] = missing_proc
# Update widgets
name_c2 = 'nr'+str(index_change_along)
self.the_value[name_c2].set(self.active_procs[index_change_along])
self.widgets[name_c2].configure(text=missing_proc)
def list_duplicates_of(self,seq,item):
start_at = -1
locs = []
while True:
try:
loc = seq.index(item,start_at+1)
except ValueError:
break
else:
locs.append(loc)
start_at = loc
return locs
root = tk.Tk()
root.title("OptionMenu test")
app = Application(master=root)
root.mainloop()
我正在开发一个(顶层)GUI,它由一组 8 个 OptionMenus 组成,每个 OptionMenus 都包含相同的选项列表。目前,我正在使用 for 循环构建这些小部件,并且我
为了实用:选项列表中的项目代表一系列处理步骤,用户可以更改处理顺序。
其中一个列表的更改将导致一个进程被执行两次,一个进程根本不执行。但是,我希望每个项目只出现一次。因此,每个用户输入都应伴随第二次 OptionMenu 更改。
例如:初始订单 1-2-3 --> 用户更改第二个流程:1-3-3,自动更正为:1-3-2,其中每个流程再次仅执行一次。
据我了解,如果我有对刚刚更改的 OptionMenu 的引用(从回调函数中),我只能让它工作。我正在调查 passing the widget into the callback。示例代码是尝试实现第二种建议的方法,但结果不是我所期望的。
问题是 OptionMenu 小部件的行为似乎与其他小部件有些不同。 OptionMenu
非常感谢您的建议!
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
self.create_widgets()
def create_widgets(self):
self.active_procs = ['proc 1','proc 2','proc 3','proc 4',
'proc 5','proc 6','proc 7','proc 8']
itemnr, widgets = dict(), dict()
for index in range(8):
name_construct = 'nr' + str(index)
itemnr[name_construct] = tk.StringVar(root)
itemnr[name_construct].set(self.active_procs[index])
widgets[name_construct] = tk.OptionMenu(self, itemnr[name_construct], *self.active_procs,
command=lambda widget=name_construct:
self.order_change(widget))
widgets[name_construct].grid(row=index+2, column=2, columnspan=2,
sticky="nwse", padx=10, pady=10)
def order_change(self,widget):
print(widget)
root = tk.Tk()
root.title("OptionMenu test")
app = Application(master=root)
root.mainloop()
OptionMenu
会将新值传递给回调,因此您无需执行任何操作即可获取新值。这就是为什么您的 widget
值不是 name_construct
的值——传入的值将覆盖您在 lambda 中提供的默认值。
要解决这个问题,您只需添加另一个参数,这样您就可以将 name_construct
的值与自动发送的值一起传递给回调。
看起来像这样:
widgets[name_construct] = tk.OptionMenu(..., command=lambda value, widget=name_construct: self.order_change(value, widget))
...
def order_change(self, value, widget):
print(value, widget)
注意:OptionMenu
实际上不是 tkinter 小部件。它只是一个方便的函数,可以创建一个标准 Menubutton
和一个关联的 Menu
。然后它在菜单上为每个选项创建一个项目,并用 StringVar
.
您自己可以很容易地获得完全相同的行为。这样做可以更改菜单中每个项目在被选中时的作用。
对于那些感兴趣的人,您可以在下面找到我如何获得我想要的小部件行为的示例代码。我采纳了 Bryan 的建议,将 OptionMenu 替换为 Menubutton/Menu 组合。我还利用 this post 在我的流程订单列表中查找重复条目。
关于如何以更简洁或更简短的方式实现它,或者如何使用不同的界面(例如拖放)获得相同的功能的任何想法或建议,当然欢迎!
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.grid(row=0, column=0, sticky=tk.N+tk.S+tk.E+tk.W)
self.create_widgets()
def create_widgets(self):
# Assisting text
l1 = tk.Label(self, text = "Data in", font=(None, 15))
l1.grid(row=0, column=2)
l2 = tk.Label(self, text = u'\N{BLACK DOWN-POINTING TRIANGLE}', font=(None, 15))
l2.grid(row=1, column=2)
l3 = tk.Label(self, text = "Data out", font=(None, 15))
l3.grid(row=11, column=2)
l4 = tk.Label(self, text = u'\N{BLACK DOWN-POINTING TRIANGLE}', font=(None, 15))
l4.grid(row=10, column=2)
# Process list
self.active_procs = ['proc a','proc b','proc c','proc d',
'proc e','proc f','proc g','proc h']
self.the_value, self.widgets, self.topmenu = dict(), dict(), dict()
for index in range(8):
name_construct = 'nr' + str(index)
self.the_value[name_construct] = tk.StringVar(root)
self.the_value[name_construct].set(self.active_procs[index])
self.widgets[name_construct] = tk.Menubutton(self, textvariable=
self.the_value[name_construct],
indicatoron=True)
self.topmenu[name_construct] = tk.Menu(self.widgets[name_construct],
tearoff=False)
self.widgets[name_construct].configure(menu=self.topmenu[name_construct])
for proc in self.active_procs:
self.topmenu[name_construct].add_radiobutton(label=proc, variable=
self.the_value[name_construct],
command=lambda proc=proc,
widget=name_construct:
self.order_change(proc,widget))
self.widgets[name_construct].grid(row=index+2, column=2, columnspan=2,
sticky="nwse", padx=10, pady=10)
def order_change(self,proc,widget):
# Get the index of the last changed Menubutton
index_user_change = list(self.widgets.keys()).index(widget)
procs_order = [] # Current order from widgets
for index in range(8):
name_construct = 'nr' + str(index)
procs_order.append(self.widgets[name_construct].cget("text"))
# 1 change may lead to 1 double and 1 missing process
doubles = self.list_duplicates_of(procs_order,proc)
if len(doubles) == 2: # If double processes are present...
doubles.remove(index_user_change) # ...remove user input, change the other
missing_proc = str(set(self.active_procs)^set(procs_order)).strip('{"\'}')
index_change_along = int(doubles[0])
# Update references
self.active_procs[index_user_change] = proc
self.active_procs[index_change_along] = missing_proc
# Update widgets
name_c2 = 'nr'+str(index_change_along)
self.the_value[name_c2].set(self.active_procs[index_change_along])
self.widgets[name_c2].configure(text=missing_proc)
def list_duplicates_of(self,seq,item):
start_at = -1
locs = []
while True:
try:
loc = seq.index(item,start_at+1)
except ValueError:
break
else:
locs.append(loc)
start_at = loc
return locs
root = tk.Tk()
root.title("OptionMenu test")
app = Application(master=root)
root.mainloop()