Python 3.6+Bokeh 12.15:循环中Hover Tool与复选框的使用问题
Hey Jeff! Let’s dig into this Bokeh issue you’re hitting—totally understand how annoying it is when tooltips drop out unexpectedly when toggling lines. The good news is yes, you absolutely can use HoverTool alongside a CheckboxGroup in a for loop, even with variable-length datasets. The problem you’re seeing usually stems from mismanaging renderer-HoverTool associations or not isolating each line’s data properly.
Let’s break down the fix step by step, with a working example tailored to your setup (Python 3.6, Bokeh 1.2.15):
First, the Root Cause of Your Tooltip Issue
Chances are, your original code either:
- Shared a single
ColumnDataSourceacross lines (which breaks with variable lengths anyway), or - Didn’t explicitly bind the HoverTool to individual line renderers, leading to Bokeh losing track of which tooltips belong to which lines when toggling visibility.
- Had a callback that accidentally modified the HoverTool’s renderer list instead of just toggling line visibility.
Working Code Solution
Here’s a complete, tested example that addresses all these points:
from bokeh.plotting import figure, show, output_file from bokeh.models import ColumnDataSource, HoverTool, CheckboxGroup, CustomJS from bokeh.layouts import column import numpy as np # 1. Create independent data sources for each line (critical for variable lengths) data_sources = [] line_labels = ["Line 1", "Line 2", "Line 3", "Line 4"] for idx in range(4): # Variable-length data (10, 11, 12, 13 points) x_vals = np.arange(10 + idx) y_vals = np.random.rand(len(x_vals)) source = ColumnDataSource(data={"x": x_vals, "y": y_vals}) data_sources.append(source) # 2. Create the figure and render each line, storing renderers p = figure( title="Variable-Length Lines with Hover & Toggle", x_axis_label="X", y_axis_label="Y", tools="pan,box_zoom,reset" ) line_renderers = [] for source, label in zip(data_sources, line_labels): renderer = p.line( "x", "y", source=source, legend_label=label, line_width=2 ) line_renderers.append(renderer) # 3. Add HoverTool explicitly bound to all line renderers hover_tool = HoverTool( tooltips=[ ("X Value", "@x"), ("Y Value", "@y{0.00}") # Format Y to 2 decimal places ], renderers=line_renderers # This ensures the tool targets only our lines ) p.add_tools(hover_tool) # 4. Create CheckboxGroup and callback to toggle line visibility checkbox_group = CheckboxGroup( labels=line_labels, active=list(range(len(line_labels))) # All lines visible by default ) # Callback logic: only toggle visibility, don't touch HoverTool settings toggle_callback = CustomJS( args={"renderers": line_renderers, "checkbox": checkbox_group}, code=""" for (let i = 0; i < renderers.length; i++) { // Set visibility based on whether the index is in the active list renderers[i].visible = checkbox.active.includes(i); } """ ) checkbox_group.js_on_change("active", toggle_callback) # 5. Assemble layout and show layout = column(checkbox_group, p) output_file("multi_line_hover_toggle.html") show(layout)
Key Fixes & Notes
- Independent Data Sources: Each line gets its own
ColumnDataSource—this is non-negotiable when dealing with variable-length datasets, as shared sources will cause rendering errors or unexpected behavior. - Explicit HoverTool Binding: By setting
renderers=line_renderers, we tell the HoverTool exactly which lines to monitor. When a line is hidden, the tool simply ignores it instead of breaking tooltips for other lines. - Isolated Toggle Logic: The callback only modifies each line’s
visibleproperty—no changes to the HoverTool itself. This keeps tooltip functionality intact for all visible lines, no matter which ones you toggle off.
This should resolve the issue where closing one line breaks tooltips for subsequent lines. Test it out with your actual data, and adjust the tooltips or styling to match your needs!
内容的提问来源于stack exchange,提问作者Jeff Tilton

