我有一个循环来动态生成小部件,并且按钮的命令是使用 lambda 设置的,但是
Listbox
es 的事件绑定似乎效果不佳:
def fn1():
def cbf(e, param1, param2):
val = param2.get(param2.curselection())
param1.delete(0, tk.END)
param1.insert(0, val)
def fn2():
for x in range(n):
entry = Entry(root, textvariable=sometextvar, bg="somecolour")
lb = Listbox(root, height=someheight)
lb.insert(0, *["Some", "values"])
entry.bind("<FocusIn>", lambda e, entry=entry: lb.grid(row=entry.grid_info()["row"] - 1, column=2, pady=0, sticky=""))
entry.bind("<FocusOut>", lambda e: lb.grid_remove())
lb.bind("<<ListboxSelect>>", lambda e, param1=entry, param2=lb: cbf(e, param1=param1, param2=param2))
fn1()
我不知道如何,但是
lb.grid_remove()
的 <FocusOut>
和 grid(...)
的 <FocusIn>
可以正常工作。 <<ListboxSelect>>
无法正常工作。
它仅适用于最后一个小部件,因为不知何故,尽管我在定义 lambda 时捕获了变量,但在循环后仅保留了最后一个小部件 (
lambda e, param1=entry, param2=lb:
)。它也仅适用于 <KeyRelease>
的最后一个小部件。
我也尝试过
functools.partial
,例如:
lb.bind("<<ListboxSelect>>", partial(cbf, None, param1=entry, param2=lb))
既作为关键字参数又作为位置参数。我对这个有
TypeError: mainfile.<locals>.cbf() got multiple values for argument 'param1' tkinter
。
我还尝试了大约 25 种其他函数定义的排列和组合、包装它们和嵌套回调等。我也不想使用类,我想要像我当前正在做的那样的东西。像
functools.partial
这样的库函数也不是首选。
为什么它适用于
command
的按钮和某些绑定,但不适用于其他?
出现此问题的原因在于 Tkinter 处理事件绑定与命令回调的方式。使用
bind()
时,Tkinter 自动传递事件对象作为第一个参数,这与不接收自动参数的按钮 command
回调不同。
在您的原始代码中,存在参数计数不匹配 - 回调需要 3 个参数
(cbf(e, param1, param2))
,但绑定实际上尝试传递 4:
e
(来自 lambda 参数)param1
(关键字参数)param2
(关键字参数)让我们试试这个
def fn1():
def cbf(e, param1, param2):
if not param2.curselection(): # Guard against empty selection
return
val = param2.get(param2.curselection())
param1.delete(0, tk.END)
param1.insert(0, val)
def fn2():
for x in range(n):
entry = Entry(root, textvariable=sometextvar, bg="somecolour")
lb = Listbox(root, height=someheight)
lb.insert(0, *["Some", "values"])
# FocusIn/Out bindings remain unchanged as they work correctly
entry.bind("<FocusIn>", lambda e, entry=entry: lb.grid(row=entry.grid_info()["row"] - 1, column=2, pady=0, sticky=""))
entry.bind("<FocusOut>", lambda e: lb.grid_remove())
# Fixed ListboxSelect binding
lb.bind("<<ListboxSelect>>", lambda e, e1=entry, lb1=lb: cbf(e, e1, lb1))
fn1()
按钮命令回调的工作方式不同,因为它们不会自动接收事件对象 - 它们是直接调用的,没有任何自动参数。
原始代码中只有最后一个小部件起作用的原因是Python在闭包中的后期绑定。通过显式捕获 lambda 默认参数中的值(e1=entry,lb1=lb),我们确保每个回调都有自己的引用副本。
让我知道进展如何