-
-
Notifications
You must be signed in to change notification settings - Fork 409
Description
ALL software version info
(this library, plus any other relevant software, e.g. bokeh, python, notebook, OS, browser, etc should be added within the dropdown below.)
Software Version Info
"panel>=1.7.2",
"holoviews>=1.21.0",
"bokeh>=3.7.3",
"pandas",
"numpy<2"
Browser: Firefox 139
OS: MacOS 14.4
Python 3.12
Description of expected behavior and the observed behavior
When plotting an overlay, the separate zoom controls (for X axis and Y axis) are not added to the toolbar, only the combined "Wheel Zoom" tool is added. The wheel zoom does still correctly zoom the X and Y axes separately if you hover over the axis while zooming.
The expected behaviour is for the X and Y zoom tools to be added separately to the toolbar if specified independently or together in tools. This is the behaviour seen when plotting an individual Curve or Points plot (for example).
I tried different variations to attempt to work around this, but they all suffer the problem detailed above:
- Only specify
xwheel_zoom,ywheel_zoomon a single plot in the overlay - Specify
xwheel_zoomandywheel_zoomon separate plots in the overlay (1 per plot) - Specify
xwheel_zoomandywheel_zoomon the overlay itself, not its child plots - Create
WheelZoomToolmanually rather than using string aliases
I can see that specifying other tools (eg: hover) are correctly propagated to the overlay.
Complete, minimal, self-contained example code that reproduces the issue
#!/usr/bin/env -S uv run
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "panel>=1.7.2",
# "holoviews>=1.21.0",
# "bokeh>=3.7.3",
# "pandas",
# "numpy<2"
# ]
# ///
import numpy as np
import pandas as pd
import holoviews as hv
import panel as pn
# Enable Panel/HoloViews
pn.extension()
hv.extension("bokeh")
# Generate random data
np.random.seed(42)
n_points = 100
# Data for curve plot
time_range = pd.date_range('2024-01-01', periods=n_points, freq='1min')
curve_data = pd.DataFrame({
'time': time_range,
'value': np.cumsum(np.random.randn(n_points)) + 100
})
# Data for points plot
points_data = pd.DataFrame({
'time': np.random.choice(time_range, size=20),
'value': np.random.uniform(95, 105, 20),
'category': np.random.choice(['A', 'B', 'C'], 20)
})
# Data with different dimension names for decoupled plots
curve_data_separate = curve_data.rename(columns={'time': 'curve_time', 'value': 'curve_value'})
points_data_separate = points_data.rename(columns={'time': 'points_time', 'value': 'points_value'})
def create_plots():
# Create curve plot for overlay
curve = hv.Curve(
curve_data,
kdims=['time'],
vdims=['value']
).opts(
color='blue',
line_width=2,
tools=['xwheel_zoom', 'ywheel_zoom', 'hover'],
title="Combined Curve + Points"
)
# Create points plot for overlay (using same dimensions for proper overlay)
points = hv.Points(
points_data,
kdims=['time', 'value'],
vdims=['category']
).opts(
color='category',
cmap={'A': 'red', 'B': 'green', 'C': 'orange'},
size=10,
tools=['xwheel_zoom', 'ywheel_zoom', 'hover']
)
# Create overlay with both plots
overlay = hv.Overlay([curve, points])
return overlay
def create_separate_curve():
# Create standalone curve plot with different dimension names
curve = hv.Curve(
curve_data_separate,
kdims=['curve_time'],
vdims=['curve_value']
).opts(
color='blue',
line_width=2,
tools=['xwheel_zoom', 'ywheel_zoom', 'hover'],
title="Standalone Curve Plot"
)
return curve
def create_separate_points():
# Create standalone points plot with different dimension names
points = hv.Points(
points_data_separate,
kdims=['points_time', 'points_value'],
vdims=['category']
).opts(
color='category',
cmap={'A': 'red', 'B': 'green', 'C': 'orange'},
size=10,
tools=['xwheel_zoom', 'ywheel_zoom', 'hover'],
title="Standalone Points Plot"
)
return points
# Create the plots
overlay_pane = create_plots()
curve_pane = create_separate_curve()
points_pane = create_separate_points()
# Create Panel dashboard
dashboard = pn.Column(
pn.pane.Markdown("## Minimal Reproduction: Curve + Points with Zoom Controls"),
pn.pane.Markdown("### Combined Overlay (no separate zoom controls)"),
pn.panel(overlay_pane, sizing_mode="stretch_both"),
pn.pane.Markdown("### Separate Curve Plot (has separate zoom controls)"),
pn.panel(curve_pane, sizing_mode="stretch_both"),
pn.pane.Markdown("### Separate Points Plot (has separate zoom controls)"),
pn.panel(points_pane, sizing_mode="stretch_both"),
sizing_mode="stretch_both"
)
if __name__ == "__main__" or __name__.startswith("bokeh"):
pn.serve(
dashboard,
title="Repro - Curve and Points",
websocket_origin="*",
show=True,
port=5007
)Stack traceback and/or browser JavaScript console output
N/A
Screenshots or screencasts of the bug in action
Individual curve and points plots have both zoom tools independently on the toolbar (on the right hand side)

An overlay of a curve and a points plot only has the combined wheel zoom tool on the right hand side:
- I may be interested in making a pull request to address this