tkinter按钮不会调用我的事件功能

时间:2017-05-01 19:56:29

标签: python python-3.x button tkinter command

这是我第一次在这里提问,所以请耐心等待。 我正在尝试为两名球员写一个井字游戏,当一名球员获胜并赢得胜利时,该球员应该通知你。 游戏运行良好,但后来我尝试用OOP做它并且它停止工作。正如你可能看到的那样,我对它很陌生并且还没有完全掌握这个概念。 TkInter也是我以前从未使用过的东西。

我尝试在__init__函数之前和之后放置click函数,但都不起作用。

from tkinter import *
from tkinter.font import Font
import tkinter.messagebox

turn = True
playerX = False  #If True, Player X won
playerO = False  #If True, Player O won
try:
    while True:
        class Game():
            def click(self, button):
                global turn
                global playerX
                global playerO
                ########## Test whether button is blank and then inserts 'X'
                if(button["text"]=="" and turn == True):
                    button["text"]= "X"
                    if(button_1["text"]=="X" and button_2["text"]=="X" and button_3["text"]=="X" or
                        button_4["text"]=="X" and button_5["text"]=="X" and button_6["text"]=="X" or
                        button_7["text"]=="X" and button_8["text"]=="X" and button_9["text"]=="X" or
                        button_1["text"]=="X" and button_5["text"]=="X" and button_9["text"]=="X" or
                        button_3["text"]=="X" and button_5["text"]=="X" and button_7["text"]=="X" or
                        button_1["text"]=="X" and button_4["text"]=="X" and button_7["text"]=="X" or
                        button_2["text"]=="X" and button_5["text"]=="X" and button_8["text"]=="X" or
                        button_3["text"]=="X" and button_6["text"]=="X" and button_9["text"]=="X"):
                        tkinter.messagebox.showinfo(title="Congrats", message="Player X won!")
                        self.root.update()
                        playerX = True
                        exit()
                    self.root.title("Player O")
                    turn=False
                ########### Test whether button is blank and then inserts 'O'   
                elif(button["text"]=="" and turn == False):
                    button["text"] = "O"
                    if(button_1["text"]=="O" and button_2["text"]=="O" and button_3["text"]=="O" or
                        button_4["text"]=="O" and button_5["text"]=="O" and button_6["text"]=="O" or
                        button_7["text"]=="O" and button_8["text"]=="O" and button_9["text"]=="O" or
                        button_1["text"]=="O" and button_5["text"]=="O" and button_9["text"]=="O" or
                        button_3["text"]=="O" and button_5["text"]=="O" and button_7["text"]=="O" or
                        button_1["text"]=="O" and button_4["text"]=="O" and button_7["text"]=="O" or
                        button_2["text"]=="O" and button_5["text"]=="O" and button_8["text"]=="O" or
                        button_3["text"]=="O" and button_6["text"]=="O" and button_9["text"]=="O"):
                        tkinter.messagebox.showinfo(title="Congrats", message="Player O won!")
                        self.root.update()
                        playerO = True
                        exit()
                    self.root.title("Player X")
                    turn = True

            def __init__(self):
                self.root = Tk()

                self.root.title("Tic-Tac-Toe")
                self.root.resizable(width=False, height=False)
                self.root.font = Font(family="Times 80", size=80)

                button_1 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click)
                button_2 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click)
                button_3 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click)
                button_4 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click)
                button_5 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click)
                button_6 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click)
                button_7 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click)
                button_8 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click)
                button_9 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click)


                button_1.grid(row=0)
                button_2.grid(row=0, column=1)
                button_3.grid(row=0, column=2)
                button_4.grid(row=1)
                button_5.grid(row=1, column=1)
                button_6.grid(row=1, column=2)
                button_7.grid(row=2)
                button_8.grid(row=2, column=1)
                button_9.grid(row=2, column=2)


        play = Game()
        play.root.mainloop()


except:
    tkinter.messagebox.showinfo(title="Error", message="Sorry there was an error!")

2 个答案:

答案 0 :(得分:1)

你有很多问题。

首先,您不需要像在CLI中那样使用while True循环,因为tkinter(和所有GUI)都有一个运行GUI的主循环。当你拨打mainloop()时,你就开始了这个循环。

如果您想在多个方法中使用按钮(或任何变量)(__init__并单击您的情况),则必须使用“self”命名它们。在前面。这些被称为“实例变量”,它是使用的类而不是全局变量。

您不希望将类定义嵌套在其他内容中。如果你想使用try块(我没有理由),那么将类实例放在try块中。

使用lambda时,必须通过在末尾添加()来调用该函数。人们通常使用lambda来调用带参数的函数,例如lambda: self.click(1)

这是你修改的代码:

from tkinter import *
from tkinter.font import Font
import tkinter.messagebox

