Привязка лямбда к команде кнопки tkinter возвращает ошибку - PullRequest
1 голос
/ 13 июля 2020

Я пытаюсь сделать игру ti c ta c toe с tkinter. Когда я создаю экземпляры кнопок на доске 3x3, каждая из которых вызывает функцию mark_square() с самим собой в качестве параметра, каждая кнопка будет ссылаться на последнюю кнопку, экземпляр которой был создан во вложенном для l oop.

People предложили привязать лямбда-функцию к кнопке, что я и сделал в этом коде:

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 не определен. Кто-нибудь знает что происходит и как исправить?

Ответы [ 2 ]

0 голосов
/ 13 июля 2020

Как уже упоминалось, в lambda grid_box=grid_box: второй grid_box ищет переменную с именем «grid_box» в текущем контексте, но его перед вы присвоили Button grid_box , поэтому вы получите ошибку. Даже если вы определили grid_box, это не сработает, потому что обратный вызов Button command не принимает параметров.

Решение состоит в том, чтобы написать собственный подкласс Button, который перехватывает обратный вызов команды для добавления информацию о кнопках, которую вы хотите. Удобно, что это self инициализируемой кнопки. Когда вы назначаете метод экземпляра для обратного вызова, он знает свое собственное «я», и его можно использовать для sh всех состояний, полезных для обратных вызовов.

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()
0 голосов
/ 13 июля 2020

Лямбда-функции могут использоваться по-разному.

Попробуйте использовать следующий код:

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...