【Python】Tkinter的 for 循环 Button 中监听事件的总结

hxy    2020-06-19 18:00

在 Python 的Tkinter库中,事件监听常用两种方式:
  1. Button 的 command,然后command 指向一个具体的函数,可以通过lambda 传参,
  2. bind 函数,可以绑定单击、双击多种操作,但是传参需要用到额外的库 functools
首先看一组 for 循环里 创建的 Button, 如果想要监听事件,需要用到 lambda 匿名函数,看一下例子:
def createGameURLs(self):
    self.button = []
    for i in range(3):
        self.button.append(Button(self, text='Game '+str(i+1),command=lambda:self.open_this(i)))
        self.button[i].grid(column=4, row=i+1, sticky=W)

def open_this(self, myNum):
    print(myNum)
这里面虽然调用了匿名函数,但是没有声明参数 i , 参数传递之后都是i 循环到末尾的值,传参失败。
热心网友给出解决办法:将lambda改为lambda i = i:self.open_this(i).

这可能看起来很神奇,但这就是正在发生的事情。当您使用该lambda定义函数时,open_this调用在您定义函数时不会获取变量 i 的值,相反,它会产生一个闭包,这有点像对自己的说法,“我应该在我被调用时,寻找变量 i 的值。”当然,在循环结束后调用该函数,所以此时我将始终等于循环中的最后一个值,使用 i = i  会导致函数在定义lambda时存储 i 的当前值,而不是等待稍后查找i的值.

看起来可能有点绕,大致意思应该是 command 指向一个 匿名函数,这个匿名函数的参数是i, 而这个i 被赋值给 for 循环里的变量i, 函数体是 self.AddNumber(i),如果写成如下格式可能更好理解一些:

def createGameURLs(self):
    self.button = []
    for i in range(3):
        self.button.append(Button(self, text='Game '+str(i+1),command=lambda x=i:self.open_this(x)))
        self.button[i].grid(column=4, row=i+1, sticky=W)
def open_this(self, myNum):
    print(myNum)

附第 2 种写法的尝试:

from functools import partial

def createGameURLs(self):
    self.button = []
    for i in range(3):
        self.button.append(Button(self, text='Game '+str(i+1)))
        self.button[i].bind('<Button-1>', partial(self.open_this, i))
        self.button[i].grid(column=4, row=i+1, sticky=W)

def open_this(self, myNum, event):
    print(myNum)

需要注意的是,这种写法需要在 具体调用 的函数里添加一个额外的参数 event

参考资料:

  1. http://www.cocoachina.com/articles/39400
Last Modified: 2020-06-23 08:15
Views: 1.8K

[[total]] comments

Post your comment
  1. [[item.time]]
    [[item.user.username]] [[item.floor]]Floor
  2. Click to load more...
  3. Post your comment