I noticed that when trying to update part of a Bokeh layout, the DataTable display changed (see attached picture) even though no modifications is made to the table explicitly in the code. The goal would be to update only one of the chart/table of the bokeh document, and not modifying the rest.
I do it by adding a column object to the root, and then modifying directly the children of the column object. Is there another more standard way to do it? I don't know whether it is a bug, or I'm breaking it by using a hack to update the charts.
The bokeh version used is 3.1.0, this issue did not appear with the previous version I was using (2.4.2)
Here is a code that illustrate this behaviour:
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)
# I tried to specify width and formatter for TableColumn and DataTable as well, but
# that did not change the behaviour
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')
def update_plot():
plot_data = pd.DataFrame(data={'x': [1, 2], 'y': [12, 8]})
plot = self._get_plot(plot_data)
# Only the second object is replaced, the Button and the DataTable are left as they are
self._layout.children[1] = plot
plot_data = pd.DataFrame(data={'x': [1, 2], 'y': [10, 15]})
plot = self._get_plot(plot_data)
button.on_click(update_plot)
self._layout = bokeh.layouts.column([button, plot, table])
bokeh.io.curdoc().add_root(self._layout)
bokeh_plot = BokehPlot()
bokeh_plot.draw_plot()
Edit:
Actually it seems to be fine if I put the plot in a bokeh object, and then modify this object. The two lines to change would be:
self._layout = bokeh.layouts.column([button, bokeh.layouts.column(plot), table])
and
self._layout.children[1].children[0] = bokeh.layouts.column(plot)
However, it seems more complicated to maintain than previous code, and more of a hack.