Tkinter循环创建控件绑定点击事件无法区分触发源问题
问题根因
两种绑定方式的回调传参逻辑存在本质差异:
- 控件
command属性绑定的回调,不会被自动传入额外参数,你写的lambda btn=btn: click(btn)可以正常通过默认参数拿到循环当前轮次的控件实例 bind()绑定的事件回调,tkinter会强制将事件对象Event作为第一个位置参数传入回调,你原来的lambda写法没有预留位置接收这个参数,导致传入的Event对象直接覆盖了你预设的btn默认值,最后传给click函数的是事件对象而非控件,自然无法区分具体触发的控件。
修复方法
你可以任选以下一种方案修复,两种方案对Label、Button等所有tkinter控件、对左键<Button-1>/右键<Button-3>等所有鼠标事件都生效:
方案1:调整lambda参数,接住自动传入的事件对象
修改bind行的lambda写法,预留第一个参数接收event,不覆盖你预设的控件默认参数即可:
active="red" default_color="white" def main(height=5,width=5): for x in range(width): for y in range(height): btn = tkinter.Button(frame, bg=default_color) btn.grid(column=x, row=y, sticky=N+S+E+W) # 仅修改这一行,第一个参数接自动传入的event,第二个参数用默认值存当前控件 btn.bind("<Button-1>", lambda event, btn=btn: click(btn)) for x in range(width): Grid.columnconfigure(frame, x, weight=1) for y in range(height): Grid.rowconfigure(frame, y, weight=1) return frame def click(button): print(button) if(button["bg"] == active): button["bg"] = default_color else: button["bg"] = active w= main(10,10)
方案2:直接从事件对象取触发控件(推荐)
tkinter传入的事件对象自带widget属性,指向触发当前事件的控件实例,不需要在循环里靠lambda默认参数缓存控件,写法更简洁也不容易出参数覆盖问题:
active="red" default_color="white" def main(height=5,width=5): for x in range(width): for y in range(height): btn = tkinter.Button(frame, bg=default_color) btn.grid(column=x, row=y, sticky=N+S+E+W) # 直接把函数传给bind即可,不需要套lambda传参 btn.bind("<Button-1>", click) # 如果要绑定右键点击,参照左键逻辑新增绑定即可 # btn.bind("<Button-3>", right_click) for x in range(width): Grid.columnconfigure(frame, x, weight=1) for y in range(height): Grid.rowconfigure(frame, y, weight=1) return frame def click(event): # 直接从事件对象取触发的控件 button = event.widget print(button) if(button["bg"] == active): button["bg"] = default_color else: button["bg"] = active w= main(10,10)
提示:如果你要给承载图片的Label绑定点击事件,两种方案完全通用,不需要额外调整逻辑。
内容的提问来源于stack exchange,提问作者Nothex
相关产品推荐
相关产品推荐

