【发布时间】:2020-09-16 11:37:46
【问题描述】:
我编写了以下程序,旨在创建一个 3x3 的条目小部件网格,当双击它时,它会在白色和黑色之间切换。
from tkinter import *
root = Tk()
temp = None
def changeColor(event, e):
print("id(e) =", id(e))
if e['bg'] == 'white':
e['bg'] = 'black'
elif e['bg'] == 'black':
e['bg'] = 'white'
global temp
temp = event
entries = [[None for i in range(3)] for j in range(3)]
for y in range(3):
for x in range(3):
e = Entry(root, width=3, bg='white', bd=0, borderwidth=3)
e.bind('<Double-Button-1>', lambda x: changeColor(x, e))
e.grid(column=x, row=y)
entries[y][x] = e
root.mainloop()
网格创建效果很好,但切换行为不正常。无论您双击哪个条目,它始终处于切换状态的右下方条目(循环中添加的最后一个条目)。
终端中的输出是
id(e) = 4376431536
id(e) = 4376431536
id(e) = 4376431536
...
我很困惑。在绑定语句中,我们为每个条目创建一个新的专用 lambda,并传递对相关条目的引用。为什么会这样??????
我找到了解决方法,将绑定函数更改为
def changeColor(event, e):
e2 = event.widget
if e2['bg'] == 'white':
e2['bg'] = 'black'
elif e2['bg'] == 'black':
e2['bg'] = 'white'
我不是要求解决我的问题,而是要求解释为什么会发生这种情况。
【问题讨论】:
-
当 for 循环完成时,变量
e将引用在 for 循环中创建的最后一个Entry。因此,当稍后调用changeColor(x, e)时,如果变量e发生变化,e将是最后一个Entry。使用event.widget是更好的方法,因为 tkinter 会将其分配给触发事件的小部件。 -
我实际上是在发布问题后偶然发现的。不过我还是不明白。如果您在函数内部定义循环,然后调用该函数,它仍然可以工作。当函数正在执行时,我可以购买它(创建 lambda 函数,稍后调用并在本地命名空间中查找 e,这是最近分配的值)但是,在这种情况下,一旦函数完成函数的命名空间(包括) e 消失了 - lambda 值现在在哪里查找该变量?
-
e在全局命名空间中,不会消失。 -
这能回答你的问题吗? how-to-understand-closure-in-a-lambda
标签: python python-3.x tkinter