Hello!
I made vbar selection via MultiSelect tool and need to optimize legend work. I don’t use bokeh server. Form data with Django and customize with CustomJS.
I need to display only selected objects (cellnames in my case) in legend. Other objects should fully disappear from legend.
I want to control selection only with MultiSelect. Clicking on legend elements should do nothing.
I attached interface i’m talking about.
One solution would be to recreate legend items on each change:
import pandas as pd
from bokeh.layouts import row
import bokeh.sampledata.stocks as stocks
from bokeh.models import MultiSelect, CustomJS
from bokeh.palettes import Spectral4
from bokeh.plotting import figure, show
p = figure(plot_width=800, plot_height=250, x_axis_type="datetime")
p.title.text = 'Click on legend entries to hide the corresponding lines'
names = ["AAPL", "IBM", "MSFT", "GOOG"]
for name, color in zip(names, Spectral4):
data = getattr(stocks, name)
df = pd.DataFrame(data)
df['date'] = pd.to_datetime(df['date'])
p.line(df['date'], df['close'], line_width=2, color=color, alpha=0.8, legend_label=name, name=name)
# `p.legend` is a splattered list. It's fine to use it to set
# attributes in Python but it's not as convenient on the JS side.
legend = p.legend[0]
legend.location = "top_left"
ms = MultiSelect(options=names, value=names)
ms.js_on_change('value', CustomJS(args=dict(legend=legend),
code="""\
const {LegendItem} = Bokeh.require('models/annotations/legend_item');
legend.items = cb_obj.value.map((v) => {
return new LegendItem({label: {value: v},
renderers: [cb_obj.document.get_model_by_name(v)]});
});
"""))
show(row(p, ms))
Just a side note: if there’s not enough items in your MultiSelect to create a scroll bar, maybe it makes sense to not use the MultiSelect at all and just use the legend instead.