Ondrej
Ondrej

Reputation: 502

Dynamically changing dropdowns in IPython notebook widgets and Spyre

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.

Upvotes: 23

Views: 26467

Answers (5)

Rub
Rub

Reputation: 2708

I took Fei Yao solution replacing

cityW.observe(update_cityW_options)

for

countryW.observe(update_cityW_options)

Because the cities displayed should depend on the country (I believe).

Also replaced Dropdown for Select (less clicking needed).

from ipywidgets import interact, Dropdown, Select

geo = {'USA':['CHI','NYC'],'Russia':['MOW','LED']}
countryW = Select(options = geo.keys())
cityW = Select()

def update_cityW_options(*args): # *args represent zero (case here) or more arguments.
    cityW.options = geo[countryW.value]

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

And it looks like this.

enter image description here

The part below #--- is unnecessary unless you want to be printing the selected combination (the original question doesn't ask for it).

If you remove it, just add

# display the widgets
countryW
cityW 

Upvotes: 0

Qaswed
Qaswed

Reputation: 3879

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

Upvotes: 2

Fei Yao
Fei Yao

Reputation: 1742

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)

Upvotes: 11

Bhavana Manchana
Bhavana Manchana

Reputation: 41

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)

Upvotes: 4

ILoveCoding
ILoveCoding

Reputation: 916

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)

Upvotes: 26

Related Questions