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

Bokeh问题:更新布局时DataTable列出现折叠现象

问题分析与解决方案

原因说明

这不是Bokeh的Bug,是3.x版本对布局更新的处理逻辑发生了变化。当你直接修改根Column的children数组替换Plot组件时,Bokeh会重新计算整个根容器的布局上下文(包括尺寸、渲染优先级等),而DataTable的渲染依赖容器上下文,因此被连带触发了不必要的重绘,导致显示异常。这种行为在2.4.2的旧布局引擎中不会出现。

正确解决方法

方法1:更新现有Plot的数据源(推荐)

Bokeh的核心设计就是通过更新数据源实现局部刷新,而非替换组件。这种方式完全不会影响其他布局元素,性能也最优:

修改代码,将Plot实例保存为类属性,在更新时直接修改其数据源:

import bokeh
import bokeh.plotting
import pandas as pd

class BokehPlot:
        
    @staticmethod
    def _init_plot():
        # 初始化空的Plot框架,后续只更新数据源
        plot = bokeh.plotting.figure(width=800, height=400, x_range=(0, 5), y_range=(0, 20))
        plot_source = bokeh.models.ColumnDataSource(data={'x': [], 'y': []})
        plot.line('x', 'y', source=plot_source)
        return plot
    
    def draw_plot(self):
        table_data = pd.DataFrame(data={'column_1': [11, 21], 'column_2': [12, 22]})
        table_source = bokeh.models.ColumnDataSource(data=table_data)
        table_columns = [bokeh.models.TableColumn(field=column, title=column) for column in table_data.columns]
        table = bokeh.models.DataTable(source=table_source, columns=table_columns)
        
        button = bokeh.models.Button(label='Click')
        
        # 初始化Plot并保存为实例属性
        self._plot = self._init_plot()
        # 设置初始数据
        initial_data = pd.DataFrame(data={'x': [1, 2], 'y': [10, 15]})
        self._plot.data_source.data = initial_data.to_dict('list')
        
        def update_plot():
            # 仅更新数据源,不替换Plot组件
            new_data = pd.DataFrame(data={'x': [1, 2], 'y': [12, 8]})
            self._plot.data_source.data = new_data.to_dict('list')
        
        button.on_click(update_plot)
        
        self._layout = bokeh.layouts.column([button, self._plot, table])
        bokeh.io.curdoc().add_root(self._layout)

bokeh_plot = BokehPlot()
bokeh_plot.draw_plot()

方法2:用嵌套容器隔离替换操作

如果确实需要替换整个Plot组件,可以将Plot单独放在一个嵌套的Column中,修改子容器的children,避免触动根容器的布局上下文:

import bokeh
import bokeh.plotting
import pandas as pd

class BokehPlot:
        
    @staticmethod
    def _get_plot(plot_data):
        plot_source = bokeh.models.ColumnDataSource(plot_data)
        plot = bokeh.plotting.figure(width=800, height=400, x_range=(0, 5), y_range=(0, 20))
        plot.line('x', 'y', source=plot_source)
        return plot
    
    def draw_plot(self):
        table_data = pd.DataFrame(data={'column_1': [11, 21], 'column_2': [12, 22]})
        table_source = bokeh.models.ColumnDataSource(data=table_data)
        table_columns = [bokeh.models.TableColumn(field=column, title=column) for column in table_data.columns]
        table = bokeh.models.DataTable(source=table_source, columns=table_columns)
        
        button = bokeh.models.Button(label='Click')
        
        # 初始化Plot并放入嵌套容器
        initial_data = pd.DataFrame(data={'x': [1, 2], 'y': [10, 15]})
        self._plot_container = bokeh.layouts.column([self._get_plot(initial_data)])
        
        def update_plot():
            new_data = pd.DataFrame(data={'x': [1, 2], 'y': [12, 8]})
            new_plot = self._get_plot(new_data)
            # 仅修改嵌套容器的children,不影响根布局
            self._plot_container.children = [new_plot]
        
        button.on_click(update_plot)
        
        self._layout = bokeh.layouts.column([button, self._plot_container, table])
        bokeh.io.curdoc().add_root(self._layout)

bokeh_plot = BokehPlot()
bokeh_plot.draw_plot()

总结

优先使用方法1,既符合Bokeh的设计理念,又能彻底避免布局副作用。方法2适合必须替换整个组件的场景,通过嵌套容器隔离更新范围,比直接修改根容器的维护成本更低。

内容的提问来源于stack exchange,提问作者K. Do

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 12:53:36