Bokeh图表标题与工具栏未渲染问题求助
排查Bokeh动态添加图表时的渲染异常问题
这个Cannot read property 'draw_legend' of undefined错误大概率不是Bokeh的bug,而是你在多线程环境下操作Bokeh文档的方式不符合它的线程安全要求,加上旧版本Bokeh的一些限制导致的。我来给你拆解问题和解决方案:
核心问题:线程不安全的文档修改
Bokeh Server的Document对象(也就是你代码里的doc)不是线程安全的,所有对文档内模型(比如添加图表、修改数据源)的操作都必须在Bokeh自身的事件循环线程中执行。而你继承multithreading.Thread后,直接在自定义子线程里执行plt_col.append(fig[fig_name])这类修改操作,会导致Python端的模型状态和前端JS端不同步——前端拿到的模型可能是不完整的,比如工具栏、图例的关联对象还没初始化,自然会抛出找不到draw_legend的错误。
解决方案:用Bokeh的回调机制修改文档
你需要把所有修改文档的代码包装到Bokeh提供的add_next_tick_callback方法中,确保操作在正确的线程上下文里执行。修改你的添加图表代码如下:
from bokeh.plotting import curdoc def add_new_figure(): if fig_name not in fig: print(f'add fig {fig_name}') TOOLS="pan,wheel_zoom,box_zoom,reset,save,tap,hover" # 创建figure fig[fig_name] = figure( plot_width=900, plot_height=280, tools=TOOLS, toolbar_location='right', logo=None, title=fig_name, name=str(fig_name) ) # 初始化数据源(建议同时初始化后续要用到的y字段,避免后续添加线条时出错) cds[fig_name] = ColumnDataSource(data=dict(x=np.array([]), y=np.array([])), name=f'{fig_name}_cds') # 获取UI容器并添加图表 plt_col = curdoc().get_model_by_name('plt_ui_col').children plt_col.append(fig[fig_name]) # 不要在子线程直接修改文档,而是提交回调到Bokeh事件循环 curdoc().add_next_tick_callback(add_new_figure)
额外的排查点
- 确保figure有渲染器:如果你的figure创建后没有添加任何线条、散点等渲染器(比如
fig[fig_name].line(...)),空图表可能会触发旧版本Bokeh的图例渲染bug。记得在添加图表后及时绑定数据源和渲染器。 - 考虑升级Bokeh版本:你用的0.12.16是2018年的旧版本,后续的1.x和2.x系列修复了大量多线程和渲染相关的问题。如果条件允许,升级到稳定版能避免很多这类奇怪的兼容性问题。
内容的提问来源于stack exchange,提问作者Andreas Gschossmann
相关产品推荐
相关产品推荐

