bokeh
bokeh copied to clipboard
Add support toggleable `CustomAction` tool buttons
Problem description
It is really hard to come up with a good legend position that works for all input data, while keeping the plot compact, so inevitably it will obscure the underlying data for some data inputs, or will take up an overly large amount of screen real estate.
However, it is frequently necessary to either hide or show items, or just to know what you are looking at.
Being able to hide/show the legend as needed would be very helpful towards making plots that work pretty well on disparate datasets without spending too much time on legend positioning.
Feature description
Add a button on the toolbar that allows you to show/hide the legend.
Potential alternatives
Putting the legend outside the plot area works, however it then takes up more real estate. Developing complex heuristics for where the legend can go and cover the least amount of data, but this can be error prone and complicated.
Allowing the legend to be dragged around is another feature request that could be valuable, but this seems simpler.
Additional information
No response
FWIW this seems like something that could be accomplished currently with a CustomAction and one line of JS code to toggle .visible on the legend.
Based on examples/basic/annotations/legend.py:
import numpy as np
from bokeh.layouts import gridplot
from bokeh.plotting import figure, show
x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)
TOOLS = "pan,wheel_zoom,box_zoom,reset,save,box_select"
p1 = figure(title="Legend Example", tools=TOOLS)
p1.scatter(x, y, legend_label="sin(x)")
p1.scatter(x, 2*y, legend_label="2*sin(x)", color="orange")
p1.scatter(x, 3*y, legend_label="3*sin(x)", color="green")
p1.legend.title = 'Markers'
p2 = figure(title="Another Legend Example", tools=TOOLS)
p2.scatter(x, y, legend_label="sin(x)")
p2.line(x, y, legend_label="sin(x)")
p2.line(x, 2*y, legend_label="2*sin(x)",
line_dash=(4, 4), line_color="orange", line_width=2)
p2.scatter(x, 3*y, legend_label="3*sin(x)",
marker="square", fill_color=None, line_color="green")
p2.line(x, 3*y, legend_label="3*sin(x)", line_color="green")
p2.legend.title = 'Lines'
from bokeh.models import CustomAction, CustomJS
toggle_legend = CustomAction(
description="Toggle legend",
callback=CustomJS(args=dict(legends=p1.legend + p2.legend), code="""
export default ({legends}) => {
for (const legend of legends) {
legend.visible = !legend.visible
}
}
"""),
)
gp = gridplot([p1, p2], ncols=2, width=400, height=400)
gp.toolbar.tools.append(toggle_legend)
show(gp)
Screencast from 19.03.2024 19:58:16.webm
Would be even better if we could use toggleable button (see PR #13571).
Note that this has to be added to GridPlot.toolbar and not Plot.toolbar, because in the later case, due to proxying, the custom action would be called as many times as there are plots, resulting in unexpected behavior. This can be alleviated by using gridplot(plots, merge_tools=False).
Would be even better if we could use toggleable button.
I think adding a toggle-able option for a custom action would be great.
I am less enthusiastic about adding a new built-in tool just for this.