我试图用3个按钮制作一个简单的界面,每个按钮应根据其标签触发一个动作。但是,即使(我认为)我传递了正确的参数,它也始终传递了最后一个按钮的标签。这是一个精简版,以显示正在发生的事情:
import tkinter as tk
import random
class Application(tk.Frame):
def __init__(self, window=None):
super().__init__(window)
self.labels = ['Washington','London','Paris','Rome','Berlin','Madrid']
self.buttons = [tk.Button(self),tk.Button(self),tk.Button(self)]
self.pack()
for k,button in enumerate(self.buttons):
button.config(width=10)
button.grid(row=0, column=k)
self.update_buttons()
def update_buttons(self):
labels = list(random.sample(self.labels,3))
random.shuffle(labels)
for label,button in zip(labels,self.buttons):
button["text"] = label
button["command"] = lambda: self.verify(label)
def verify(self, label):
print(f'You pressed the button with label {label}')
self.update_buttons()
window = tk.Tk()
app = Application(window=window)
app.mainloop()
为什么?
答案 0 :(得分:2)
您遇到(late biding) closure problem。
使用lambda
创建函数时,将创建一个闭包。这意味着在调用lambda时将在函数主体中查找变量,而不是在创建lambda时查找(并且创建lambda的范围将包含具有最终赋值的变量)。
为防止这种情况,您需要创建一个参数并将其设置为默认值,该默认值将在创建时存储变量的当前值。
import tkinter as tk
import random
class Application(tk.Frame):
def __init__(self, window=None):
super().__init__(window)
self.labels = ['Washington','London','Paris','Rome','Berlin','Madrid']
self.buttons = [tk.Button(self),tk.Button(self),tk.Button(self)]
self.pack()
for k,button in enumerate(self.buttons):
button.config(width=10)
button.grid(row=0, column=k)
self.update_buttons()
def update_buttons(self):
labels = list(random.sample(self.labels,3))
random.shuffle(labels)
for label,button in zip(labels,self.buttons):
button["text"] = label
button["command"] = lambda label=label: self.verify(label) # Here
def verify(self, label):
print(f'You pressed the button with label {label}')
self.update_buttons()
window = tk.Tk()
app = Application(window=window)
app.mainloop()
您还可以使用functools.partial
,我认为它看起来更干净:
import tkinter as tk
import random
from functools import partial
class Application(tk.Frame):
def __init__(self, window=None):
super().__init__(window)
self.labels = ['Washington','London','Paris','Rome','Berlin','Madrid']
self.buttons = [tk.Button(self),tk.Button(self),tk.Button(self)]
self.pack()
for k,button in enumerate(self.buttons):
button.config(width=10)
button.grid(row=0, column=k)
self.update_buttons()
def update_buttons(self):
labels = list(random.sample(self.labels,3))
random.shuffle(labels)
for label,button in zip(labels,self.buttons):
button["text"] = label
button["command"] = partial(self.verify, label)
def verify(self, label):
print(f'You pressed the button with label {label}')
self.update_buttons()
window = tk.Tk()
app = Application(window=window)
app.mainloop()
答案 1 :(得分:1)
您需要为lambda函数分配一个参数,并将其作为参数传递给该函数。
import tkinter as tk
import random
class Application(tk.Frame):
def __init__(self, window=None):
super().__init__(window)
self.labels = ['Washington','London','Paris','Rome','Berlin','Madrid']
self.buttons = [tk.Button(self),tk.Button(self),tk.Button(self)]
self.pack()
for k,button in enumerate(self.buttons):
button.config(width=10)
button.grid(row=0, column=k)
self.update_buttons()
def update_buttons(self):
labels = list(random.sample(self.labels,3))
random.shuffle(labels)
for label,button in zip(labels,self.buttons):
button["text"] = label
button["command"] = lambda lbl=label: self.verify(lbl) # <-- here
def verify(self, label):
print(f'You pressed the button with label {label}', flush=True) # <-- added flush=True to ensure the printing is done as the moment you click
self.update_buttons()
window = tk.Tk()
app = Application(window=window)
app.mainloop()