Controlling Tabulator layout/rendering in a GoldenTemplate layout

I am linking Tabulators together so that selecting a row in one drives the data loaded and viewed in another two. When I view the individual UI components in separate Jupyter notebooks, everything appears to be fine. However, when I try to put these components in separate tabs within a GoldenTemplate layout, the headers of some of my Tabulators disappear!

I was about to open a new GitHub issue to report these disappearing linked Tabulator headers in the GoldenTemplate layout, when it occurred to me that perhaps it had something to do with my specifying hidden_columns in the constructor.

When I commented out the hidden_columns, sure enough all the headers appeared (including the ones I didn’t want to see!), but after selecting a row in the first Tabulator (which loads related data into the second Tabulator) the headers and data columns are not aligned.

If I set the Tabulator.hidden_columns after launching the GoldenLayout app I can hide the columns, but only if I am viewing the tab containing that Tabulator (otherwise the headers disappear again!). Or if I run the whole notebook (Cell > Run All) and hide the columns without allowing the app to render first, the headers again disappear.

I have pushed the above reproducible example to GitHub with more descriptions and screenshots in the README. I am testing with a recent development version of panel (0.13.0a31) and I have included conda environment YAML files in the repo.

I have a feeling I’m missing something in how I initialise and synchronise the Tabulators. Any advice would be greatly appreciated.

Is there a way to force a Tabulator (or any other panel widget) to refresh itself or rerender after changing some of its properties?

I also seem to remember seeing some code in a Discourse answer that allows for code to run after the app is fully running, but I cannot remember the name of the attribute or property that was checked so my searches have drawn a blank!

See the pn.state.onload() method.

I can see you have put a lot of work into this. Though this can (at least for me) make it hard to understand the problem itself and therefore possible to find a solution or verify if it is a bug. So is it possible for you to reduce the problem into a minimal, reproducible example (MRE)?

When looking at your repo I will suggest you put all the widgets into functions so they don’t end up sharing their state between different sessions.

Thanks @Hoxbro. I have now cut down the example to a single short notebook and updated the README in my GH repo.

Cell #3 controls (hide_columns) whether the Tabulators are created with or without hidden_columns.

These are the resulting screenshots of the GoldenLayout tabs and the Jupyter notebook cell.

Hide some columns (hide_columns=True)

No Tabulator headers are visible in the GoldenLayout tabs.

Show all columns (hide_columns=False)

In the GoldenLayout tabs the columns below the headers are not aligned with the headers.

I’m running panel from the master version (0.13.0a36.post8+gc24e28fa) and running the MRE code from a python script (see the code at the end) and do not see the bug you are describing. Which is great! But I have noticed another bug, which only shows the tabulator inside the Description tab:

with the following error code from the console:

