Welcome, guest | Sign In | My Account | Store | Cart

This recipes tries to solve the problem of bind_all and unbind_all for tkinter.

When a callback is registered using bind_all method and later it's unregistered using unbind_all, all the callbacks are deleted for the "all" tag event. This makes difficult to register and unregister only one callback at a time. This recipes tries to solve this problem.

Observe the difference between the code below and the recipe. With the code below, when the user clicks nothing happens. But with my recipe it's possible to bind and unbind specific callbacks.

try:
    from Tkinter import Tk, Frame
except ImportError:
    from tkinter import Tk, Frame

root = Tk()
f = Frame(root, width= 300, height=300)
f.pack()

def callback1(event):
    print("callback1")

def callback2(event):
    print("callback2")

def callback3(event):
    print("callback3")

root.bind_all("<1>", callback1, add="+")
f.bind_all("<1>", callback2, add="+")
f.bind_all("<1>", callback3, add="+")

f.unbind_all("<1>")

root.mainloop()
Python, 72 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
from __future__ import print_function

import sys
import collections
import functools

_handlers = collections.defaultdict(list)

def _run_handlers(list_of_handlers, event):
    exc_info = None
    for func in list_of_handlers:
        try:
            func(event)
        except SystemExit:
            exc_info = sys.exc_info()
        except:
            import traceback
            print("Error in global_binding._run_handlers:", file=sys.stderr)
            traceback.print_exc()
            exc_info = sys.exc_info()

    if exc_info is not None:
        raise exc_info[0], exc_info[1], exc_info[2]


def global_bind(w, event, func, add=None):
    root = w.nametowidget(".")
    
    handlers_of_event = _handlers[event]
    if len(handlers_of_event) == 0:
        root.bind_all(event, functools.partial(_run_handlers, handlers_of_event))
    
    if add == "+":
        handlers_of_event.append(func)
    else:
        handlers_of_event[:] = [func]

    return func

def global_unbind(w, event, func):
    handlers_of_event = _handlers[event]
    handlers_of_event.remove(func)
    
    if len(handlers_of_event) == 0:
        w.unbind_all(event)
        
if __name__ == "__main__":
    try:
        from Tkinter import Tk, Frame
    except ImportError:
        from tkinter import Tk, Frame
        
    root = Tk()
    f = Frame(root, width= 300, height=300)
    f.pack()
    
    def callback1(event):
        print("callback1")
        
    def callback2(event):
        print("callback2")
    
    def callback3(event):
        print("callback3")
        
    global_bind(root, "<1>", callback1, add="+")
    global_bind(f, "<1>", callback2, add="+")
    global_bind(f, "<1>", callback3, add="+")
    
    global_unbind(f, "<1>", callback1)
    
    root.mainloop()