Python: Get single signal from ipywidgets.observe() rather than 3

Tags: , ,



Essentially, I am creating a number of Toggle Buttons using ipywidgets. When the button is clicked, I would like to add an element to a list. If the button is unclicked, the element is to be removed. (I have not gotten to the action yet)

For the Toggle Button, I am using .observe() and find that each time I press a button, I am returned 3 signals. {False, True, True} if clicked and {True, False, False} if unclicked. I think .observe() is running 3 times each time there is a button click. Is there any way to return just one signal or is there an error with my code?

import ipywidgets as widgets
import numpy as np
test = np.array(['test1','test2'])
def buttonArray(button_list):
    switch = [widgets.ToggleButton(description = name, value = False) for name in button_list]
    combined = widgets.HBox(switch)
    display(combined)    

    def upon_clicked(btn):
        signal = btn.owner.value
        print(signal)

    for n in range(len(button_list)):
        switch[n].observe(upon_clicked)

buttonArray(test)

See image for output when a button is pressed:

enter image description here

Answer

If you print(btn) in the observed function you will see that the function is being run three times. You aren’t getting an array of three values, it is a function that produces a single value being run three times:

{'name': '_property_lock', 'old': traitlets.Undefined, 'new': {'value': True}, 'owner': ToggleButton(value=False, description='test1'), 'type': 'change'}

{'name': 'value', 'old': False, 'new': True, 'owner': ToggleButton(value=True, description='test1'), 'type': 'change'}

{'name': '_property_lock', 'old': {'value': True}, 'new': {}, 'owner': ToggleButton(value=True, description='test1'), 'type': 'change'}

The _property_lock attribute is being changed twice, and the the value attribute once in the middle, hence the three function calls.

In most cases, you probably just want the middle set data. To achieve this, you need to state the name values that get passed to the observed function, in this case names=['value']:

import ipywidgets as widgets
import numpy as np
test = np.array(['test1','test2'])
def buttonArray(button_list):
    switch = [widgets.ToggleButton(description = name, value = False) for name in button_list]
    combined = widgets.HBox(switch)
    display(combined)    

    def upon_clicked(btn):
        print(btn)
        signal = btn.owner.value
#         print(signal)

    for n in range(len(button_list)):
        switch[n].observe(upon_clicked, names=['value'])  # CHANGED HERE

buttonArray(test)


Source: stackoverflow