【问题标题】:Binding lambda to a tkinter button command returns error将 lambda 绑定到 tkinter 按钮命令返回错误
【发布时间】:2020-07-13 02:02:05
【问题描述】:

我正在尝试使用 tkinter 制作井字游戏。当我在 3x3 板上实例化按钮时,每个按钮都调用 mark_square() 的函数,并将它们自己作为参数,每个按钮都将引用嵌套 for 循环中实例化的最后一个按钮。

人们建议我将 lambda 函数绑定到我在这段代码中所做的按钮:

from tkinter import *
from tkinter import messagebox

turn = "X"


def mark_square(box):
    global turn
    if box.cget('text') == " ":
        box['text'] = turn
    else:
        messagebox.showerror(title="Invalid", message="Invalid")


root = Tk()
root.title("Tic-Tac-Toe")

for x in range(0, 3):
    for y in range(0, 3):
        grid_box = Button(text=" ", font=("Arial", 40), padx=20, command=lambda grid_box=grid_box: mark_square(grid_box))
        grid_box.grid(row=x, column=y)

root.mainloop()

但是,我收到一条错误消息,告诉我 grid_box 未定义。有谁知道发生了什么以及如何解决它?

【问题讨论】:

  • 您没有正确执行此操作,因为您试图将 grid_box 的值作为参数的默认值传递给 lambda 函数,但是在第一次迭代中for 循环,该变量尚不存在,这将导致错误......即使它不存在,每次它都会引用最后一次分配给变量的值(这将是 上一个 Button)。
  • grid_box 尚未创建。所以创建后使用grid_box.config(command=lambda ...)
  • 发布完整的追溯消息。它显示了失败的确切错误和行。

标签: python tkinter


【解决方案1】:

Lambda 函数可以有不同的使用方式。

尝试使用以下代码:

from tkinter import *
from tkinter import messagebox

turn = "X"


def mark_square(box):
    global turn
    if box.cget('text') == " ":
        box['text'] = turn
    else:
        messagebox.showerror(title="Invalid", message="Invalid")


root = Tk()
root.title("Tic-Tac-Toe")

for x in range(0, 3):
    for y in range(0, 3):
        grid_box = Button(text=" ", font=("Arial", 40), padx=20, command=lambda grid_box: mark_square(grid_box))
        grid_box.grid(row=x, column=y)

root.mainloop()

【讨论】:

  • 我遇到了一些问题。所以还没有测试。但是错误原因似乎是这样的。
  • 不,这不是 OP 问题的答案。
  • 我有一些环境问题。您在我的代码中遇到任何问题吗?您能否提供错误消息或回溯?
  • 当点击其中一个按钮时,您的代码将引发TypeError: <lambda>() missing 1 required positional argument: 'grid_box'
【解决方案2】:

如前所述,在lambda grid_box=grid_box: 中,第二个grid_box 正在当前上下文中搜索名为“grid_box”的变量,但它之前您已将Button 分配给@987654324 @,所以你得到了错误。即使你定义了grid_box,它也不起作用,因为Button command 回调没有参数。

一种解决方案是编写自己的Button 子类,它拦截命令回调以添加您想要的按钮信息。方便的是,它是正在初始化的按钮的self。当您将实例方法分配给回调时,它知道自己的“自我”,并且可用于存储对回调有用的各种状态。

from tkinter import *
from tkinter import messagebox

turn = "X"


def mark_square(box):
    global turn
    if box.cget('text') == " ":
        box['text'] = turn
    else:
        messagebox.showerror(title="Invalid", message="Invalid")

class ButtonWithContext(Button):
    """Specializes tkinter.Button to allow a `command` that takes
    the button as a parameter"""

    def __init__(self, *args, **kwargs):
        try:
            self._my_command = kwargs["command"]
            kwargs["command"] = self.run_command
        except KeyError:
            self._my_command = None
        super().__init__(*args, **kwargs)

    def run_command(self):
        if self._my_command is not None:
            return self._my_command(self)


root = Tk()
root.title("Tic-Tac-Toe")

for x in range(0, 3):
    for y in range(0, 3):
        grid_box = ButtonWithContext(text=" ", font=("Arial", 40), padx=20,
            command=mark_square)
        grid_box.grid(row=x, column=y)

root.mainloop()

【讨论】:

    猜你喜欢
    • 2013-10-17
    • 2020-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-30
    • 1970-01-01
    • 2013-09-16
    • 2013-04-19
    相关资源
    最近更新 更多