You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 19:42:23