Ipywidgets: Update One Widget Based On Results From Another
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"