You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重建Bokeh绘图后Pan工具与Wheel Zoom工具无法使用问题排查

解决Bokeh回调重建绘图后工具栏失效的问题

我帮你分析下这个问题哈,你遇到的工具栏失效问题,本质是因为你直接替换全局的fig变量后,Bokeh的文档(Document)并没有同步更新这个引用——旧的figure还挂在文档里,工具栏也是和旧图绑定的,新创建的figure其实根本没被正确加到文档中,自然没法用工具栏操作它。

下面给你两种可行的解决方案,优先推荐第一种,性能更好也更符合Bokeh的设计思路:

方案1:更新数据源而非重建Figure(推荐)

Bokeh的核心优势就是基于ColumnDataSource的交互式更新,不需要重建整个绘图,只需要切换数据源就能实现内容更新,工具栏也会一直正常工作。

from bokeh.plotting import figure, curdoc
from bokeh.models import Select, ColumnDataSource
from bokeh.layouts import column
import pandas as pd
from datetime import datetime

# 模拟不同属性对应的数据集
def get_data(attribute):
    if attribute == 'Attribute 1':
        return pd.DataFrame({
            'x': [datetime(2023, 1, i) for i in range(1, 11)],
            'y': [i*2 for i in range(1, 11)]
        })
    elif attribute == 'Attribute 2':
        return pd.DataFrame({
            'x': [datetime(2023, 1, i) for i in range(1, 11)],
            'y': [i*3 for i in range(1, 11)]
        })
    else:
        return pd.DataFrame({'x': [], 'y': []})

# 初始化数据源和基础绘图
initial_data = get_data('Attribute 1')
source = ColumnDataSource(data=initial_data.to_dict('list'))
fig = figure(
    x_axis_type='datetime',
    plot_width=1000,
    plot_height=600,
    tools=['pan','crosshair','wheel_zoom']
)
# 添加绘图元素(这里用line举例,你可以换成自己的图形类型)
fig.line(x='x', y='y', source=source)

# 初始化下拉框
select = Select(title="Select Attribute", options=['Attribute 1', 'Attribute 2'], value='Attribute 1')

# 下拉框回调:仅更新数据源
def update_plot(attr, old, new):
    new_data = get_data(new)
    source.data = new_data.to_dict('list')

select.on_change('value', update_plot)

# 构建布局并添加到文档
layout = column(select, fig)
curdoc().add_root(layout)

方案2:必须重建Figure时,正确替换布局中的组件

如果你因为某些原因必须重建整个Figure,那一定要确保把旧的Figure从布局中移除,再把新的Figure添加进去,让Bokeh文档能追踪到新的绘图对象。

from bokeh.plotting import figure, curdoc
from bokeh.models import Select
from bokeh.layouts import column
from datetime import datetime

# 初始化下拉框和空布局
select = Select(title="Select Attribute", options=['Attribute 1', 'Attribute 2'], value='Attribute 1')
layout = column(select)

# 封装创建Figure的函数
def create_figure(attribute):
    fig = figure(
        x_axis_type='datetime',
        plot_width=1000,
        plot_height=600,
        tools=['pan','crosshair','wheel_zoom']
    )
    # 根据属性添加对应的绘图逻辑
    if attribute == 'Attribute 1':
        fig.line([datetime(2023,1,1), datetime(2023,1,10)], [2, 20])
    elif attribute == 'Attribute 2':
        fig.circle([datetime(2023,1,1), datetime(2023,1,10)], [3, 30], size=10)
    return fig

# 下拉框回调:替换布局中的Figure
def select_onchange(attr, old, new):
    # 移除布局中已存在的旧Figure(如果有)
    if len(layout.children) > 1:
        layout.children.pop(1)
    # 创建新Figure并添加到布局
    new_fig = create_figure(new)
    layout.children.append(new_fig)

select.on_change('value', select_onchange)

# 初始添加第一个Figure
initial_fig = create_figure(select.value)
layout.children.append(initial_fig)

curdoc().add_root(layout)

为什么你的原代码不行?

你原来的代码只是单纯替换了全局的fig变量,但Bokeh的文档并没有感知到这个变化——文档里依然引用着最初创建的旧Figure,工具栏也是和旧图绑定的,新创建的Figure根本没有被加入到文档的布局结构中,所以工具栏的操作自然无法作用到新图上。

内容的提问来源于stack exchange,提问作者Cracken

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:26:20