Skip to content Skip to sidebar Skip to footer

Ipywidgets: Update One Widget Based On Results From Another

I am using widgets in IPython that allows the user to repeatedly search for a phrase and see the results (different titles) in another widget (a selection widget) and then select o

Solution 1:

You can hold notifications for during the assignment to options:

with search_result.hold_trait_notifications():
    search_result.options = titles

Thus:

search_text = widgets.Text(description = 'Search') 
search_result = widgets.Select(description = 'Select table')

defsearch_action(sender):
    phrase = search_text.value
    df = search(phrase) # A function that returns the results in a pandas df
    titles = df['title'].tolist()
    with search_result.hold_trait_notifications():
        search_result.options = titles

See hmelberg's explanation below

"The root of the error is that the widget also has a value property and the value may not be in the new list of options. Because of this, widget value may be "orphaned" for a short time and an error is produced."

Solution 2:

I encountered this exact problem an hour ago. I have hacked together a solution using the minimum example here: Dynamically changing dropdowns in IPython notebook widgets and Spyre, since my own requirements were to have dynamically linked lists. I am sure you'll be able to adapt your requirements using this solution.

The key is to pre-generate all Dropdowns/Select. For some reason, w.options = l only sets w._options_labels but not w.options. Subsequent validation of the selected value of w will then fail horribly.

import ipywidgets as widgets
from IPython.display import display

geo={'USA':['CHI','NYC'],'Russia':['MOW','LED']}
geoWs = {key: widgets.Select(options=geo[key]) for key in geo}

defget_current_state():
    return {'country': i.children[0].value,
            'city': i.children[1].value}

defprint_city(**func_kwargs):
    print('func_kwargs', func_kwargs)
    print('i.kwargs', i.kwargs)
    print('get_current_state', get_current_state())

defselect_country(country):
    new_i = widgets.interactive(print_city, country=countryW, city=geoWs[country['new']])
    i.children = new_i.children

countryW = widgets.Select(options=list(geo.keys()))
init = countryW.value
cityW = geoWs[init]

countryW.observe(select_country, 'value')

i = widgets.interactive(print_city, country=countryW, city=cityW)

display(i)

Note lastly that it is not trivial to obtain the most up-to-date state of the widgets. These are

  • directly from the children's values, via get_current_state. This can be trusted.
  • from the interactive instance, via i.kwargs
  • from the supplied args to print_city

The latter two can sometimes be out of date, for various reasons I don't wish to find out further.

Hope this helps.

Solution 3:

I had a similar problem and solved it Registering callbacks to trait changes in the kernel

caption = widgets.Label(value='The values of range1 and range2 are synchronized')
slider = widgets.IntSlider(min=-5, max=5, value=1, description='Slider')

defhandle_slider_change(change):
    caption.value = 'The slider value is ' + (
        'negative'if change.new < 0else'nonnegative'
    )

slider.observe(handle_slider_change, names='value')

display(caption, slider)

I guess this solution wasn't available back in 2016 or my problem wasn't as similar as thought.

Solution 4:

The root of the error is that the widget also has a value property and the value may not be in the new list of options. Because of this, widget value may be "orphaned" for a short time and an error is produced.

The solution is either to assign the widget value to the option list before assigning it to the widget (and remove the value/option after if desired), or as Dan writes: use create a hold_trait-notifications()

Dan's approach is the best. The above just explains the cause of the problem.

Post a Comment for "Ipywidgets: Update One Widget Based On Results From Another"