Callback not responding to value

I have a dataframe and a template of bar plots I would like as below:

df= pd.DataFrame({
    'A':[12,5,8],
    'B':[18,12,8],
    'C':[6,18,12],
    'D':[4,5,2],
    'Year':['2000', '2005', '2010']
})

source = ColumnDataSource(df)

p1=figure(x_range=['2000'],x_axis_label ='2000', y_axis_label = 'Bar Plot')
p1.vbar(x=dodge('Year', -0.3, range=p1.x_range), top='A', source=source,  width=0.19, color="#c9d9d3", legend_label="A")
p1.vbar(x=dodge('Year',  -0.1,  range=p1.x_range), top='B', source=source,  width=0.19, color="#718dbf", legend_label="B")
p1.vbar(x=dodge('Year',  0.1, range=p1.x_range), top='C', source=source,  width=0.19, color="#e84d60", legend_label="C")
p1.vbar(x=dodge('Year',  0.3, range=p1.x_range), top='D', source=source,  width=0.19, color="#555555", legend_label="D")      
p1.x_range.range_padding = 0.1
p1.xgrid.grid_line_color = None
p1.legend.orientation = "horizontal"

I would like the menu items plots change each time. But it doesnt seem to be working:

sel_src = ColumnDataSource(data={'Year':[]})

callback = CustomJS(args=dict(source=source, sel_src=sel_src), code="""
var f = cb_obj.value

sel_src.data['Year']=[]
sel_src.data['A']=[]
sel_src.data['B']=[]
sel_src.data['C']=[]
sel_src.data['D']=[]
for(var i = 0; i <= source.get_length(); i++){
    
        if (f==source.data['Year'][i]){
            sel_src.data['A'].push(source.data['A'][i]);
            sel_src.data['B'].push(source.data['B'][i]);
            sel_src.data['C'].push(source.data['C'][i]);
            sel_src.data['D'].push(source.data['D'][i]);
            sel_src.data['Year'].push(source.data['Year'][i])
     }
}   

sel_src.change.emit();
""")

menu = Select(options=list(df['Year']),value='2000', title = 'Year')  # drop down menu
menu.js_on_change('value', callback) # calling the function on change of selection
layout=column(menu,p1)
show(layout) 

How do i make the individual values (each year) respond to the callback so that there are 3 different plots for 3 years in the menu?

@Tranquil_Oshan I don’t really understand what you are trying to accomplish from the description above. Please break down the exact interaction you want to have have in more detail.

Selecting year should change the plot every time (each year has different columns value). How do I accomplish that ?

By far, the simplest thing to do is to create all the plots up front, and put them all in a column (or whatever) and then have the CustomJS just toggle the .visible attributtes of the plots approprtiately, based on the dropdown value.

Trying to update an existing plot with several glyphs and a legend “in-place” is more work, and more fragile.

Having different plots is ok if we have only few plots. But what if sometimes we have , say, 20 plots ?

It depends, on how much data is in each plot and/or whether you can share the same data source between the different plots (ideally yes). The simplest thing to do is just give it a try and see if it works for your situation. If not, then next thing might be to ad all the glyphs up front to a single plot, and use the CustomJS to toggle the visibility of the glyphs. But you will probably need to manage the legend in manually in that case (but that’s also the case with your original approach).

Kinda an aside but setup for legend updating could be vastly simplified if this feature ever became a thing: [FEATURE] GlyphLegendItem for arbitrary legend items · Issue #10776 · bokeh/bokeh · GitHub

You could just build a dictionary with a key for each dropdown item, and store the datasource, the plot renderers and the legend renders in each dict entry, e.g.

d = {'A': {'src':ColumnDataSource(),'rend':p.line(...), 'leg_entries':[GlyphLegendItem(..)] }}

Then just pass d into CustomJS and update the pieces accordingly based on dropdown item active change. This whole setup works really nicely now, except for the legend piece.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.