Posssible to hide all lines in my plot and then show them again one by one?

Hi!

I am very new to Bokeh and I am exploring it as an alternative to Plotly interactive graphs. I am running into a small issue / question when trying to use the interactive legends: in general, i work with plots that show 35 lines at the same time, so it is very common that i need to hide all and only show one or two at a time. This is quite easy to do with Plotly just by double clicking, but I cannot figure out how to do it on Bokeh - and haven’t found any answers online either.

This is some code that I am using to test. I have plotted two lines and enabled interactive legend to mute them. Since I don’t think there is a built-in way to hide all lines at the same time, I came across this option to add a custom button via JS. When I click the button, it hides or shows all lines, but this cannot be later modified by clicking on the legend. What I mean: if I hide all lines with the button, then I cannot show one of them again by clicking on the legend. Is there a way I could make this happen? Or have a similar outcome, I just need to easily isolate lines on my plots.

Thanks in advance!

from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.models import CustomJS, Button
from bokeh.layouts import column
import numpy as np

# Enable the output in the notebook
output_notebook()
fig = figure(title="Scatter Plot Example", x_axis_label='X-Axis', y_axis_label='Y-Axis')

x = np.linspace(0, 100, 500)

a1, b1 = np.random.random(2) * 10
series1 = a1 + b1 * np.sin(x)

a2, b2 = np.random.random(2)
series2 = a2 + b2 * np.cos(x)

line1 = fig.line(x, series1, color='red', legend_label='series1', muted_alpha=0.2)
line2 = fig.line(x, series2, color='blue', legend_label='series2', muted_alpha=0.2)
fig.legend.click_policy = 'mute'

# Create a button to hide/show all lines
button = Button(label="Hide All Lines", button_type="success")
callback = CustomJS(args=dict(lines=[line1, line2], button=button), code="""
    let allVisible = lines.every(line => line.visible);
    for (let i = 0; i < lines.length; i++) {
        lines[i].visible = !allVisible;
        lines[i].muted = !lines[i].visible;
    }
    button.label = allVisible ? 'Show All Lines' : 'Hide All Lines';
""")

button.js_on_click(callback)
layout = column(button, fig)

# Show the results
show(layout)

one way to do it is to hide them by using muted_alpha=0 instead of hiding the lines. That way you can still use the interactive legend to make them visible one by one:

from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.models import CustomJS, Button
from bokeh.layouts import column
from bokeh.palettes import Category10_10
import numpy as np

# Enable the output in the notebook
output_notebook()
fig = figure(title="Scatter Plot Example", x_axis_label='X-Axis', y_axis_label='Y-Axis')

x = np.linspace(0, 100, 500)
lines = []
for i in range(5):
    a1, b1 = np.random.random(2) * 10
    series1 = a1 + b1 * np.sin(x)

    lines.append(
        fig.line(x, series1, color=Category10_10[i], legend_label=f'series{i+1}', muted_alpha=0, width=2)
    )


fig.legend.click_policy = 'mute'
fig.add_layout(fig.legend[0], 'right')

# Create a button to hide/show all lines
button = Button(label="Hide All Lines", button_type="success")
callback = CustomJS(args=dict(lines=lines, button=button), code="""
    let isVisible = button.label == 'Hide All Lines'
    for (let i = 0; i < lines.length; i++) {
        lines[i].muted = isVisible;
    }
    button.label = isVisible ? 'Show All Lines' : 'Hide All Lines';
""")

button.js_on_click(callback)
layout = column(button, fig)

# Show the results
show(layout)

2 Likes

Yes, thanks! That’s what I tried to do but my JS is not that formidable. This really solves a big issue for me.

Have a nice day :slight_smile:

2 Likes