tkinter widget.unbind异常:指定funcid却移除全部事件回调
Tkinter unbind移除指定funcid时意外删除所有绑定的问题
我调用unbind(seq, funcid=_id)时,原本只想移除widget对应事件序列中指定_id的回调函数,但实际却删掉了该序列下的所有回调。这个问题在macOS和Linux上都能复现,是我操作错了还是对文档理解有偏差?
测试代码
import tkinter as tk click1_id = None # 记录添加的函数ID def add_binding(): global click1_id print('Add Binding', label.bind(), click1_id, end=' ') # 追踪日志 if click1_id is None: click1_id = label.bind("<Button>", on_click1(), add='+') # on_click1() 返回一个带序号的函数 print('Added Binding', label.bind(), click1_id, '\n') # 追踪日志 def remove_binding(): global click1_id print('Remove', click1_id, end=" ") # 追踪日志 if click1_id: print('Unbinding', click1_id) # 追踪日志 label.unbind("<Button>", funcid=click1_id) # 这里本该只移除指定funcid的绑定,却删掉了所有 click1_id = None print('Bindings:', label.bind(), 'ID:', click1_id, '\n') # 追踪日志 def on_click0(event): print("Click: 0", event, 'Widget:', event.widget) def on_click_root(event): # 即使Label的所有事件被解绑,仍能检测到点击 print("Click root", event, 'Widget:', event.widget, '\n') seq = 0 def on_click1(): """返回一个带序号的回调函数""" global seq seq += 1 count = seq def func(event): print('Click:', count, event, 'Widget:', event.widget) return func root = tk.Tk() label = tk.Label(root, text='Click to action') doadd = tk.Button(root, text=' Add binding ', command=add_binding) doremove = tk.Button(root, text='Remove binding', command=remove_binding) # 这里是关键:第一次绑定未使用add='+' label.bind("<Button>", on_click0) root.bind("<Button>", on_click_root) label.grid(row=0, column=0, columnspan=2, pady=5) doadd.grid(row=1, column=0, padx=5, pady=5) doremove.grid(row=1, column=1, padx=5, pady=5) root.mainloop()
操作追踪日志
# 点击Label - 符合预期 Click: 0 <ButtonPress event num=1 x=40 y=15> Widget: .!label Click root <ButtonPress event num=1 x=40 y=15> Widget: .!label # 点击Add Binding - 符合预期 Click root <ButtonPress event num=1 x=56 y=12> Widget: .!button Add Binding ('<Button>',) None Added Binding ('<Button>',) 140691558063232func # 点击Label - 符合预期 Click: 0 <ButtonPress event num=1 x=39 y=14> Widget: .!label Click: 1 <ButtonPress event num=1 x=39 y=14> Widget: .!label Click root <ButtonPress event num=1 x=39 y=14> Widget: .!label # 点击Remove Binding - 异常 Click root <ButtonPress event num=1 x=58 y=12> Widget: .!button2 Remove 140691558063232func Unbinding 140691558063232func Bindings: () ID: None # 预期应为 Bindings: ('<Button>',) # 点击Label - 预期输出Click:0,实际只有Click root Click root <ButtonPress event num=1 x=62 y=11> Widget: .!label # 点击Add Binding Click root <ButtonPress event num=1 x=74 y=17> Widget: .!button Add Binding () None Added Binding ('<Button>',) 140691567712256func # 点击Label - 仅输出Click 2 Click: 2 <ButtonPress event num=1 x=48 y=15> Widget: .!label Click root <ButtonPress event num=1 x=48 y=15> Widget: .!label
问题原因与解决方案
问题根源
你第一次绑定on_click0时没有使用add='+'参数,这会让Tkinter将其设置为该事件序列的默认绑定(无独立标识tag);而后续添加的on_click1返回的函数使用了add='+',会被分配一个独立的funcid(tag)。当你调用unbind指定这个独立funcid时,Tk的底层逻辑出现异常,误将整个事件序列的绑定(包括默认绑定)全部删除。
解决方案
所有绑定操作都使用add='+'参数,确保每个回调都拥有独立的标识tag,这样unbind指定funcid时只会移除对应的回调,不会影响其他绑定。
修改后的关键代码:
# 保存第一个绑定的funcid,并使用add='+' click0_id = label.bind("<Button>", on_click0, add='+')
修改后再测试:
- 点击Label会触发
Click:0和Click root - 添加绑定后点击Label会触发
Click:0、Click:1和Click root - 移除绑定后点击Label会恢复触发
Click:0和Click root,符合预期
内容的提问来源于stack exchange,提问作者Tls Chris
相关产品推荐
相关产品推荐

