Search code examples
pythonjupyter-notebookgoogle-colaboratoryipywidgets

How to create a dynamic dependent dropdown menu using ipywidgets?


I created a form with two drop down menu's:

My goal is to make one dropdown dependent on the other dropdown.

This picture illustrates my goal and the current situation.

The sample code below can be run in Google Colab or Jupyter notebook to replicate the current situation.

#@title 
import ipywidgets as widgets
from ipywidgets import HBox, Label
from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, IntSlider
import time
import pandas as pd

#Create DF
df = df = pd.DataFrame(columns = ['Dropdown_column', 'Float_column'])
df

# Layout
form_item_layout = Layout(
    display='flex',
    flex_flow='row',
    justify_content='space-between',
)


button_item_layout = Layout(
    display='flex',
    flex_flow='row',
    justify_content='center',
    padding = '5%'
)


# Independent dropdown item

drop_down_input = 'Dropdown_input_1'

drop_down = widgets.Dropdown(options=('Dropdown_input_1', 'Dropdown_input_2'))

def dropdown_handler(change):
    global drop_down_input
    print('\r','Dropdown: ' + str(change.new),end='')
    drop_down_input = change.new

drop_down.observe(dropdown_handler, names='value')


# Dependent drop down

# Dependent drop down elements

dependent_drop_down_elements = {}
dependent_drop_down_elements['Dropdown_input_1'] = ['A', 'B']
dependent_drop_down_elements['Dropdown_input_2'] = ['C', 'D', 'E'] 

# Define dependent drop down

dependent_drop_down = widgets.Dropdown(options=(dependent_drop_down_elements['Dropdown_input_1']))

def dropdown_handler(change):
    global drop_down_input
    print('\r','Dropdown: ' + str(change.new),end='')
    drop_down_input = change.new  
drop_down.observe(dropdown_handler, names='value')



# Button

button = widgets.Button(description='Add row to dataframe')
out = widgets.Output()
def on_button_clicked(b):
    global df
    button.description = 'Row added'
    time.sleep(1)
    with out:
      new_row = {'Dropdown_column': drop_down_input, 'Float_column': float_input}
      df = df.append(new_row, ignore_index=True)
      button.description = 'Add row to dataframe'
      out.clear_output()  
      display(df)
button.on_click(on_button_clicked)

# Form items

form_items = [         
    Box([Label(value='Independent dropdown'),
         drop_down], layout=form_item_layout),
    Box([Label(value='Dependent dropdown'),
         dependent_drop_down], layout=form_item_layout)
         ]

form = Box(form_items, layout=Layout(
    display='flex',
    flex_flow='column',
    border='solid 1px',
    align_items='stretch',
    width='30%',
    padding = '1%'
))
display(form)
display(out)

Is this possible? If so, can you explain how?


Solution

  • You can do this by setting a if statement in your dropdown_handler function that checks if the value chosen in your independent dropdown is Dropdown_input_1 or Dropdown_input2. Still in the dropdown_handler, you can then modify accordingly the options arguments to either dependent_drop_down_elements['Dropdown_input_1'] or dependent_drop_down_elements['Dropdown_input_2'].

    See code below:

    import ipywidgets as widgets
    from ipywidgets import HBox, Label
    from ipywidgets import Layout, Button, Box, FloatText, Textarea, Dropdown, Label, IntSlider
    import time
    import pandas as pd
    
    #Create DF
    df = df = pd.DataFrame(columns = ['Dropdown_column', 'Float_column'])
    df
    
    # Layout
    form_item_layout = Layout(
        display='flex',
        flex_flow='row',
        justify_content='space-between',
    )
    
    
    button_item_layout = Layout(
        display='flex',
        flex_flow='row',
        justify_content='center',
        padding = '5%'
    )
    
    
    # Independent dropdown item
    
    drop_down_input = 'Dropdown_input_1'
    drop_down = widgets.Dropdown(options=('Dropdown_input_1', 'Dropdown_input_2'))
    
    
    # Dependent drop down
    # Dependent drop down elements
    dependent_drop_down_elements = {}
    dependent_drop_down_elements['Dropdown_input_1'] = ['A', 'B']
    dependent_drop_down_elements['Dropdown_input_2'] = ['C', 'D', 'E'] 
    
    # Define dependent drop down
    
    dependent_drop_down = widgets.Dropdown(options=(dependent_drop_down_elements['Dropdown_input_1']))
    
    def dropdown_handler(change):
        global drop_down_input
        print('\r','Dropdown: ' + str(change.new),end='')
        drop_down_input = change.new
        #If statement checking on dropdown value and changing options of the dependent dropdown accordingly
        if change.new=='Dropdown_input_2':
           dependent_drop_down.options=dependent_drop_down_elements['Dropdown_input_2']
        elif change.new=='Dropdown_input_1':
            dependent_drop_down.options=dependent_drop_down_elements['Dropdown_input_1']
    drop_down.observe(dropdown_handler, names='value')
    
    # Button
    
    button = widgets.Button(description='Add row to dataframe')
    out = widgets.Output()
    def on_button_clicked(b):
        global df
        button.description = 'Row added'
        time.sleep(1)
        with out:
          new_row = {'Dropdown_column': drop_down_input, 'Float_column': float_input}
          df = df.append(new_row, ignore_index=True)
          button.description = 'Add row to dataframe'
          out.clear_output()  
          display(df)
    button.on_click(on_button_clicked)
    
    # Form items
    
    form_items = [         
        Box([Label(value='Independent dropdown'),
             drop_down], layout=form_item_layout),
        Box([Label(value='Dependent dropdown'),
             dependent_drop_down], layout=form_item_layout)
             ]
    
    form = Box(form_items, layout=Layout(
        display='flex',
        flex_flow='column',
        border='solid 1px',
        align_items='stretch',
        width='30%',
        padding = '1%'
    ))
    display(form)
    display(out)
    

    As an example, this is what the output looks like when you pick Dropdown_input_2 on the first widget:

    enter image description here