我正在尝试使用Tkinter为Tic Tac Toe的游戏实现一个简单的GUI。第一步,我试图制作一系列按钮,这些按钮从未标记变为单击时带有“ X”标记。我尝试了以下方法:
import Tkinter as tk
class ChangeButton:
def __init__(self, master, grid=np.diag(np.ones(3))):
frame = tk.Frame(master)
frame.pack()
self.grid = grid
self.buttons = [[tk.Button()]*3]*3
for i in range(3):
for j in range(3):
self.buttons[i][j] = tk.Button(frame, text="", command=self.toggle_text(self.buttons[i][j]))
self.buttons[i][j].grid(row=i, column=j)
def toggle_text(self, button):
if button["text"] == "":
button["text"] = "X"
root = tk.Tk()
root.title("Tic Tac Toe")
app = ChangeButton(root)
root.mainloop()
但是,结果窗口如下所示:
并且单击时按钮不会改变。任何想法为什么这不起作用?
主要的问题是,使用时command=self.toggle_text(self.buttons[i][j]))
,您将调用回调函数,并将其结果绑定到command. Instead, you have to bind the function itself to
命令, or a
lambda`,该命令将使用正确的参数来调用该函数。一个简单的方法是这样的:
command=lambda: self.toggle_text(self.buttons[i][j]) # won't work!
但这在循环内部不起作用,因为在执行函数时(即循环之后)对里面的变量lambda
进行求值,即并且会为每个函数取最后一个值。有关更详细的说明,请参见例如此处。解决此问题的一种方法是将这些变量声明为的参数,同时使用循环中的当前值作为其默认值,即。这样,并且在声明函数时(而不是在调用函数时)进行求值。i
j
lambda
lambda i=i, j=j: ...
i
j
command
然后,您和周围的循环将如下所示:
for i in range(3):
for j in range(3):
self.buttons[i][j] = tk.Button(frame, text="",
command=lambda i=i, j=j: self.toggle_text(self.buttons[i][j]))
self.buttons[i][j].grid(row=i, column=j)
初始化self.buttons
列表的方式还有另一个问题,与第一个问题无关。这样[[tk.Button()]*3]*3
,该列表将包含对同一列表的三个引用,每个引用均包含对同一按钮的三个引用。参见例如此处以进行更深入的讨论。同样,您完全不需要初始化列表中的按钮,因为您随后在循环中进行了设置。我建议改用嵌套的列表理解:
self.buttons = [[None for _ in range(3)] for _ in range(3)]
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句