Error rendering Bokeh items: TypeError: e._parent is undefined
    relayout http://localhost:5006/static/extensions/panel/panel.min.js?v=825da5bcba32652b44f53ebde35c74003f27a4004248d5508e6b3e5a5ca1924d:52
    renderComplete http://localhost:5006/static/extensions/panel/panel.min.js?v=825da5bcba32652b44f53ebde35c74003f27a4004248d5508e6b3e5a5ca1924d:52
    renderComplete http://localhost:5006/static/extensions/panel/panel.min.js?v=825da5bcba32652b44f53ebde35c74003f27a4004248d5508e6b3e5a5ca1924d:52
    renderTable http://localhost:5006/static/extensions/panel/bundled/datatabulator/tabulator-tables@4.9.3/dist/js/tabulator.js:4968
    redraw http://localhost:5006/static/extensions/panel/bundled/datatabulator/tabulator-tables@4.9.3/dist/js/tabulator.js:5667
    redraw http://localhost:5006/static/extensions/panel/bundled/datatabulator/tabulator-tables@4.9.3/dist/js/tabulator.js:10882
    redraw http://localhost:5006/static/extensions/panel/panel.min.js?v=825da5bcba32652b44f53ebde35c74003f27a4004248d5508e6b3e5a5ca1924d:52
    after_layout http://localhost:5006/static/extensions/panel/panel.min.js?v=825da5bcba32652b44f53ebde35c74003f27a4004248d5508e6b3e5a5ca1924d:52
    after_layout http://localhost:5006/static/js/bokeh.min.js?v=b3e0592a1e90448fa40dcddd5cd5217dcd6b3c7dd368020f6a8e4be4fdd1adbdeb2824eb75ab4f5120c80dc7684f8ff2aa2f8b00d6f393108c96d2f6f2cf0297:496
    compute_layout http://localhost:5006/static/js/bokeh.min.js?v=b3e0592a1e90448fa40dcddd5cd5217dcd6b3c7dd368020f6a8e4be4fdd1adbdeb2824eb75ab4f5120c80dc7684f8ff2aa2f8b00d6f393108c96d2f6f2cf0297:496
    build http://localhost:5006/static/js/bokeh.min.js?v=b3e0592a1e90448fa40dcddd5cd5217dcd6b3c7dd368020f6a8e4be4fdd1adbdeb2824eb75ab4f5120c80dc7684f8ff2aa2f8b00d6f393108c96d2f6f2cf0297:496
    renderTo http://localhost:5006/static/js/bokeh.min.js?v=b3e0592a1e90448fa40dcddd5cd5217dcd6b3c7dd368020f6a8e4be4fdd1adbdeb2824eb75ab4f5120c80dc7684f8ff2aa2f8b00d6f393108c96d2f6f2cf0297:496
    f http://localhost:5006/static/js/bokeh.min.js?v=b3e0592a1e90448fa40dcddd5cd5217dcd6b3c7dd368020f6a8e4be4fdd1adbdeb2824eb75ab4f5120c80dc7684f8ff2aa2f8b00d6f393108c96d2f6f2cf0297:584

Though, if I change pn.Column to pn.panel it works:

So my advice will be to try and update to the latest dev version, and/or wait for the next dev version 0.13.0a37. And see if the problems still occur for your bigger reproducible example. For the error with using pn.Column I will properly advise you to create an issue on Github.

Code used

Run with panel serve FILENAME

import pandas as pd
import panel as pn
pn.extension('tabulator')

hide_columns = True
use_column = True

# Description Tabulator widget
descr = ['Under the Weather', 'Top Drawer', 'Happy as a Clam']
code = [f'{i+1:02d}' for i in range(len(descr))]
dummy = ['dummy'] * len(descr)
df1 = pd.DataFrame(dict(code=code, descr=descr, dummy=dummy))

t1 = pn.widgets.Tabulator(
    df1,
    disabled=True, # disable editing => click anywhere selects row
    header_filters=True,
    selectable=1,
    show_index=False,
    height=200,
    name="Description"
)

if hide_columns:
    t1.hidden_columns = ['dummy']


# Linked item/datetime Tabulator widget
df2 = pd.DataFrame(dict(
        code_idx=[0, 0, 0, 1, 2],
        item=['Rain', 'Sleet', 'Snow', 'T-shirt', 'Joy'],
        datetime=pd.date_range(
            '2021-12-1 12:00', '2022-1-21 12:00', periods=5)))

t2 = pn.widgets.Tabulator(
    df2,
    disabled=True, # disable editing => click anywhere selects row
    header_filters=dict(
        code_idx=dict(type='input', func='='),
        item=dict(type='input'),
        # exclude datetime field so Tabulator widget displays properly
    ),
    selectable=False,
    show_index=False,
    height=200,
    name="Linked"
)

if hide_columns:
    t2.hidden_columns = ['code_idx']

def link_tabs(*events):
    for event in events:
        if event.name == 'selection':
            idx = event.obj.selected_dataframe.index
            code_idx = None if idx.empty else idx[0].item()
            df = df2[df2.code_idx == code_idx]
            t2.selection = []
            t2.value = df

t1.param.watch(link_tabs, ['selection'], onlychanged=False)

golden = pn.template.GoldenTemplate(title=f'Linked Tabulators ({hide_columns = }, {use_column = })')

if use_column:
    golden.main.append(pn.Column(t1, name='Description'))
    golden.main.append(pn.Column(t2, name='Linked'))
else:
    golden.main.append(pn.panel(t1, name='Description'))
    golden.main.append(pn.panel(t2, name='Linked'))

golden.servable()