Dynamically changing dropdowns in IPython notebook widgets and Spyre

18,668

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).

Share:
18,668
Ondrej
Author by

Ondrej

Updated on July 27, 2022

Comments

  • Ondrej
    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 second interact 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
    Kamil Sindi over 8 years
    Note: IPython.html.widgets has moved to ipywidgets. import ipywidgets as widgets
  • ILoveCoding
    ILoveCoding over 3 years
    This 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
    ILoveCoding over 3 years
    That 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
    Andy over 2 years
    Second approach is really neat!