Why is my ipywidget observe being call multiple times on a single state change?

Question:

I have some code in a cell in Jupyter notebook using both a radio button and a slider. I have a method which I want called only when the selection is changed (in the case of the radio button); and only when the slider is released (in the case of the slider).

However, using the ‘observe’ method is triggering multiple times when a radio button is only changed once (it fires 3 times I believe). And the slider observe method fires when the mouse-down and mouse-up occurs.

Can this be changed so that it only is called once or do I need to use something other than observe?

[EDIT] Here is updated example using a radio button along with the output printed when an option is selected once:

import ipywidgets as widgets

    def radio_called(sender):
        print('radio_called')
        print(sender)

    radio = widgets.RadioButtons(options=['option 1', 'option2', 'option3'])
    radio.observe(radio_called)
    display(radio)

Printed output when an option is clicked once:
radio_called

{'name': '_property_lock', 'old': traitlets.Undefined, 'new': {'index': 1}, 'owner': RadioButtons(options=('option 1', 'option2', 'option3'), value='option 1'), 'type': 'change'}
radio_called
{'name': 'label', 'old': 'option 1', 'new': 'option2', 'owner': RadioButtons(index=1, options=('option 1', 'option2', 'option3'), value='option 1'), 'type': 'change'}
radio_called
{'name': 'value', 'old': 'option 1', 'new': 'option2', 'owner': RadioButtons(index=1, options=('option 1', 'option2', 'option3'), value='option2'), 'type': 'change'}
radio_called
{'name': 'index', 'old': 0, 'new': 1, 'owner': RadioButtons(index=1, options=('option 1', 'option2', 'option3'), value='option2'), 'type': 'change'}
radio_called
{'name': '_property_lock', 'old': {'index': 1}, 'new': {}, 'owner': RadioButtons(index=1, options=('option 1', 'option2', 'option3'), value='option2'), 'type': 'change'}
Asked By: laurie

||

Answers:

If you print the sender object you can see what is being passed to the function. Each instance is a different Trait changing (there is more than just a single action happening when you click), try the code below.

If you want to filter to only happen once, in your observe call specify the names you want. E.g.

radio_input.observe(bind_selected_to_output, names=['value'])

    import ipywidgets as widgets # If not already imported

    output_radio_selected = widgets.Text() # Used to take the user input and access it when needed
    radio_input = widgets.RadioButtons(options=['Option 1', 'Option 2']) # Declare the set of radio buttons and provide options

    def bind_selected_to_output(sender): # Connect the input from the user to the output so we can access it
        print(sender)
        global selected_option # Global variable to hold the user input for reuse in your code
        output_radio_selected.value = radio_input.value
        selected_option = output_radio_selected.value # Example variable assigned the selected value
        print('Selected option set to: ' + selected_option) # For test purposes

    radio_input.observe(bind_selected_to_output, names=['value']) # Run the bind... function when the radio button is changed
    radio_input # Display the radio buttons to the user

See here for more info: https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html#Traitlet-events

Answered By: ac24

Here’s an updated version of ac24’s code so that the output is handled correctly and doesn’t go to the Log console when run in JupyterLab:

import ipywidgets as widgets # If not already imported

OUT = widgets.Output()

output_radio_selected = widgets.Text() # Used to take the user input and access it when needed
radio_input = widgets.RadioButtons(options=['Option 1', 'Option 2']) # Declare the set of radio buttons and provide options

def bind_selected_to_output(sender): # Connect the input from the user to the output so we can access it
    OUT.clear_output()
    with OUT:
        print(sender)
    global selected_option # Global variable to hold the user input for reuse in your code
    output_radio_selected.value = radio_input.value
    selected_option = output_radio_selected.value # Example variable assigned the selected value
    with OUT:
        print('Selected option set to: ' + selected_option) # For test purposes

radio_input.observe(bind_selected_to_output, names=['value']) # Run the bind... function when the radio button is changed
#radio_input # Display the radio buttons to the user
display(radio_input,OUT)
Answered By: Wayne