如何在Bokeh中启用交互式CheckboxGroup?点击后阴影区域不显示问题
问题原因分析
你的代码有几个关键问题导致复选框点击后阴影区域没显示:
静态HTML无法运行Python回调:你用
output_file()生成静态HTML文件,但.on_click()绑定的Python函数clicker属于服务器端回调——这种回调需要Bokeh Server才能执行,静态HTML里根本跑不了Python代码。渲染器管理逻辑错误:就算用了Bokeh Server,每次点击都调用
p.quad()会不断新增矩形,而不是切换已有元素的显示/隐藏;而且当没有勾选复选框时,直接访问checkbox_group.active[0]会抛出索引越界的错误。缺少文档更新触发:服务器端回调需要明确通知文档更新,否则页面不会同步变化。
解决方案
下面提供两种可行方案,你可以根据需求选择:
方案一:用Bokeh Server运行Python回调
如果想保留Python逻辑,需要用Bokeh Server启动代码,步骤如下:
- 预先创建两个矩形渲染器,初始设置为隐藏;
- 在回调里根据复选框的激活状态切换对应矩形的
visible属性; - 用
curdoc().add_root()替代show(),通过Bokeh Server启动服务。
修改后的代码:
from bokeh.io import curdoc from bokeh.models import ColumnDataSource, HoverTool, CheckboxGroup, WidgetBox from bokeh.plotting import figure from bokeh.layouts import layout # 模拟测试数据(你可以替换成自己的真实数据) source = ColumnDataSource(data=dict(x=[10,20,30,40], y=[10,20,30,40])) hover = HoverTool(tooltips=["(indep,dep)","($x,$y)"]) p = figure(plot_width=300, plot_height=300, tools=[hover], title="sample") # 预先创建两个阴影矩形,初始隐藏 rect1 = p.quad(top=50.0, bottom=25.0, left=0.0, right=25.0, visible=False, fill_color='lightblue', fill_alpha=0.3) rect2 = p.quad(top=25.0, bottom=0.0, left=25.0, right=50.0, visible=False, fill_color='lightgreen', fill_alpha=0.3) checkbox_group = CheckboxGroup(labels=["select 1","select 2"], active=[]) def clicker(active): # 根据激活的复选框索引切换矩形可见性 rect1.visible = 0 in active rect2.visible = 1 in active checkbox_group.on_click(clicker) circles = p.circle('x','y', source=source) hover.renderers.append(circles) L = layout([p, WidgetBox(checkbox_group)], sizing_mode='fixed') curdoc().add_root(L)
运行方式:在终端执行bokeh serve --show your_script.py,浏览器会自动打开交互式页面。
方案二:用CustomJS做客户端回调(适合静态HTML)
如果想生成独立的静态HTML文件,不需要Bokeh Server,就用JavaScript回调:
- 预先创建两个矩形渲染器,初始隐藏;
- 编写JS逻辑,根据复选框状态切换矩形的可见性;
- 通过
js_on_change绑定回调到复选框的active属性。
修改后的代码:
from bokeh.io import output_file, show from bokeh.models import ColumnDataSource, HoverTool, CheckboxGroup, WidgetBox, CustomJS from bokeh.plotting import figure from bokeh.layouts import layout # 模拟测试数据 source = ColumnDataSource(data=dict(x=[10,20,30,40], y=[10,20,30,40])) hover = HoverTool(tooltips=["(indep,dep)","($x,$y)"]) p = figure(plot_width=300, plot_height=300, tools=[hover], title="sample") # 预先创建两个阴影矩形,初始隐藏 rect1 = p.quad(top=50.0, bottom=25.0, left=0.0, right=25.0, visible=False, fill_color='lightblue', fill_alpha=0.3) rect2 = p.quad(top=25.0, bottom=0.0, left=25.0, right=50.0, visible=False, fill_color='lightgreen', fill_alpha=0.3) checkbox_group = CheckboxGroup(labels=["select 1","select 2"], active=[]) # 编写JavaScript回调逻辑 callback = CustomJS(args=dict(rect1=rect1, rect2=rect2), code=""" const active = cb_obj.active; // 切换矩形可见性 rect1.visible = active.includes(0); rect2.visible = active.includes(1); """) checkbox_group.js_on_change('active', callback) circles = p.circle('x','y', source=source) hover.renderers.append(circles) output_file("test_scatter.html", mode="inline", title="sample scatter") L = layout([p, WidgetBox(checkbox_group)], sizing_mode='fixed') show(L)
直接运行脚本生成HTML文件,打开后点击复选框就能正常显示对应的阴影区域了。
关键要点总结
- 静态HTML只能用CustomJS客户端回调,Python回调必须依赖Bokeh Server;
- 推荐预先创建渲染器,通过切换
visible属性控制显示/隐藏,而非每次点击新增元素; - 判断复选框状态时,用
in检查索引是否在active列表中,避免索引越界错误。
内容的提问来源于stack exchange,提问作者Ron
相关产品推荐
相关产品推荐

