How can I unbind every single binding from a Tkinter root window

Question:

So I have an app in Tkinter that has a lot of buttons in the first screen and when you press one you pass into a new “Window” (basically destroying all widgets and drawing the ones that are needed for the ‘window’). There is a standard function that uses some commands to destroy every child on the root. I would like to add some code that can unbind all of the bindings that are made in the root. Bindings that are on specific widgets get destroyed but those that are bind on the root stay there and cause error.

Here’s the code for destroying the widgets.

@staticmethod
def clear():
    for widget in guihandle.root.winfo_children():
        widget.destroy()

@staticmethod
def set(db,table):

    guihandle.clear()
    curW = Window(db,table)
    guihandle.current_Window = curW
    curW.initialize()
    guihandle.windows.push(curW)

(Yes, I make the base GUI from a sqlite3 database :P)

Asked By: nikosdcs

||

Answers:

There is nothing in Tkinter to do what you want. Your app will need to keep track of the bindings it wants to remove.

That being said, depending on just how complex your real problem is, there may be other solutions. For example, instead of binding to the root window, bind to a custom binding tag (also called a bind tag or bindtag). You will then need to add that tag to every widget to enable the bindings, and remove the tag from any existing widgets to effectively disable the bindings.

Answered By: Bryan Oakley

I know I am VERY late but, if you go through your code and replace every widget.bind with the function below, which was taken and modified from here

def bindWidget(widget: Widget, event, all_bool=False, func=None):
    # https://stackoverflow.com/a/226141/19581763
    '''Set or retrieve the binding for an event on a widget'''
    try:
        _ = widget.__dict__['bindings']
    except KeyError:
        has_binding_key = False
    else:
        has_binding_key = True
    if not has_binding_key:
        setattr(widget, 'bindings', dict())

    if func:
        if not all:
            widget.bind(event, func)
        else:
            widget.bind_all(event, func)
        widget.bindings[event] = func
    else:
        return(widget.bindings.setdefault(event, None))

Than the function will keep track of every binding for you by setting a attribute called bindings which has both, the event and the function inside of it.

For example:

label.bind('<Button-1>', label.destroy)

Will become:

bindWidget(label, '<Button-1>', func=label.destroy)

After doing that, you can write a simple function which deletes all of the bindings and widgets:

def clear(self): # Self will be your Tk() instance
    """Clears everything on the window, including bindings"""
    for child in self.winfo_children():
        child.destroy()
    if not hasattr(self, 'bindings'):
        self.bindings = {}
    for event, func in self.bindings.items():
        self.unbind_all(event)
    self.bindings = {}

There are only 2 caveats with this approach

  1. Time
    • You will have to go through your code to replace every widget.bind and if you have lots of bindings, than it will take a lot of time
  2. Readability
    • bindWidget(label, '<Button-1>', func=label.destroy) is less readable and less clear than label.bind('<Button-1>', label.destroy)
Edit 8/11/2022

Alternatively, you can destroy the entire window and recreate it like this:

window.destroy()
window = Tk()

However, I am not sure if creating a whole Tcl interpreter is good.

Answered By: superheroyrr