Search code examples
pythonjupyter-notebookgoogle-colaboratoryipywidgets

How to remove all previous registered handlers for widgets events (registered with observe or on_click)


I have created and registered a handler.

# Example code
selectWidgetName.observe(myFunction, 'value')

Then I updated the code of the function and want to re-register it with the same command.

That adds a handler in parallel to the first one, not overwriting the first one.

How can I remove all existing handlers so as to apply a new and only one?


Solution

  • For buttons:

    # Remove existing click handler
    buttonWidgetName._click_handlers.callbacks = []
    
    # Add our handler
    buttonWidgetName.on_click(newFunction)
    

    For widgets that use observe:

    # Remove existing handler 
    # not sure if this
    selectWidgetName.unobserve(None)
    # or this
    selectWidgetName.unobserve(oldFunction)
    
    # Add our handler
    selectWidgetName.observe(newFunction, 'value')
    

    Thanks to NimSed for the second one.

    However, if you try to remove a handler still not present you get

    /usr/local/lib/python3.7/dist-packages/traitlets/traitlets.py in _remove_notifiers(self, handler, name, type)
       1284                 del self._trait_notifiers[name][type]
       1285             else:
    -> 1286                 self._trait_notifiers[name][type].remove(handler)
       1287         except KeyError:
       1288             pass
    
    ValueError: list.remove(x): x not in list
    

    So we need a way to know the existing handlers attached to the widget, and if present, remove it.

    To know all the handlers look into

    widget_name._trait_notifiers
    

    An example output:

    {'comm': {'change': [<traitlets.traitlets.ObserveHandler at 0x7efe05c4e9d0>]},
     'index': {'change': [<traitlets.traitlets.ObserveHandler at 0x7efe05bc4090>]},
     'label': {'change': [<traitlets.traitlets.ObserveHandler at 0x7efe05bc4290>]},
     'options': {'change': [<traitlets.traitlets.ObserveHandler at 0x7efe05c3ef50>]},
     'value': {'change': [<traitlets.traitlets.ObserveHandler at 0x7efe05bc4190>,
       <function __main__.funct_f1>,
       <function __main__.funct_f2>]}}
    

    So putting it all together

    # Check if present
    if funct_f1 in widget_name._trait_notifiers['value']['change']:
      # Remove
      wordSelector.unobserve(handler=funct_f1, names='value', type='change')
      print("Removed handler")
    # Attach
    widget_name.observe(handler=funct_f1, names='value', type='change')
    print("Attached handler")