Python Tkinter按钮没有传递正确的参数?

时间:2018-09-06 15:12:11

标签: python tkinter

我试图用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()

为什么?

2 个答案:

答案 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()