Tkinter井字棋GUI:鼠标悬停动态聚焦Frame失效问题排查
问题原因分析
你的猜测完全正确——循环里的lambda表达式并没有捕获当前循环迭代时的i和j值,而是持有对i、j变量的引用。当循环结束后,i和j最终都停留在2,所以所有<Enter>事件触发时,lambda都会调用on_event并传入self.frames[2][2],也就是最后创建的右下角Frame。
解决方法
有三种常用的方式可以解决这个循环绑定lambda的变量引用问题:
方法1:利用lambda默认参数捕获当前值
在lambda中添加默认参数,把当前的i、j值绑定到默认参数上,这样每次循环迭代时都会生成一个带有当前值的lambda:
import tkinter as tk def on_event(event, target): target.focus() print(target) class Example(tk.Tk): def __init__(self): super().__init__() self.frames = [] self.main = tk.Frame(self, bg = "black") self.build_widgets() self.main.pack() def build_widgets(self): for i in range(3): self.frames.append([]) for j in range(3): newframe = tk.Frame(self.main, bg='white', width=50, height=50, highlightcolor="blue", highlightthickness=1) self.frames[i].append(newframe) # 添加默认参数i=i、j=j,捕获当前循环的i和j值 self.frames[i][j].bind("<Enter>", lambda event, i=i, j=j : on_event(event, self.frames[i][j])) self.frames[i][j].grid(row=i, column=j, padx = 1, pady = 1) Example().mainloop()
方法2:直接传入当前创建的newframe
既然已经创建了newframe变量,完全不需要通过self.frames[i][j]来获取,直接把newframe作为参数传给lambda,更简洁:
import tkinter as tk def on_event(event, target): target.focus() print(target) class Example(tk.Tk): def __init__(self): super().__init__() self.frames = [] self.main = tk.Frame(self, bg = "black") self.build_widgets() self.main.pack() def build_widgets(self): for i in range(3): self.frames.append([]) for j in range(3): newframe = tk.Frame(self.main, bg='white', width=50, height=50, highlightcolor="blue", highlightthickness=1) self.frames[i].append(newframe) # 直接传入当前的newframe self.frames[i][j].bind("<Enter>", lambda event, frame=newframe : on_event(event, frame)) self.frames[i][j].grid(row=i, column=j, padx = 1, pady = 1) Example().mainloop()
方法3:使用functools.partial绑定参数
functools.partial可以帮你提前绑定函数参数,避免lambda的变量引用问题:
import tkinter as tk from functools import partial def on_event(event, target): target.focus() print(target) class Example(tk.Tk): def __init__(self): super().__init__() self.frames = [] self.main = tk.Frame(self, bg = "black") self.build_widgets() self.main.pack() def build_widgets(self): for i in range(3): self.frames.append([]) for j in range(3): newframe = tk.Frame(self.main, bg='white', width=50, height=50, highlightcolor="blue", highlightthickness=1) self.frames[i].append(newframe) # 使用partial绑定target参数 self.frames[i][j].bind("<Enter>", partial(on_event, target=newframe)) self.frames[i][j].grid(row=i, column=j, padx = 1, pady = 1) Example().mainloop()
任意选择一种方法,都能让每个Frame绑定到自己的<Enter>事件,鼠标进入时正确聚焦对应的Frame。
内容的提问来源于stack exchange,提问作者Korido
相关产品推荐
相关产品推荐

