Remove old legend items on bokeh server when new data is loaded

Hello community-

I have a built a bokeh server where users can select new sources of data and I plot lines for machines across time. I add each line to the figure in a loop and I want interactive legends that will show or hide each machine.

My problem is that when users choose a new source of data, the legend items from the previous plots remain on figure.legend.items

This has the side effect that when the new machine lines are hidden (when their legends are clicked), plot lines from previous data remain on the screen.

I have tried:

legend_item.destroy() on each legend item

figure.legend.items = [ ]
source.data = { }

before loading new data, but this does not actually remove the previous legend items.

Any ideas?

Hi @mateo it’s not really possible to speculate without more context. I’d suggest paring things down to a a very small Minimal Reproducible Example that tries to add/remove glyphs and legends that can actually be run and diagnosed.

Thanks Bryan for responding. I’m putting together a minimal example to show the issue.

Here is an example snippet to show the issue.

Steps:

  1. run bokeh server with this code
  2. press the “get data” button
  3. press the " get data" button again
  4. click on the legends to hide the lines.

If the are lines still showing from the first plotting, then these lines will not be removed hidden from the plot!

import pandas as pd
import numpy as np

from bokeh.models import ColumnDataSource, Button, Legend
from bokeh.plotting import figure
from bokeh.palettes import d3
from bokeh.layouts import layout
from bokeh.io import curdoc

# Global variables

df = None
fig = None

# defs

def get_data():
    global df
    
    df = pd.DataFrame({'p1' : np.random.randint(0, 10, 31),
                       'p2' : np.random.randint(0, 10, 31),
                       'p3' : np.random.randint(0, 10, 31)},
                      index=pd.date_range("2022-01-01", periods=31, freq="D"))
    df.index.name = "Date"


# bokeh specific

button = Button(label="Get data")

source = ColumnDataSource({})

tools = ["box_select","pan","wheel_zoom","box_zoom","reset","save","hover"]
tooltips = [
    ('value', '$y'),
    ('title', '$name')
]

fig = figure(plot_width=1000, plot_height=400,
             title = "signals",
             x_axis_type='datetime',
             y_axis_label='Signal value',
             tools=tools, tooltips=tooltips)

fig.add_layout(Legend(), 'right')
fig.legend.click_policy= 'hide'


def update_fig():
    global fig, df, source
    
    get_data()
    source.data = df

    cols = df.columns
    
    dict = {}
    for signalData, cc, title in zip(cols, d3['Category10'][len(cols)], cols):
        dict[title] = fig.line('Date', signalData, source=source, color=cc, name=title)

    legends = [(tt, [dict[tt]]) for tt in dict]
    fig.legend.update(items = legends)
    

def button_callback():
    global fig, source
    
    source.data = {}
    fig.legend.items = []

    update_fig()
    
button.on_click(button_callback)

layout = layout([[fig], [button]])

curdoc().add_root(layout)
curdoc().title = "snippet plot clear issue"

@mateo The glyph renderers are stored in plot.renderers you would need to either remove glyph renderers from that, or set their .visible=False to remove (or hide) them from the plot. Just removing legend items has no effect on what renderers the plot has (glyphs can be present without being in legends, a single legend item can display multiple glyphs, e.g. a line+marker combo, there is not a trivial 1-1 relation, so they have to be considered independently).

Thank you @Bryan for your quick reply. For the sake of other people who find this issue, both of these solutions work:

def button_callback():
    global fig, source
    
    source.data = {}
    fig.legend.items = []

    # SOLUTION 1
    fig.renderers = [] 

    update_fig()

or

    # SOLUTION 2
    for rr in fig.renderers:
        rr.visible = False

1 Like

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