Legend_field labels no longer update with CDS in Bokeh 3

Hi community,

Now that Panel supports Bokeh 3 :grinning:, I’m excited to transition some working code from Bokeh 2.4.3 to 3.1.1, but I seem to have hit an issue with the legend_field feature. I have a plot whose CDS gets updated dynamically from a database, and in that process, entries in the legend_field column may change. In Bokeh 2.4.3, when this happened, the Legend smartly updated itself with the current entries from the legend_field column. In Bokeh 3.1.1, the Legend labels no longer do.

So I’ve copied the Legend Field tutorial from the Bokeh documentation to make a minimal example.

As you’ll see, I’ve just added a radio group next to the plot which, when clicked, updates the CDS. The minimal code is below. I’ve run this code in both a Bokeh 2.4.3 environment and a Bokeh 3.1.1 environment, and it outputs an html file with the dynamic plot. Below I’ve attached a slide of screenshots showing how, in 2.4.3, the Legend updates when the data changes, but in 3.1.1, you see it does not.

Does anyone know why this could be, and if it’s a temporary bug or a permanent change?

Thanks for your help,
Sam

import bokeh
import numpy as np
from bokeh.io import show
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.plotting import figure, save
from bokeh.models.widgets import RadioGroup

orange, blue = '#ef8a62', '#67a9cf'

all_data=dict(
    x=[1, 2, 3, 4, 5, 6],
    y=[2, 1, 2, 1, 2, 1],
    color=[orange, blue, orange, blue, orange, blue],
    label=['hi', 'lo', 'hi', 'lo', 'hi', 'lo'],
)

# Make some subsets of this data and collect into a dict
data_hi={k:np.array(v)[np.array(all_data['label'])=='hi'] for k,v in all_data.items()}
data_lo={k:np.array(v)[np.array(all_data['label'])=='lo'] for k,v in all_data.items()}
datas={'all':all_data,'hi':data_hi,'lo':data_lo}

# We'll start with the "hi" subset
source = ColumnDataSource(data_hi)

# Create the plot using the 'label' as the legend_field
p = figure(x_range=(0, 7), y_range=(0, 3), height=300, toolbar_location=None)
p.circle('x', 'y', radius=0.5, color='color',
         legend_field='label', source=source)

# When a radio element is clicked, change source.data to the corresponding entry in datas
# Slight js tweak here between v2.4.3 and v3.1.1 based on how the serialization works I think(?)
rg = RadioGroup(labels=list(datas.keys()),active=1)
rg.js_on_change('active',CustomJS(args=dict(datas=datas,order=dict(enumerate(datas.keys())),source=source),
        code="source.data=datas[order[cb_obj.active]];" if bokeh.__version__.startswith('2') else \
             "source.data=datas[order.get(cb_obj.active)];"))

# Save to file
print("Bokeh Version: ",bokeh.__version__)
save(bokeh.layouts.row([p,rg]),
    title=f'LegendFieldRegression_Bokeh{bokeh.__version__}',
    filename=f'LegendFieldRegression_Bokeh{bokeh.__version__}.html')

I’d suggest making a GitHub Issue with full details.

Thanks Bryan, will do immediately!

Edit: And here it is.

1 Like

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