Updating multiple tabs in DataTable using 1 slider for all tabs

See the code below.

I have a loop that creates 3 tabs on 1 DataTable and 1 RangeSlider.

I want all 3 tabs to update when I use the slider, but unfortunately only the last tab updates.

It probably has something to do with the iteration because it’s the last tab always, but I can’t wrap my head around how to make it work.

from bokeh.models.widgets import DataTable, DateFormatter, TableColumn, Panel, Tabs, RangeSlider
from bokeh.io import output_file, show
from bokeh.models import ColumnDataSource, Div
from bokeh.plotting import curdoc
from bokeh.plotting import figure
from bokeh.layouts import layout, widgetbox, row, column
from datetime import date
from random import randint

import pandas as pd

from os.path import dirname, join

df1 = pd.DataFrame(
        {'x': [date(2014, 3, i+1) for i in range(10)],
         'y': [randint(0, 100) for i in range(10)],
         'z': [randint(0, 50) for i in range(10)],
         'q': ['B' for i in range(10)]
        })
df2 = pd.DataFrame(
        {'x': [date(2014, 3, i+1) for i in range(10)],
         'y': [randint(0, 100) for i in range(10)],
         'z': [randint(0, 50) for i in range(10)],
         'q': ['A' for i in range(10)]
        })
df3 = pd.DataFrame(
        {'x': [date(2014, 3, i+1) for i in range(10)],
         'y': [randint(0, 100) for i in range(10)],
         'z': [randint(0, 50) for i in range(10)],
         'q': ['A' for i in range(10)]
        })

value_slider = RangeSlider(title="Value", range =(0,100), start=0, end=100, step=1)

tab_list = []
for df in [df1, df2, df3]:
    source = ColumnDataSource(data=dict(
            x=df['x'],
            y=df['y'],
            z=df['z']
        ))
    columns = [
        TableColumn(field="x", title="Date", formatter=DateFormatter()),
        TableColumn(field="y", title="Downloads"),
        TableColumn(field="z", title="Z"),
        ]

    data_table = DataTable(source=source, columns=columns, width=400, height=280)
    tab = Panel(child=data_table, title="table")
    tab_list.append(tab)

    def select():
        selected = df[
            (df['y'] >= value_slider.range[0]) &
            (df['y'] <= value_slider.range[1])
                ]
        return selected

    def update():
        df1 = select()
        source.data = dict(
            x = df1['x'],
            y = df1['y'],
            z = df1['z'],
            )

tabs = Tabs(tabs=tab_list)
value_slider.on_change('range' ,lambda attr, old, new: update())
selectors = widgetbox(value_slider,width=200)

all_plot = row(column(selectors), tabs)
sizing_mode = 'fixed'
l = layout([[all_plot]], sizing_mode=sizing_mode)

curdoc().add_root(l)

Your functions definition should not be in the for loop. Even outside the select() callback is not necessary, and anyway it’s using the last value taken by ‘df’, which is df3, that’s why only your last table updates.

This works:
from bokeh.models.widgets import DataTable, DateFormatter, TableColumn, Panel, Tabs, RangeSlider
from bokeh.io import output_file, show
from bokeh.models import ColumnDataSource, Div
from bokeh.plotting import curdoc
from bokeh.plotting import figure
from bokeh.layouts import layout, widgetbox, row, column
from datetime import date
from random import randint

import pandas as pd

from os.path import dirname, join

df1 = pd.DataFrame(
{‘x’: [date(2014, 3, i+1) for i in range(10)],
‘y’: [randint(0, 100) for i in range(10)],
‘z’: [randint(0, 50) for i in range(10)],
‘q’: [‘B’ for i in range(10)]
})
df2 = pd.DataFrame(
{‘x’: [date(2014, 3, i+1) for i in range(10)],
‘y’: [randint(0, 100) for i in range(10)],
‘z’: [randint(0, 50) for i in range(10)],
‘q’: [‘A’ for i in range(10)]
})
df3 = pd.DataFrame(
{‘x’: [date(2014, 3, i+1) for i in range(10)],
‘y’: [randint(0, 100) for i in range(10)],
‘z’: [randint(0, 50) for i in range(10)],
‘q’: [‘A’ for i in range(10)]
})

value_slider = RangeSlider(title=“Value”, range =(0,100), start=0, end=100, step=1)

columns = [TableColumn(field=“x”, title=“Date”, formatter=DateFormatter()),
TableColumn(field=“y”, title=“Downloads”),
TableColumn(field=“z”, title=“Z”),
]

tab_list =
df_list = [df1, df2, df3]
source_list = [ColumnDataSource(data={‘x’:df[‘x’],‘y’:df[‘y’],‘z’:df[‘z’]}) for df in df_list]
tab_list = [Panel(child=DataTable(source=source, columns=columns, width=400, height=280), title=“table”) for source in source_list]

def update():
for i,source in enumerate(source_list):
new_df = df_list[i][(df_list[i][‘y’]>=value_slider.range[0]) & (df_list[i][‘y’]<=value_slider.range[1])]
source.data = { ‘x’:new_df[‘x’],‘y’:new_df[‘y’],‘z’:new_df[‘z’],}

tabs = Tabs(tabs=tab_list)
value_slider.on_change(‘range’ ,lambda attr, old, new: update())
selectors = widgetbox(value_slider,width=200)

all_plot = row(column(selectors), tabs)
sizing_mode = ‘fixed’
l = layout([[all_plot]], sizing_mode=sizing_mode)

curdoc().add_root(l)

``

you my friend are a genius.

Thanks so much :slight_smile: