我正在尝试在for循环中在tkinter中创建按钮。并且每个循环都将i count值作为参数传递给命令值。因此,当从命令值调用函数时,我可以判断按下了哪个按钮并相应地执行操作。问题是,假设len为3,它将创建3个按钮,标题为“Game 1”到“Game 3”,但是当按下任何按钮时,打印值总是2,即最后一次迭代。因此看起来按钮是作为单独的实体制作的,但命令参数中的i值似乎完全相同。这是代码:
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值以坚持使用该特定按钮?
答案 0 :(得分:49)
将您的lambda更改为lambda i=i: self.open_this(i)
。
这可能看起来很神奇,但这就是正在发生的事情。当您使用该lambda定义函数时,open_this调用在您定义函数时不会获取变量i的值。相反,它会产生一个闭包,这有点像对自己的说法“我应该在我被称为时查找变量i的值”。当然,在循环结束后调用该函数,所以那时我将始终等于循环中的最后一个值。
使用i=i
技巧会导致函数在定义lambda时存储i的当前值,而不是等待稍后查找i的值。
答案 1 :(得分:6)
这就是闭包在python中的工作方式。我曾经遇到过这个问题。
您可以使用functools.partial
。
for i in range(3):
self.button.append(Button(self, text='Game '+str(i+1), command=partial(self.open_this, i)))
答案 2 :(得分:0)
只需将您的按钮作用域附加到这样的lambda函数中:
btn["command"] = lambda btn=btn: click(btn)
,其中click(btn)
是传递按钮本身的功能。
这将创建一个从按钮到函数本身的绑定范围。
功能:
#Python2
#from Tkinter import *
#import Tkinter as tkinter
#Python3
from tkinter import *
import tkinter
root = Tk()
frame=Frame(root)
Grid.rowconfigure(root, 0, weight=1)
Grid.columnconfigure(root, 0, weight=1)
frame.grid(row=0, column=0, sticky=N+S+E+W)
grid=Frame(frame)
grid.grid(sticky=N+S+E+W, column=0, row=7, columnspan=2)
Grid.rowconfigure(frame, 7, weight=1)
Grid.columnconfigure(frame, 0, weight=1)
active="red"
default_color="white"
def main(height=5,width=5):
for x in range(width):
for y in range(height):
btn = tkinter.Button(frame, bg=default_color)
btn.grid(column=x, row=y, sticky=N+S+E+W)
btn["command"] = lambda btn=btn: click(btn)
for x in range(width):
Grid.columnconfigure(frame, x, weight=1)
for y in range(height):
Grid.rowconfigure(frame, y, weight=1)
return frame
def click(button):
if(button["bg"] == active):
button["bg"] = default_color
else:
button["bg"] = active
w= main(10,10)
tkinter.mainloop()