Search code examples
python-3.xtkintercallback

In Tkinter, is it possible to retrieve the list of callback functions bound to a widget?


Given the following code in which multiple callbacks are associated with a single button:

import tkinter as tk

def hi(event):
    print('hello')
    return hi

def bye():
    print('bye')
    return bye

def salutations(event):
    print('... and salutations...')
    return bye

def and_so_forth(event):
    print('... and so forth...')
    return bye

root = tk.Tk()
button = tk.Button(root, text='test', command = bye)
button.bind("<Button-1>", hi)
button.bind("<Button-1>", salutations, "+")
button.bind("<Button-1>", and_so_forth, "+")

button.pack()
root.mainloop()

Is there a method I can call that will list all callbacks bound to button?

The results I'd like to get are something akin to:

command_list = ('hi', 'salutations', 'and_so_forth', 'bye')

What I've tried:

  • I searched the property list, but didn't see anything promising.
  • button.command.get() (it's ignored)
  • button.command.cget() (also ignored)
  • button.invoke() (only reports on bye function)

Does anyone know if this is even possible?

Thank you.


Solution

  • The closest thing I've found is the _tclCommands attribute:

    commands = button._tclCommands
    print(commands)
    # >>> ['1310081992192bye', '1310100638592hi', '1310100643904salutations', '1310100643712and_so_forth']
    

    Given the leading underscore, I don't imagine you're supposed to do this, but it's as close as I could get. Also, bear in mind that those leading numbers change every time you run your app.

    It's a bit cumbersome, but you can also cross-reference this list with the functions listed by dir() to get the names of the callbacks for a given widget:

    def get_callbacks(widget):
        callbacks = []
        for fn in dir():  # list all functions in this module
            for callback_name in widget._tclCommands:  # get callbacks
                if fn in callback_name:  # print matches
                    callbacks.append[fn]
        return callbacks
    
    # naturally, this could also be done with a list comprehension, but it's
    # a bit easier to see what's going on this way
    # callbacks = [fn for fn in dir() for cmd in widget._tclCommands if fn in cmd]
    

    EDIT to point out that if you have a binding to a lambda on your widget, then the _tclCommands attribute will include something along the lines of '2160369114752<lambda>' for each lambda bound. In this case, cross-referencing dir() won't work, so you'll have to resort to just stripping the numbers off instead.