Dynamically changing dropdowns in IPython notebook widgets and Spyre
Solution 1
Use interactive
instead of interact
and update your widget:
from IPython.html import widgets
from IPython.display import display
geo={'USA':['CHI','NYC'],'Russia':['MOW','LED']}
def print_city(city):
print city
def select_city(country):
cityW.options = geo[country]
scW = widgets.Select(options=geo.keys())
init = scW.value
cityW = widgets.Select(options=geo[init])
j = widgets.interactive(print_city, city=cityW)
i = widgets.interactive(select_city, country=scW)
display(i)
display(j)
Solution 2
The answer with the most up-votes is useful but seems a bit clumsy for me. After searching for a while, I found the answer here based on Jupyter docs is more preferred for me. I adapted them and provide the following.
from ipywidgets import interact, Dropdown
geo = {'USA':['CHI','NYC'],'Russia':['MOW','LED']}
countryW = Dropdown(options = geo.keys())
cityW = Dropdown()
def update_cityW_options(*args): # *args represent zero (case here) or more arguments.
cityW.options = geo[countryW.value]
cityW.observe(update_cityW_options) # Here is the trick, i.e. update cityW.options based on countryW.value.
@interact(country = countryW, city = cityW)
def print_city(country, city):
print(country, city)
As an alternative, I also found that I can just update the cityW.options
within print_city
function, an even clearer practice!
from ipywidgets import interact, Dropdown
geo = {'USA':['CHI','NYC'],'Russia':['MOW','LED']}
countryW = Dropdown(options = geo.keys())
cityW = Dropdown()
@interact(country = countryW, city = cityW)
def print_city(country, city):
cityW.options = geo[country] # Here is the trick, i.e. update cityW.options based on country, namely countryW.value.
print(country, city)
Solution 3
import datetime
import ipywidgets as ipyw
from bokeh.models.widgets.inputs import AutocompleteInput
from IPython.display import display
dp1 = ipyw.Dropdown(options = ['Asia','Europe','Africa'])
dp2 = ipyw.Dropdown(options = ['India','China','Pakistan','Tibet'])
asia_list = ['India','China','Pakistan','Tibet']
europe_list = ['Germany','France','Italy']
africa_list = ['south africa','Nigeria','Kenya']
global_vbox = ipyw.VBox()
global_vbox.children = [dp1,dp2]
display(global_vbox)
def continent_change_event(x):
global dp1
global dp2
list_name = dp1.value
dp2.index = None #This line is very important for setting the values for widgets other than widget with observe method
dp2.index = 0
if(list_name == 'Asia'):
dp2.options = asia_list
elif(list_name == 'Europe'):
dp2.options = europe_list
else:
dp2.options = africa_list
dp1.observe(continent_change_event)
Solution 4
For those working with widgets.interactive
: update the options of the dynamic widget in your main function:
import ipywidgets as widgets
geo={'USA':['CHI','NYC'],'Russia':['MOW','LED']}
def main_function(city, country):
print (f'{city} is a city in {country}')
cityW.options = geo[country]
scW = widgets.Select(options=geo.keys())
cityW = widgets.Select(options=geo[init])
widgets.interactive(main_function, city=cityW, country=scW)
Note: this only differs from the top rated answer by having only one interactive function instead of two. Bonus: if you want to pass fix parameters to your main function, use name=widgets.fixed("Bob")
(see here).
Ondrej
Updated on July 27, 2022Comments
-
Ondrej almost 2 years
I have a dropdown in an IPython notebook (as part of the HTML widgets) and in a Spyre app (as a
dropdown
element), say to pick a continent and I'd like to add a second dropdown to select the country within the continent. Now obviously the options within the second dropdown are dependent on the value of the first one. I'm struggling to find a convenient way to have a callback function that would update this UI element.I have almost done this in the IPython notebook where I had one
interact
function and within the called function, I'd create a secondinteract
element with the second dropdown. But whenever I'd change the first dropdown a new dropdown element would be created, so I'd end up with one additional dropdown with each change. But I only want one dropdown to be updated, that's all.Hope the issue is clear. Thank you.
-
Kamil Sindi over 8 yearsNote:
IPython.html.widgets
has moved toipywidgets
.import ipywidgets as widgets
-
ILoveCoding over 3 yearsThis works well indeed if your 2 functions are quick to execute. If deriving the cityW options based on the country was taking a long time, it would be a waste to do it on every city change, not just on country changes. You need to update the city options before doing the work (print) otherwise you can print MOW is a city in USA...
-
ILoveCoding over 3 yearsThat seems to be a good solution indeed, except if your intermediary work is not trivial (then it's a bit wastefull to recompute the city options when just the city changes). But yeah that's probably fine for many use cases.
-
Andy over 2 yearsSecond approach is really neat!