Legend_field, multiple data sources, click_policy: is click_policy supposed to apply to entire data source rather than just one legend item?

I have a plot with two data sources. Each data source is plotted using figure.multi_line.

The plot needs to be updated on the fly based on user choices in widgets that allow changing the subset plotted or the preferred color scheme to highlight entries with various properties. The legend also needs to update based on the new color scheme. I use the legend_field option to accomplish this.

In response to the widget callbacks, I take my “raw data”, filter it and rerun pandas.groupby to create new data, and update the plot by doing data_source.data = new_view_of_raw_data

The plot and legend update correctly. However, I am using click_policy = “hide” and the clicking is not working as expected.

If you click on any of the legend items for data source “X”, all of the legend items for data source “X” disappear, not just the ones for the row of the legend you clicked on.

I really wanted just the one item to be hidden.

Is this a bug or expected behavior?

It’s a limitation wrt legend_field, and one I am not sure how we can work around. The hide policy acts very simply: hiding means toggling the visible property on the glyph, which hides the entire glyph. But legend_field (added later) “splits up” a glyph in a fairly unusual way for Bokeh, and the result is the same glyph being used in multiple rows of a legend. But then that means hiding any row will hide them all.

Making these two things work together would not be trivial, unfortunately. I’m not even really sure what a good approach might look like, offhand, since it would definitely not be appropriate, e.g. for a click on a legend to update actual data so as to make one subset “disappear”. But I am not sure what else could be done.

Probably the most likely short-term solution would be to implement user-visible click event on legend rows, so then users could potentially “hide” parts of the data themselves (e.g. if you want to modify an “alpha” column to hide some of the rows of data, that’s ok as your chioice).

Hmmm… OK… I had been looking at creating LegendItem objects manually as a possible fallback option, but you don’t think that would work either?

Not really. As long as there is only actually one single glyph, then anything that sets the value of visible will affect everything that glyph renders. The only mechanism I can think of to hide a “subset” of a glyph is to have a column of alpha values, and to set the individual rows to 0 or 1 depending on whether you want the item for a row drawn or not. With a “legend click” event, that is something users could do in their own CustomJS callbacks, according to their specific needs.

For reference, there are issues for both the limitation and the workaround feature:

[BUG] legend click policy 'hide' has unexpected behavior on automatic-grouping · Issue #10998 · bokeh/bokeh · GitHub

[FEATURE] Legend click events · Issue #12185 · bokeh/bokeh · GitHub

Yeah, I don’t really want to create new glyphs for each subgroup, since that would mean there would be a different number of glyphs depending on the user’s choices.

There’s no easy way to clear out the glyphs already in a plot and start over. It’s much easier to keep the glyph object the same and just swap in a new set of data.

Is what it is, I guess. The bug has been sitting there since 2021…

It’s a corner case that does not actually seem to come up that much in practice, yet will require an extensive and expensive ground-up reworking of some fundamental inner workings of Bokeh to fix properly. Meanwhile, like almost every OSS project anywhere, there is vastly more work than people to do it. It is, as you say, what it is.

FWIW, I would much prefer to just have a way to “delete all the glyphs” and make a new plot content and legend than have to deal with click events on individual legend items manually.

My suggestion would just be to encapsulate plot creation and swap out the old plot for a new plot.

1 Like