How to add one legend that controlls multiple bokeh figures?

Question:

how can I create one legend to control multiple bokeh figures? Or how can I customize an exported html file created with bokeh to add legend with similar functionality?

Here is the scenario. I create an html file with 4 different figures. Each figure has a legend with labels/names for individual lines that are shown in the particular figure. Each of the four legend is clickable to toggle the lines separately in each figure.

Even though each of the four figures has one legend, the lines are related, so they each line describes one thing.

I now want to create a legend for all figures combined in one, to toggle each line in all four figures.

Maybe there is a way to add this kind of functionality to the exported html file in some way?

I thought someone with more experience has an idea how to achieve that.

Thanks in advance!

Kind regards

Asked By: sehan2

||

Answers:

Legends are not (yet?) ‘standalone’ bokeh models, they need to be attached to a figure. For now to have an external legend for multiple figures, and place it wherever in a layout, some workaround is needed.

I typically do it like below, with an ‘invisible’ figure that holds the shared legend. You then have to define the legend items manually and assign to each their label and list of renderers.

from bokeh.io import show
from bokeh.plotting import figure
from bokeh.models import LegendItem, Legend
from numpy.random import random, choice
from bokeh.layouts import gridplot
from webcolors import html4_names_to_hex

del html4_names_to_hex['white']
palette = list(html4_names_to_hex.keys())

fig_list = [figure(plot_width=300,plot_height=300) for i in range(4)]

renderer_list = []
color_list = []
for fig in fig_list:
    for i in range(5):
        color = choice(palette)
        renderer = fig.line(range(10),random(10),line_width=2,color=color)
        renderer_list += [renderer]
        color_list += [color]

# Lines with the same color will share a same legend item
legend_items = [LegendItem(label=color,renderers=[renderer for renderer in renderer_list if renderer.glyph.line_color==color]) for color in set(color_list)]

## Use a dummy figure for the LEGEND
dum_fig = figure(plot_width=300,plot_height=600,outline_line_alpha=0,toolbar_location=None)
# set the components of the figure invisible
for fig_component in [dum_fig.grid[0],dum_fig.ygrid[0],dum_fig.xaxis[0],dum_fig.yaxis[0]]:
    fig_component.visible = False
# The glyphs referred by the legend need to be present in the figure that holds the legend, so we must add them to the figure renderers
dum_fig.renderers += renderer_list
# set the figure range outside of the range of all glyphs
dum_fig.x_range.end = 1005
dum_fig.x_range.start = 1000
# add the legend
dum_fig.add_layout( Legend(click_policy='hide',location='top_left',border_line_alpha=0,items=legend_items) )

figrid = gridplot(fig_list,ncols=2,toolbar_location='left')
final = gridplot([[figrid,dum_fig]],toolbar_location=None)
show(final)
Answered By: Seb