Legend visibility should add/remove items from hover tool?

I would like only visible glyphs to appear in my hover tool, however all plotted glyphs are shown.

I see this was addressed with #6120, however it does not seem to be working with my current code.

Is there something I can easily change? Not seeing anything obvious in the docs that I am missing.

After creating my figure and adding all glyphs to the hover tool, I then toggle visibility for certain glyphs with:

p.select_one({'name':'%s' % x}).visible = False

But this does not seem to remove them from the hover tool…

Relevant code:

def make_airtemp_fig(station_list, label_list, hide_list, legend_out, start, end):
  Create the air temp panel and plot data
  station_list: list of dataframes, in legend display order
  label_list: list of label strings, in legend display order
  hide_list: list of label strings for initial setting of visible=False, any order
  legend_out: boolean, True puts legend outside, False keeps legend inside
  p: Bokeh Figure object
  valid_stations = []
  valid_labels = []
  valid_colors = []

  for station, label in zip(station_list, label_list):
    if not station.empty: # check for empty dataframes
  cds = create_cds(valid_stations, valid_labels, 'air_temp')
  p = figure(
      title="Air Temp",
      x_range = (start,end),
      y_range = (0,1),
      width=width, height=height,
  tooltips=[("Date", "@Date_hover")]
  for color, label in zip(valid_colors, valid_labels):
    legenddict[label] = p.line(x='Date', y=label, line_width=2, color=color, alpha=0.8, name=label, source=cds)
    tooltips.append(("%s" % label, "@%s{int}" % label))

  if legend_out==True:
    legend1 = Legend(
        location=(0, 15),
        padding=0, #only applies when border visible

    legend2 = Legend(
        location=(0, 10),
        padding=0, #only applies when border visible

    p.add_layout(legend1, 'below')
    p.add_layout(legend2, 'below')
    legend = Legend(
  for x in hide_list: # go through stations on the hide_list
    if p.select_one({'name':'%s' % x}): # check if the line glyph is present
      p.select_one({'name':'%s' % x}).visible = False
  return p

Hi @Patrick_Wright Can you share a complete, minimal reproducer? In general a complete script that can be copy/pasted and immediately run, as-is, with no modifications, is much, much more useful than snippets.

@Bryan yes I have always regretted not being able to actually post a complete reproducer.

Unfortunately the code snippet above is part of a much larger app (currently 1700+ lines of Bokeh plotting code, and another 1200+ lines of code to import and process the source data). I could come up with some dummy data and reproduce the problem in a smaller standalone example… but I was hoping there might be something obvious with the way I am using the hover tool with the snippet above.

I’m happy to provide any other relevant pieces if it useful (such as building the column data sources or making the layout…). But it seems like the snippet above is the only relevant piece.

Thanks much.

The only other potentially helpful piece I can provide at this time is an example of the current behavior in production. The following URL shows how all glyphs are in the hover tool, despite many of them with visibility toggled to hidden.

The snippet above creates the first panel.


(you no doubt recognize this app from my various posts over the years… thanks again to @Bryan and the core Bokeh dev team!)

I do remember it and it is really great looking (would be a great addition to the Showcase, I don’t think it is in there yet?)

That said, an MRE is an ask for you to construct something purpose built and minimal that demonstrates a problem (which may have nothing to do with your “actual” code, it should really just demo the issue and nothing else). Most MRE’s should be < 30 lines of code. We have to ask for these for a few reasons:

  • There are 100s of thousands of users and a few of us. Even a tiny amount of “filling in the gaps” multiplied out becomes a huge burden on a very few people
  • Easily possible to guess something wrong and “fill the gaps” incorrectly, that risks wasting effort on the wrong problem entirely. This is a huge disincentive.
  • It’s often possible with a complete MRE that it can be run directly and an expert can recognize a problem or solution nearly immediately. Any time I personally see an MRE I am usually inclined to try it out right there, which often means some kind of answer, right there.

So, if you can MRE I am very happy to run it and see what more I can say about it. It’s entirely possible there is a regression but in that case we need and MRE to serve as a test case anyway.

Yes I would like to add this to the Showcase… there are a few more improvements to make, and then I would like to include in the Showcase, perhaps in a few months.

Totally understood regarding the MRE. Thanks again and I will try to put something together.

1 Like

@Patrick_Wright I just realized there was an MRE in the original issue, so I ran that as a check and it is behaving as expected. I.e., there is no hover tooltip displayed when hovering over hidden glyphs.

So I looked at your deployed example, and it also appears to behave as I would expect. E.g. if you hide all the glyphs on a plot, then no hover tooltip shows up. However you appear to have mode="vline" set active, so is there is even one glyph visible, then the entire tooltip shows up (as expected) anytime the “vline” hits that glyph. Are expecting the tooltip content itself to change, i.e. rows to show up/disappear based on visibility? If so, nothing like that has ever been suggested or implemented. The tooltip, if the cursor “hits” a gyph, always just shows exactly the complete content it is configured with. You could possibly do something with CustomJSHover to get that kind of behavior though I have not tried.

@Bryan, it appears that I was misunderstanding the goal of that original MRE in the GH Issue. You are correct that I was hoping there was built-in functionality to add/remove rows from the hover tool content based on what is visible in the figure.

Sorry for the misunderstanding and thanks for following up. I may explore a solution with CustomJSHover.

1 Like