Callbacks to update widgets in Bootstrap template sidebar based on active tab?

Hi everyone,

This is my first post in the discourse, so excuse me if I don’t follow protocol here…

I am building a feature, where I’d like the widgets in a template sidebar to be updated depending on which tab is active. Given my understanding of Panel, I thought the best way to achieve this is via a callback/linking.

I can’t share the exact code for compliance reasons, but I created a dummy example (leveraging https://panel.holoviz.org/user_guide/Templates.html) and a dummy callback. However, it doesn’t seem to work.

Am I missing something here? Or is such a feature not supported?

Any input is greatly appreciated. Thank you all and excited to be part of this community.

import panel as pn
import holoviews as hv
import numpy as np
    
pn.extension()
hv.extension('bokeh')
pn.config.sizing_mode = 'stretch_width'

def insert_widget(target, event):
    active_tab_idx = event.new
    target.objects.insert(active_tab_idx, freq)

bootstrap = pn.template.BootstrapTemplate(title='Bootstrap Template')

xs = np.linspace(0, np.pi)
freq = pn.widgets.FloatSlider(name="Frequency", start=0, end=10, value=2)
phase = pn.widgets.FloatSlider(name="Phase", start=0, end=np.pi)

@pn.depends(freq=freq, phase=phase)
def sine(freq, phase):
    return hv.Curve((xs, np.sin(xs*freq+phase))).opts(
        responsive=True, min_height=400)

@pn.depends(freq=freq, phase=phase)
def cosine(freq, phase):
    return hv.Curve((xs, np.cos(xs*freq+phase))).opts(
        responsive=True, min_height=400)

bootstrap.sidebar.append(freq)
bootstrap.sidebar.append(phase)
tabs = pn.Tabs(dynamic=True, active=0)
tabs.append(('Sine', pn.Card(hv.DynamicMap(sine), title='Sine')))
tabs.append(('Cosine', pn.Card(hv.DynamicMap(cosine), title='Cosine')))
tabs.link(bootstrap.sidebar, callbacks={'active': insert_widget})
bootstrap.main.append(tabs)
bootstrap.show()

Welcome ! I am not so familiar with the link method, instead I prefer to use the pn.depends decorators. In that case, you can populate the sidebar with a pn.Column and with a callback you can replace the elements in that pn.Column object. I think the pn.Column object like a simple python list, then you can replace the panel object in the list so simply like replacing the elements. In this case you have only one element, but it can be the quantity you want. The working example is

import panel as pn
import holoviews as hv
import numpy as np
    
pn.extension()
hv.extension('bokeh')
pn.config.sizing_mode = 'stretch_width'


bootstrap = pn.template.BootstrapTemplate(title='Bootstrap Template')

xs = np.linspace(0, np.pi)
freq = pn.widgets.FloatSlider(name="Frequency", start=0, end=10, value=2)
phase = pn.widgets.FloatSlider(name="Phase", start=0, end=np.pi)

@pn.depends(freq=freq, phase=phase)
def sine(freq, phase):
    return hv.Curve((xs, np.sin(xs*freq+phase))).opts(
        responsive=True, min_height=400)

@pn.depends(freq=freq, phase=phase)
def cosine(freq, phase):
    return hv.Curve((xs, np.cos(xs*freq+phase))).opts(
        responsive=True, min_height=400)

col = pn.Column(freq)
bootstrap.sidebar.append(col)

tabs = pn.Tabs(dynamic=True, active=0)
tabs.append(('Sine', pn.Card(hv.DynamicMap(sine), title='Sine')))
tabs.append(('Cosine', pn.Card(hv.DynamicMap(cosine), title='Cosine')))

@pn.depends(tabs.param.active, watch=True)
def insert_widget(active_tab):
    print (active_tab)
    if active_tab == 0: 
        col[0] = freq
    else:
        col[0] = phase

    
bootstrap.main.append(tabs)
bootstrap.show()
1 Like

This is the same example with the link method. it is noteworthy to mention you can use col.append() to add elements dinamically to the sidebar.

import panel as pn
import holoviews as hv
import numpy as np
    
pn.extension()
hv.extension('bokeh')
pn.config.sizing_mode = 'stretch_width'


bootstrap = pn.template.BootstrapTemplate(title='Bootstrap Template')

xs = np.linspace(0, np.pi)
freq = pn.widgets.FloatSlider(name="Frequency", start=0, end=10, value=2)
phase = pn.widgets.FloatSlider(name="Phase", start=0, end=np.pi)

@pn.depends(freq=freq, phase=phase)
def sine(freq, phase):
    return hv.Curve((xs, np.sin(xs*freq+phase))).opts(
        responsive=True, min_height=400)

@pn.depends(freq=freq, phase=phase)
def cosine(freq, phase):
    return hv.Curve((xs, np.cos(xs*freq+phase))).opts(
        responsive=True, min_height=400)

col = pn.Column(freq)
bootstrap.sidebar.append(col)

tabs = pn.Tabs(dynamic=True, active=0)
tabs.append(('Sine', pn.Card(hv.DynamicMap(sine), title='Sine')))
tabs.append(('Cosine', pn.Card(hv.DynamicMap(cosine), title='Cosine')))

def insert_widget( target, event):
    print (event.new)
    if event.new == 0: 
         col[0] = freq
    else:
         col[0] = phase
        
tabs.link(col, callbacks={'active': insert_widget})
    
bootstrap.main.append(tabs)
bootstrap.show()
2 Likes