Dynamically show/hide panel layouts

panel: 0.11.3
bokeh: 2.3.2
python: 3.9.4

ISSUE: I have a data analysis and visualization application which makes use of the visible property of objects to show / hide certain user controls, plots, etc. based on the state of the system and user interactions with the app.

It is functionally possible to accomplish all of this by working at the lowest possible level – modifying the visible properties of individual widgets and figures.

However, it is more convenient, robust, and a smoother user experience if this can be done at the layout level. The app is structured into logical layout groups, so being able to do this one rows, columns, grids of models/figures is programmatically straightforward.

This all works as expected for objects that I can wrap in core bokeh row, column layouts. However, for groups that require me to use panel layouts (pn.Row, pn.Column), because they include panel specific things like Card or an indicator gauge, I have not been able to make this work.

Specifically, panel Row and Column layouts do not have a visible property. I can access and set the visible property by getting the root – which references a bokeh layout, it appears – but that seems to have no actual affect on the visibility.

The motivation for all of this is that setting the visible properties of individual models, widgets, figures is that the user-experience is less than ideal. One can see discrete steps of objects disappearing one-by-one, the web page updating, etc. rather than smoothly doing everything at once.

Here’s a simplified example of a server app that shows what is being attempted. Note that the panel layout group remains visible and its state is not consistent with the reported visible property of the root object.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np

from bokeh.models import ColumnDataSource
from bokeh.models import RadioGroup

from bokeh.plotting import figure
from bokeh.layouts import column

import panel as pn

t = np.arange(0.0, 1.01, 0.01)
source = ColumnDataSource(data=dict(t=t, ct=np.cos(2.0*np.pi*t), st=np.sin(2.0*np.pi*t)))

bk_figs = []

p = figure(width=500, height=250, title='cos(t) bokeh layout')
p.line(x='t', y='ct', source=source)
bk_figs += [p]

p = figure(width=500, height=250, title='sin(t) bokeh layout')
p.line(x='t', y='st', source=source)
bk_figs += [p]

pn_figs = []
p = figure(width=500, height=250, title='cos(t) panel layout')
p.line(x='t', y='ct', source=source)
pn_figs += [p]

p = figure(width=500, height=250, title='sin(t) panel layout')
p.line(x='t', y='st', source=source)
pn_figs += [p]

bk_col = column(bk_figs)

pn_col = pn.Column(*pn_figs)
pn_root = pn_col.get_root()

def show_hide_cb(attr, old, new):
    if new == 0:
        bk_col.visible = False
        pn_root.visible = False
        bk_col.visible = True
        pn_root.visible = True

    print("bokeh visible {:} | panel visible {:}".format(bk_col.visible, pn_root.visible))

b = RadioGroup(labels=['Hide','Show'], active=1)
b.on_change('active', show_hide_cb)

ll = pn.Column(b,bk_col,pn_col)
1 Like