Tkinter批量循环创建的Frame绑定事件失效问题求助
Tkinter批量循环创建的Frame绑定事件失效问题求助
兄弟我帮你分析下问题出在哪哈,你这是踩了Tkinter绑定事件时的两个常见坑:bind_all的误用和lambda表达式的延迟绑定,一步步给你拆解解决~
问题1:第一个代码的核心问题:bind_all不是绑定单个组件!
你用了main.bind_all('<Button-1>', ...),这个方法是把事件绑定到整个Tkinter应用的所有组件,意思是不管你点界面上哪个地方,都会触发这个事件。更关键的是,lambda里的main变量在循环结束后,指向的是最后一个创建的Frame,所以不管你点哪个Frame,实际操作的都是最后那个。
解决办法很简单:把bind_all换成bind,这样事件只会绑定到当前的Frame上;同时给lambda加个默认参数,捕获当前的Frame实例,避免延迟绑定的坑。
修改后的第一个代码版本:
import tkinter as tk r = tk.Tk() def change_color(wid): print(wid) if wid['bg'] == 'purple': wid['bg'] = 'green' elif wid['bg'] == 'green': wid['bg'] = 'purple' def frame(): main = tk.Frame(r, width=50, height=50, borderwidth=1, relief='solid', bg='purple') # 把bind_all改成bind,用默认参数current_frame=main捕获当前实例 main.bind('<Button-1>', lambda a, current_frame=main: change_color(current_frame)) main.pack() x = 0 while x < 10: frame() x += 1 r.mainloop()
问题2:字典版本的坑:lambda延迟绑定+KeyError
你第二个代码里,循环变量x在lambda里是延迟求值的——循环结束后x变成了3,所以lambda里的dic[str(x)]就变成了dic['3'],但你的循环是x < 3,也就是只创建了x=1和2的Frame,字典里根本没有'3'这个键,自然会报KeyError。而且你还是用了bind_all,同样会触发全局事件。
解决这个的话,同样要换bind,并且用默认参数捕获当前的Frame或者当前的x值。下面是修改后的字典版本:
import tkinter as tk r = tk.Tk() def change_color(wid): print(wid) if wid['bg'] == 'purple': wid['bg'] = 'green' elif wid['bg'] == 'green': wid['bg'] = 'purple' dic = {} x = 1 while x < 3: dic[str(x)] = tk.Frame(r, width=50, height=50, borderwidth=1, relief='solid', bg='purple') # 换成bind,用默认参数捕获当前的frame dic[str(x)].bind('<Button-1>', lambda a, current_frame=dic[str(x)]: change_color(current_frame)) # 或者也可以捕获当前的x值:lambda a, x_val=x: change_color(dic[str(x_val)]) dic[str(x)].pack() x += 1 r.mainloop()
额外小技巧:利用事件对象直接获取触发组件
其实还有更简洁的方法:Tkinter的事件处理函数会自动接收一个event对象,这个对象的widget属性就是触发事件的组件本身。这样你连传递组件实例都不用了!
示例代码如下:
import tkinter as tk r = tk.Tk() def change_color(event): wid = event.widget print(wid) if wid['bg'] == 'purple': wid['bg'] = 'green' elif wid['bg'] == 'green': wid['bg'] = 'purple' x = 0 while x < 10: main = tk.Frame(r, width=50, height=50, borderwidth=1, relief='solid', bg='purple') main.bind('<Button-1>', change_color) # 直接传函数,不用lambda main.pack() x += 1 r.mainloop()
这个方法更干净,完全避开了lambda绑定的问题~
备注:内容来源于stack exchange,提问作者Red_Meer
相关产品推荐
相关产品推荐

