tkinter 按钮不调用我的事件函数
tkinter Button doesn't call my event function
这是我第一次在这里提问,所以请多多包涵。
我正在尝试为两个玩家编写一个井字游戏,该游戏应该在玩家获胜以及谁获胜时通知您。
游戏运行良好,但随后我尝试使用 OOP 进行操作,但它停止运行了。正如您可能看到的那样,我对它还很陌生,还没有完全掌握这个概念。 TkInter 也是我以前从未接触过的东西。
我尝试将点击功能放在 __init__ 功能之前和之后,但都不起作用。
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!")
你有很多问题。
首先,您不需要像在 CLI 中那样的 while True
循环,因为 tkinter(和所有 GUI)有一个运行 GUI 的主循环。当您调用 mainloop()
时,您就开始了该循环。
如果你想在不止一种方法中使用你的按钮(或任何变量)(__init__ 然后点击你的情况),那么你必须在前面用 "self." 命名它们.这些被称为 "instance variables" 并且它是 classes 使用的而不是全局变量。
您不想将 class 定义嵌套在其他内容中。如果您想使用 try 块(我认为没有理由),请将 class instance 放入 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 是否获胜?
您在构建 Button
时使用的 command = lambda: self.click
有效地创建了一堆未命名的函数,每个函数都等同于此:
def _():
return self.click
当这样写时更容易看出,除了返回 class 实例的 click
方法外,没有任何反应。然而,需要做的是在回调发生时 调用 方法(它 returns 将被忽略)。
换句话说,使用 command=lambda: self.click()
而不是您所拥有的可以解决问题。
但是,这样的函数除了增加回调过程的开销外并没有完成任何事情——这意味着直接用[=指定方法会更好15=](末尾再次没有括号,因为所有 tkinter
要求的是分配的值是不带参数的可调用值)。这样做可以避免调用一个只调用另一个函数的函数所带来的不必要的开销。
感谢@Novel 在评论中向我指出这一点——这是显而易见的——优化。
这是我第一次在这里提问,所以请多多包涵。 我正在尝试为两个玩家编写一个井字游戏,该游戏应该在玩家获胜以及谁获胜时通知您。 游戏运行良好,但随后我尝试使用 OOP 进行操作,但它停止运行了。正如您可能看到的那样,我对它还很陌生,还没有完全掌握这个概念。 TkInter 也是我以前从未接触过的东西。
我尝试将点击功能放在 __init__ 功能之前和之后,但都不起作用。
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!")
你有很多问题。
首先,您不需要像在 CLI 中那样的 while True
循环,因为 tkinter(和所有 GUI)有一个运行 GUI 的主循环。当您调用 mainloop()
时,您就开始了该循环。
如果你想在不止一种方法中使用你的按钮(或任何变量)(__init__ 然后点击你的情况),那么你必须在前面用 "self." 命名它们.这些被称为 "instance variables" 并且它是 classes 使用的而不是全局变量。
您不想将 class 定义嵌套在其他内容中。如果您想使用 try 块(我认为没有理由),请将 class instance 放入 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 是否获胜?
您在构建 Button
时使用的 command = lambda: self.click
有效地创建了一堆未命名的函数,每个函数都等同于此:
def _():
return self.click
当这样写时更容易看出,除了返回 class 实例的 click
方法外,没有任何反应。然而,需要做的是在回调发生时 调用 方法(它 returns 将被忽略)。
换句话说,使用 command=lambda: self.click()
而不是您所拥有的可以解决问题。
但是,这样的函数除了增加回调过程的开销外并没有完成任何事情——这意味着直接用[=指定方法会更好15=](末尾再次没有括号,因为所有 tkinter
要求的是分配的值是不带参数的可调用值)。这样做可以避免调用一个只调用另一个函数的函数所带来的不必要的开销。
感谢@Novel 在评论中向我指出这一点——这是显而易见的——优化。