Search code examples
pythonjupyter-notebookipywidgets

How to add independent titles or headers to dropdown menus created using ipywidgets?


I am using the following code to produce a tool in a Jupyter Notebook that allows the user to print a statement describing which coloured fruit they would like to try:

import ipywidgets as widgets
from ipywidgets import interactive

fruits = ['Banana', 'Apple','Lemon','Orange']
colors = ['Blue', 'Red', 'Yellow']

drop1 = widgets.Dropdown(options=fruits, value='Banana', description='Fruit:', disabled=False)
drop2 = widgets.Dropdown(options=colors, value='Blue', description='Color:', disabled=False)


def update_dropdown(fruit, color):
    info = f"I would love to try a {color.lower()} {fruit.lower()}!"
    display(info)  

w = interactive(update_dropdown, fruit=drop1, color=drop2) 
display(w)

The code comes from the answer to this Stack Overflow question.

When the user chooses a fruit and/or a color from the dropdown menus, an associated print statement should also be printed, reading "I would love to try a {color} {fruit}!". The following image, produced by the code above, show how the partial, expected output looks like in Jupyter Notebook:

enter image description here

However, I am trying to display "Choose a fruit!" right above the fruit dropdown menu and display "Choose a color right above the color dropdown menu, as so: enter image description here

When I try to insert these print statements using the following code:

...
drop1 = ...
print("Hey")

drop2 = ...
print("Hello")

I see this, which is not what I want:

enter image description here

How can I modify the .interactive() function line to insert the print statements I am trying to show?


Solution

  • We get some idea of how to solve this problem from the last example of The Grid Layout section: see output of code block #16.

    Thus, using GridBox, we might be able to properly layout dropdown menus with their titles.

    To create titles, we use widgets.HTML - widgets with no values but plain descriptions. Combining these two concepts, we get:

    import ipywidgets as widgets
    from ipywidgets import interactive, GridBox, Layout, VBox
    
    fruits = ['Banana', 'Apple','Lemon','Orange']
    colors = ['Blue', 'Red', 'Yellow']
    
    header1 = widgets.HTML(description="Choose a fruit:", value="",)
    header2 = widgets.HTML(description="Choose a color:", value="",)
    
    drop1 = widgets.Dropdown(options=fruits, value='Banana', description='Fruit:', disabled=False)
    drop2 = widgets.Dropdown(options=colors, value='Blue', description='Color:', disabled=False)
    
    def update_dropdown(header1, header2, fruit, color):
        info = f"I would love to try a {color.lower()} {fruit.lower()}!"
        display(info)  
    
    w = interactive(update_dropdown, header1=header1, fruit=drop1, header2=header2, color=drop2) 
    controls = GridBox(children=[header1, drop1, header2, drop2],
            layout=Layout(
                grid_template_rows='auto auto auto auto',
                grid_template_areas='''
                "header1"
                "drop1"
                "header2"
                "drop2"
                ''')
           )
    display(controls)
    

    This realises dropdown menus with respective titles, but doesn't create a widget which is interactive, displays output and shows the entire title of a dropdown menu.

    To do this, we use ac24's answer for managing interactiveness and aod's answer for displaying full titles. Finally, we have:

    import ipywidgets as widgets
    from ipywidgets import interactive, GridBox, Layout, VBox
    
    fruits = ['Banana', 'Apple','Lemon','Orange']
    colors = ['Blue', 'Red', 'Yellow']
    header1  = widgets.HTML(
        description="Choose a fruit:",
        value="",
        style= {'description_width': 'initial'}
    )
    header2 = widgets.HTML(
        description="Choose a color:",
        value='',
        style= {'description_width': 'initial'}
    )
    drop1 = widgets.Dropdown(options=fruits, value='Banana', description='Fruit:', disabled=False)
    drop2 = widgets.Dropdown(options=colors, value='Blue', description='Color:', disabled=False)
    
    def update_dropdown(header1, header2, fruit, color):
        info = f"I would love to try a {color.lower()} {fruit.lower()}!"
        display(info)  
    
    w = interactive(update_dropdown, header1=header1, fruit=drop1, header2=header2, color=drop2) 
    controls = GridBox(children=[header1, drop1, header2, drop2],
            layout=Layout(
                grid_template_rows='auto auto auto auto',
                grid_template_areas='''
                "header1"
                "drop1"
                "header2"
                "drop2"
                ''')
           )
    output = w.children[-1]
    display(VBox([controls, output]))
    

    This gives a widget that displays which colored fruit the user wants to try after user's first interaction with the widget:

    enter image description here