Tkinter开发井字棋:如何将按钮作为参数传入函数并解决未定义报错
问题原因
你遇到的name b1 is not defined错误,是因为创建b1按钮的语句里,lambda id=b1: play(id)中的b1还没完成赋值——Python执行到这行代码时,会先解析lambda表达式,此时b1变量还没被定义(按钮对象还没创建完成),所以触发报错。
解决方案
这里给你两种实用的解决方法,都能实现用同一个函数处理所有按钮的点击逻辑:
方法1:先创建按钮,再绑定command
先完成按钮对象的创建,此时b1已经被定义,再给它设置command参数,这样lambda就能正确引用到按钮对象:
import tkinter as tk app = tk.Tk() # 假设你已提前加载所需图片 cross = tk.PhotoImage(file="cross.png") circle = tk.PhotoImage(file="circle.png") white = tk.PhotoImage(file="white.png") player = "X" # 初始玩家 def play(bid): global player # 声明使用全局变量player if player == "X": bid.config(image=cross) player = "O" # 切换玩家 else: bid.config(image=circle) player = "X" # 先创建按钮,此时b1已定义 b1 = tk.Button(app, text="", image=white) # 绑定command,lambda直接引用b1 b1.config(command=lambda: play(b1)) b1.grid(column=0, row=0) # 其他按钮按相同逻辑创建 b2 = tk.Button(app, text="", image=white) b2.config(command=lambda: play(b2)) b2.grid(column=1, row=0) app.mainloop()
方法2:用bind绑定点击事件,通过event获取按钮对象
这种方法不需要给每个按钮单独传参,直接通过点击事件的event.widget属性获取被点击的按钮,代码更简洁:
import tkinter as tk app = tk.Tk() cross = tk.PhotoImage(file="cross.png") circle = tk.PhotoImage(file="circle.png") white = tk.PhotoImage(file="white.png") player = "X" def play(event): global player bid = event.widget # 获取被点击的按钮对象 if player == "X": bid.config(image=cross) player = "O" else: bid.config(image=circle) player = "X" # 创建按钮时用bind绑定左键点击事件 b1 = tk.Button(app, text="", image=white) b1.bind('<Button-1>', play) b1.grid(column=0, row=0) b2 = tk.Button(app, text="", image=white) b2.bind('<Button-1>', play) b2.grid(column=1, row=0) app.mainloop()
额外注意点
- 必须在
play函数里声明global player,否则无法修改全局的player变量,玩家切换逻辑会失效。 - 原代码里的两个独立
if语句存在逻辑漏洞,改成if-else能确保每次点击只会执行其中一个分支。
内容的提问来源于stack exchange,提问作者nikuiz
相关产品推荐
相关产品推荐

