pythondrop-down-menuwidget

Dropdown widget Python


Please bear with me. First post for me here. I am using Python, first programming language for me, for about two weeks. Now I am stuck..

I want to create a single graph with a dropdown widget. On the x-axis I want the selected year and on the y-axis I want the respective number for that year. So the year 2009 on the x-axis and 130 on the y-axis and so on.

I tried different things, basically playing around for three hours..

Maybe someone can provide me a solution? Thanks!

%matplotlib inline

import ipywidgets as widgets
from IPython.display import display
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_style("darkgrid")

dropdown = widgets.Dropdown(
    options=['2009', '2010', '2011', "2012", "2013", "2014", "2015", "2016", "2017", "2018","2019"],
    value='2009',
    description='Jahr:',
    )

def Dropdown_Menu(b):

    fig, ax = plt.subplots()
    fig.dpi = 500

    x = [2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019]
    y = [130, 137, 104, 147, 401, 274, 234, 770, 857, 746, 704]

    ax.plot(x, y, label = "Flugstunden pro Jahr", marker = ".")


    ax.legend()

    ax.set_title("Flugstunden")
    ax.set_xlabel("Jahr")
    ax.set_ylabel("Flugstunden")
    ax.set_facecolor((0.9,0.9,0.9))



plt.show()

dropdown.observe(Dropdown_Menu, names="value")    
display(dropdown)

Solution

  • dropdown executes Dropdown_Menu with selected value but it will not select values from list x,y. You have to do it.

    For some reasons dropdown.observe() doesn't works for me so I used widgets.interact()

    EDIT: it seems dropdown.observe() works only in juputer notebook but not in jupyter lab (which I use) and it would need widget Output() to work. If you will use dropdown.observe() then in Dropdown_Menu you have to use value = value.new.

    %matplotlib inline
    
    import ipywidgets as widgets
    from IPython.display import display
    import matplotlib.pyplot as plt
    
    dropdown = widgets.Dropdown(
            #options=['2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017', '2018', '2019'],
            #value='2009',
            options=list(range(2009, 2020)), # integers instead of strings
            value=2009,  # integer instead of string
            description='Jahr:',
    )
    
    def Dropdown_Menu(value=2009):
    
        #print(type(value))
        #value = int(value)  # I dont have to convert if `Dropdown` uses integer values
        
        fig, ax = plt.subplots()
        fig.dpi = 150
    
        x = [2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019]
        y = [130, 137, 104, 147, 401, 274, 234, 770, 857, 746, 704]
        
        # get all value for `year >= value`
        #pairs = [(a,b) for a,b in zip(x, y) if a >= value] # use `==` to get only one value
        #selected_x, selected_y = zip(*pairs)
        
        # select values
        pos = x.index(value)
        selected_x = x[pos]
        selected_y = y[pos]
        
        print('x:', selected_x)
        print('y:', selected_y)
        
        ax.plot(selected_x, selected_y, label="Flugstunden pro Jahr", marker=".")
    
        ax.legend()
    
        ax.set_title("Flugstunden")
        ax.set_xlabel("Jahr")
        ax.set_ylabel("Flugstunden")
        ax.set_facecolor((0.9,0.9,0.9))
    
        #plt.show()
        
    widgets.interact(Dropdown_Menu, value=dropdown)
    #dropdown.observe(Dropdown_Menu, names="value")    
    #display(dropdown)
    

    EDIT: similar code with ipympl which gives interactive plot - so it doesn't have to replot all again and again but it can replace only line(s) (remove old line and plot new line) or it can replace only data used in line (without removig line)

    EDIT (2025):

    As @Wayne suggested in comment below: currently can be used %matplotlib ipympl and it explocitly shows that it needs to install ipympl (so it doesn't need comment # needs ipympl in code).

    And as @Wayne said: "%matplotlib widget is a leftover from a legacy time when JupyterLab and Jupyter Noebook didn't use the same things."

    And link to basic example in ipympl doc (also from comment).

    %matplotlib ipympl    # explictly shows that it needs `ipympl`
    
    # %matplotlib widget  # still work as legacy but doesn't show that it needs `ipympl`
    # needs ipympl
    
    import ipywidgets as widgets
    from IPython.display import display
    import matplotlib.pyplot as plt
    
    dropdown = widgets.Dropdown(
            #options=['2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017', '2018', '2019'],
            #value='2009',
            options=list(range(2009, 2020)), # integers instead of strings
            value=2009,  # integer instead of string
            description='Jahr:',
    )
    
    fig, ax = plt.subplots()
    fig.dpi = 150
    
    x = [2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019]
    y = [130, 137, 104, 147, 401, 274, 234, 770, 857, 746, 704]
    
    line, = ax.plot(x, y, label="Flugstunden pro Jahr", marker=".")
    
    ax.legend()
    
    ax.set_title("Flugstunden")
    ax.set_xlabel("Jahr")
    ax.set_ylabel("Flugstunden")
    ax.set_facecolor((0.9,0.9,0.9))
    
    #plt.show()
    
    def on_change1(value=2009):
        """remove old line(s) and plot new line(s)"""
        
        #print(type(value))
        #value = int(value)   # I don't have to convert string to integer
        
        # get all value for `year >= value`
        #pairs = [(a,b) for a,b in zip(x, y) if a >= value] # use `==` to get only one value
        #selected_x, selected_y = zip(*pairs)
    
        # select data 
        pos = x.index(value)
        selected_x = x[pos]  # create `selected_x` to keep original values in `x`
        selected_y = y[pos]  # create `selected_y` to keep original values in `y`
    
        print('x:', selected_x)
        print('y:', selected_y)
    
        # remove old line(s)
        for l in ax.lines:
            l.remove()
            
        # plot new line(s)
        ax.plot(selected_x, selected_y, label="Flugstunden pro Jahr", marker=".")   
        
        
    def on_change2(value=2009):
        """keep line, remove all data from line and use new data with the same line"""
        
        #print(type(value))
        #value = int(value)   # I don't have to convert string to integer
        
        # get all value for `year >= value`
        #pairs = [(a,b) for a,b in zip(x, y) if a >= value] # use `==` to get only one value
        #selected_x, selected_y = zip(*pairs)
    
        # select data 
        pos = x.index(value)
        selected_x = x[pos]  # create `selected_x` to keep original values in `x`
        selected_y = y[pos]  # create `selected_y` to keep original values in `y`
    
        print('x:', selected_x)
        print('y:', selected_y)
    
        line.set_xdata(selected_x)
        line.set_ydata(selected_y)
        #fig.canvas.draw()
        
    widgets.interact(on_change1, value=dropdown) 
    #widgets.interact(on_change2, value=dropdown)