Is there a way to hide/show all glyphs with one action?

When you plot a figure with legends you can set the legends to hide/show the glyph it belongs to when clicking on it. This is what I’ve done in this simple example:

from bokeh.plotting import figure, show
import numpy as np

x = np.linspace(0, 2*np.pi, 101)

fig = figure()
fig.line(x, np.cos(x), color="red", legend_label="cos(x)")
fig.line(x, np.sin(x), color="green", legend_label="sin(x)")
fig.line(x, np.cos(x) + 1, color="blue", legend_label="cos(x) + 1")

fig.legend.location = "right"
fig.legend.click_policy = "hide"

show(fig)

While very useful, it can quickly become tedious if you have a large number of glyphs and you want to hide every glyph except for one. Is there any way to create an additional label that hides/shows every glyph when you click on it?

You can control the visibility property of all figure renderers with CustomJS. Just pass the figure instance as an arg, and loop through its renderers property, turning visibility of each on/off.

Here’s a rough example building on yours, using a button to accomplish this. Other widgets may obviously be more appropriate for your use case:

from bokeh.plotting import figure, show
from bokeh.models import CustomJS, Button
from bokeh.layouts import row
import numpy as np

from bokeh.models import Button

x = np.linspace(0, 2*np.pi, 101)

fig = figure()
fig.line(x, np.cos(x), color="red", legend_label="cos(x)")
fig.line(x, np.sin(x), color="green", legend_label="sin(x)")
fig.line(x, np.cos(x) + 1, color="blue", legend_label="cos(x) + 1")

fig.legend.location = "right"
fig.legend.click_policy = "hide"

btn = Button(label='Hide All')
cb = CustomJS(args=dict(fig=fig,btn=btn)
              ,code='''
              if (btn.label=='Hide All'){
                  for (var i=0; i<fig.renderers.length; i++){
                          fig.renderers[i].visible=false}
                  btn.label = 'Show All'
                  }
              else {for (var i=0; i<fig.renderers.length; i++){
                      fig.renderers[i].visible=true}
              btn.label = 'Hide All'}
              ''')

btn.js_on_click(cb)
show(row([fig,btn]))

Now a good follow up I don’t have the answer to:

I could add an extra LegendItem to the existing legend, say an extra item with the label “Show/Hide All” . However, it does not appear that the ability to trigger CustomJS on clicking a particular LegendItem is exposed, meaning I don’t think you can do this. This gets me thinking further → I think what happens when you say legend.click_policy = ‘hide’ is a CustomJS gets auto-generated and attributed to each LegendItem, but that CustomJS is not available/exposed to the user. Or is it and I just can’t find it? I would enjoy an explanation/correction of my understanding of these mechanics too :slight_smile:

1 Like

Legend clicking has to also handle muting, which involves activating an entire other version of the glyph, so it’s more complicated. The codepaths for handling legend clicks is built into BokehJS. There are not currently any user-facing events triggered on legend clicks, thought it is possible those could be added. That would open a path for users to add more sophisticated behaviors if they need something more than the stock mute/hide options. A GitHub Issue would be the first start.

2 Likes

@gmerritt123 Thanks a lot for the code! =)

@Bryan Ok, I’ll create an issue in the repo.