Python tkinter为循环传递命令参数创建按钮

Python tkinter为循环传递命令参数创建按钮,python,tkinter,Python,Tkinter,我正在尝试在for循环中的tkinter中创建按钮。在每个循环中,将i count值作为参数传递给命令值。因此,当从命令值调用函数时,我可以知道按下了哪个按钮,并相应地执行操作。问题是,假设len为3,它将创建3个标题为“Game 1”到“Game 3”的按钮,但当按下任何按钮时,打印值始终为2,即最后一次迭代。因此,按钮似乎是作为单独的实体制作的,但命令参数中的i值似乎完全相同。代码如下: def createGameURLs(self): self.button = [] f

我正在尝试在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值,以使用特定的按钮?

这就是闭包在python中的工作方式。我自己也遇到过这个问题。 你可以用这个

for i in range(3):
    self.button.append(Button(self, text='Game '+str(i+1), command=partial(self.open_this, i)))

将lambda更改为
lambda i=i:self。打开此(i)

这可能看起来很神奇,但发生的事情如下。使用该lambda定义函数时,此调用的open_在定义函数时不会获取变量i的值。取而代之的是,它做了一个闭包,这有点像是给自己写了一个便条,上面写着“我应该寻找我被调用时变量I的值”。当然,这个函数是在循环结束后调用的,所以在那个时候,我将始终等于循环中的最后一个值


使用
i=i
技巧可使函数在定义lambda时存储i的当前值,而不是等待以后查找i的值。

只需将按钮范围附加到lambda函数中,如下所示:

btn[“command”]=lambda btn=btn:click(btn)
其中
click(btn)
是按钮本身传递的功能。 这将创建一个从按钮到函数本身的绑定范围

特点:

  • 自定义网格大小
  • 响应式调整尺寸
  • 切换活动状态


非常感谢你们两位,卢卡德和布伦巴恩,我已经为此奋斗了好几天,信不信由你。两种方法都非常有效。我暂时使用了I=I修复程序,但我肯定会仔细阅读这些工具。我很欣赏这两个答案。如果BrenBarns的解决方案对您有效,那么您应该将其标记为您接受的答案。如果我们想将两个参数传递给像open_this这样的函数,可能会出现重复?阿门:这取决于您希望这些参数是什么。如果两者都来自某个外部循环,并且您希望以上面所示的方式“冻结”它们,那么您只需执行
lambda x=x,y=y:self。打开这个(x,y)
。这是一个出色、直截了当且很好的解释。这应该是答案。@BrenBarn刚才我遇到了同样的情况,真是太神奇了。我真的很奇怪你们是怎么发现这些技术的。“太谢谢你了!”酷云:跟我说的一样。您说过“参数存储在函数中”。我说“函数存储值”。这是同样的事情。看起来很简单,如果你使用虚拟环境,你可能会遇到问题,请参阅第一页:
self.button.append(button(self,text='Game'+str(i+1),command=lambda x=i:self.open_this(x))
#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()