class Game():
    def click(self, button_idx):
        button = self.buttons[button_idx]
        ########## Test whether button is blank and then inserts 'X'
        if(button["text"]=="" and self.turn == True):
            button["text"]= "X"
            if(self.button_1["text"]=="X" and self.button_2["text"]=="X" and self.button_3["text"]=="X" or
                self.button_4["text"]=="X" and self.button_5["text"]=="X" and self.button_6["text"]=="X" or
                self.button_7["text"]=="X" and self.button_8["text"]=="X" and self.button_9["text"]=="X" or
                self.button_1["text"]=="X" and self.button_5["text"]=="X" and self.button_9["text"]=="X" or
                self.button_3["text"]=="X" and self.button_5["text"]=="X" and self.button_7["text"]=="X" or
                self.button_1["text"]=="X" and self.button_4["text"]=="X" and self.button_7["text"]=="X" or
                self.button_2["text"]=="X" and self.button_5["text"]=="X" and self.button_8["text"]=="X" or
                self.button_3["text"]=="X" and self.button_6["text"]=="X" and self.button_9["text"]=="X"):
                tkinter.messagebox.showinfo(title="Congrats", message="Player X won!")
                self.root.update()
                self.playerX = True
                self.root.quit()
            self.root.title("Player O")
            self.turn=False
        ########### Test whether button is blank and then inserts 'O'
        elif(button["text"]=="" and self.turn == False):
            button["text"] = "O"
            if(self.button_1["text"]=="O" and self.button_2["text"]=="O" and self.button_3["text"]=="O" or
                self.button_4["text"]=="O" and self.button_5["text"]=="O" and self.button_6["text"]=="O" or
                self.button_7["text"]=="O" and self.button_8["text"]=="O" and self.button_9["text"]=="O" or
                self.button_1["text"]=="O" and self.button_5["text"]=="O" and self.button_9["text"]=="O" or
                self.button_3["text"]=="O" and self.button_5["text"]=="O" and self.button_7["text"]=="O" or
                self.button_1["text"]=="O" and self.button_4["text"]=="O" and self.button_7["text"]=="O" or
                self.button_2["text"]=="O" and self.button_5["text"]=="O" and self.button_8["text"]=="O" or
                self.button_3["text"]=="O" and self.button_6["text"]=="O" and self.button_9["text"]=="O"):
                tkinter.messagebox.showinfo(title="Congrats", message="Player O won!")
                self.root.update()
                self.playerO = True
                self.root.quit()
            self.root.title("Player X")
            self.turn = True

    def __init__(self):
        self.root = Tk()
        self.turn = True
        self.playerX = False  #If True, Player X won
        self.playerO = False  #If True, Player O won

        self.root.title("Tic-Tac-Toe")
        self.root.resizable(width=False, height=False)
        self.root.font = Font(family="Times 80", size=80)

        self.button_1 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click(0)) # python is zero-indexed, so 0 is the first button
        self.button_2 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click(1))
        self.button_3 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click(2))
        self.button_4 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click(3))
        self.button_5 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click(4))
        self.button_6 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click(5))
        self.button_7 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click(6))
        self.button_8 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click(7))
        self.button_9 = Button(self.root, text = "", height = 6, width = 12, command = lambda: self.click(8))

        self.button_1.grid(row=0)
        self.button_2.grid(row=0, column=1)
        self.button_3.grid(row=0, column=2)
        self.button_4.grid(row=1)
        self.button_5.grid(row=1, column=1)
        self.button_6.grid(row=1, column=2)
        self.button_7.grid(row=2)
        self.button_8.grid(row=2, column=1)
        self.button_9.grid(row=2, column=2)

        self.buttons = [self.button_1, self.button_2, self.button_3, self.button_4, self.button_5, self.button_6, self.button_7, self.button_8, self.button_9]

play = Game()
play.root.mainloop()

对于某些改进,为什么不使用循环来定义按钮?如果您这样做,则必须使用functools.partial而不是lambda来设置命令。您还可以使用self.buttons列表进行检查。此外,你重复了很多代码。为什么不制作一个可以检查X或O是否赢了的动态函数?

答案 1 :(得分:1)

构建command = lambda: self.click时你正在使用的Button也有效地创建了一堆未命名的函数,每个函数都相当于:

def _():
    return self.click

当像这样写出来时更容易看到,除了返回的类实例的click方法之外没有任何反应。然而,当回调发生时,需要发生的方法是调用(它返回的内容将被忽略)。

换句话说,使用command=lambda: self.click()代替您所拥有的内容可以解决问题。

然而,除了为回调过程增加开销之外,这样的函数没有做很多事情 - 这意味着用command=self.click直接指定方法会更好(没有最后的括号,因为所有tkinter需要的是赋值的值是可调用的,没有参数的东西。这样做可以避免不必要的费用来调用一个除了调用另一个函数之外什么都不做的函数。

感谢@Novel在评论中向我指出了这个无可否认的显而易见的优化。