Bokeh 2.4.3如何为散点添加onclick点击事件跳转对应子图?
实现方法
Bokeh 2.4.3 实现这个需求不需要启动服务端,纯静态输出即可完成点击跳转,同时可以通过批量处理逻辑完全避免硬编码重复代码,具体实现如下:
核心逻辑
- 提前批量生成所有子图,保存为独立HTML文件,和主图放在同一目录
- 给主图散点的数据源追加「对应子图文件路径」字段
- 绑定
TapTool点击事件的CustomJS回调,点击点时读取对应路径,新标签页打开子图 - 所有子图的生成、数据读取通过循环实现,后续新增数据只需要更新配置和索引表,不需要重复写绘图代码
完整可运行代码
from bokeh.plotting import figure, show, output_file, save from bokeh.models.tools import HoverTool, TapTool from bokeh.models import CustomJS, ColumnDataSource import pandas as pd # -------------------------- 配置区,后续新增文件只需要修改这里即可 -------------------------- # 主索引表,存储所有子图的编号、对应数据文件映射关系 main_df = pd.read_csv("list_of_all_five_data_file.csv") # 文件命名规则配置,和本地实际命名保持一致即可 subplot_html_prefix = "subplot_" subplot_csv_prefix = "sample_data" # ----------------------------------------------------------------------------------------- # 第一步:批量生成所有子图,无需手动编写df1/df2、p1/p2这类重复代码 subplot_paths = [] for file_no in main_df["file_no"]: # 读取当前子图对应的数据 csv_path = f"{subplot_csv_prefix}{file_no}.csv" sub_df = pd.read_csv(csv_path) # 绘制子图 p_sub = figure(title=f"This is data{file_no} plot") p_sub.scatter(sub_df["x_values"], sub_df["y_values"]) # 保存子图为独立HTML文件 sub_html_name = f"{subplot_html_prefix}{file_no}.html" output_file(sub_html_name, mode="inline") save(p_sub) subplot_paths.append(sub_html_name) # 第二步:绘制主图 output_file("main.html") # 用ColumnDataSource统一管理主图点数据,包含坐标、对应子图路径三个字段 main_source = ColumnDataSource(data={ "x": list(range(1, len(main_df)+1)), "y": main_df["file_no"].values, "subplot_path": subplot_paths }) plot = figure(title="Main Plot") # 绑定数据源绘制散点 dot_renderer = plot.dot(x="x", y="y", source=main_source, size=15) # 保留原有悬停提示功能 hover = HoverTool() hover.tooltips = """ <div>Click to see this data</div> """ plot.add_tools(hover) # 绑定点击跳转回调 tap_callback = CustomJS(args=dict(source=main_source), code=""" // 获取当前选中的点索引 const selectedIdx = source.selected.indices[0]; if (selectedIdx === undefined) return; // 读取对应子图路径,新标签页打开 const targetPath = source.data["subplot_path"][selectedIdx]; window.open(targetPath, "_blank"); // 清空选中状态,避免重复点击同一个点不触发跳转 source.selected.indices = []; """) tap_tool = TapTool(callback=tap_callback, renderers=[dot_renderer]) plot.add_tools(tap_tool) show(plot)
注意事项
- 代码修正了原有示例的参数错误:原
plot.dot的y参数传入了整个DataFrame列对象、和x列表长度不匹配的问题,所有绘图数据统一通过ColumnDataSource管理 - 批量生成子图时用
save()而不是show(),避免生成过程中弹出大量浏览器窗口,最后仅展示主图即可 - 所有生成的HTML文件(main.html和所有subplot_*.html)需要放在同一目录下,否则跳转路径会匹配失败
- 如果需要调整子图样式,只需要修改循环内的子图绘制逻辑,所有子图会统一应用修改,不需要逐个调整
内容的提问来源于stack exchange,提问作者AbdulMuhaymin
相关产品推荐
相关产品推荐

