1

I have simple df have main category and its sub items. I made two select boxes to select main category and it automatically shows belonged items at the other select box using 'observe' But it works only at the first time category selection and after I selected another category it didn't show sub items in an another select box.

If i put 'observe' inside the 'def' the function is worked as I wanted. But this approach is troublesome if I have many other 'def' executing on observe on change. I am also looking for the way to make this code simple without using global. And also welcome any ideas to make this code simpler and lighter.

import pandas as pd
import ipywidgets as wg
from ipywidgets import *
from IPython.display import display, clear_output
df_list=[{'Category':'fruit','name':['banana','apple','mango']},\
         {'Category':'animal','name':['lion','monkey','tiger','cat','dog']},\
         {'Category':'body','name':['eyes','hands','ears','arms']},\
         {'Category':'office','name':['table','computer','printer']}]
df=pd.DataFrame(df_list)

Category_box=wg.Select(options=list(df.Category),continuous_update=False,layout=Layout(width='30%'))
name_box=wg.Select(options=list(df[df['Category']==Category_box.value].name)[0],continuous_update=False,\
                   layout=Layout(width='30%'))
hbox=wg.HBox(children=[Category_box,name_box])
display(hbox)

def select_on_change(change):
    if change['name'] == 'value':
        global Category_box
        global name_box
        global hbox
        Category_box=wg.Select(options=list(df.Category),continuous_update=False,layout=Layout(width='30%'),value=Category_box.value)
        name_box=wg.Select(options=list(df[df['Category']==Category_box.value].name)[0],continuous_update=False,\
                   layout=Layout(width='30%'))
        hbox=wg.HBox(children=[Category_box,name_box])
        clear_output()
        display(hbox)
    #Category_box.observe(select_on_change)


Category_box.observe(select_on_change)

1 Answers1

1

Firstly, thanks for the complete and clear example.

The solution is to not create new widgets in your function that you are observing. Instead just update the .options of your name_box widget. This way, no globals required.

import pandas as pd
import ipywidgets as wg
from ipywidgets import *
from IPython.display import display, clear_output
df_list=[{'Category':'fruit','name':['banana','apple','mango']},\
         {'Category':'animal','name':['lion','monkey','tiger','cat','dog']},\
         {'Category':'body','name':['eyes','hands','ears','arms']},\
         {'Category':'office','name':['table','computer','printer']}]
df=pd.DataFrame(df_list)

Category_box=wg.Select(options=list(df.Category),continuous_update=False,layout=Layout(width='30%'))
name_box=wg.Select(options=list(df[df['Category']==Category_box.value].name)[0],continuous_update=False,\
                   layout=Layout(width='30%'))
hbox=wg.HBox(children=[Category_box,name_box])
display(hbox)

def select_on_change(change):
    if change['name'] == 'value':
        name_box.options = list(df[df['Category']==Category_box.value].name)[0]


Category_box.observe(select_on_change)
ac24
  • 5,325
  • 1
  • 16
  • 31