How to update a holoviews plot within a tab using Callback API

I’m trying to update a plot within a tab using the callback api. I’m running the following in Jupyterlab.

import pandas as pd
import holoviews as hv
import panel as pn

pn.extension()
hv.extension('bokeh')

data = pd.DataFrame([[0,1],[2,3],[4,5]], columns=['a', 'b'])

radio_button = pn.widgets.RadioButtonGroup(
    name="radio_button", options=["a", "b"], value="a",
)
plot_pane = pn.pane.HoloViews(
                hv.Curve(data, kdims=['index'], vdims=radio_button.value)
)

layout = pn.Column(
    radio_button,
    pn.Tabs(('plot 1', plot_pane)),
)

def update(event):
    plot_pane.object = hv.Curve(data, kdims=['index'], vdims=[radio_button.value])
    return
    
radio_button.param.watch(update, 'value')

layout

I get the error below when I toggle the radio buttons. The code works as expected if I place the plot in the Column directly, and not in a tab. Could anyone offer some help on what I could be doing wrong here?

Traceback (most recent call last):
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/pyviz_comms/__init__.py", line 316, in _handle_msg
    self._on_msg(msg)
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/panel/viewable.py", line 243, in _on_msg
    doc.unhold()
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/bokeh/document/document.py", line 668, in unhold
    self._trigger_on_change(event)
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/bokeh/document/document.py", line 1151, in _trigger_on_change
    self._with_self_as_curdoc(event.callback_invoker)
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/bokeh/document/document.py", line 1169, in _with_self_as_curdoc
    return f()
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/bokeh/util/callback_manager.py", line 155, in invoke
    callback(attr, old, new)
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/panel/viewable.py", line 495, in _comm_change
    self._process_events({attr: new})
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/panel/viewable.py", line 512, in _process_events
    self.param.set_param(**self._process_property_change(events))
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/param/parameterized.py", line 1365, in set_param
    self_._batch_call_watchers()
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/param/parameterized.py", line 1480, in _batch_call_watchers
    watcher.fn(*events)
  File "<ipython-input-12-e6b7464ee34f>", line 14, in update
    plot_pane.object = hv.Curve(data, kdims=['index'], vdims=[radio_button.value])
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/param/parameterized.py", line 294, in _f
    instance_param.__set__(obj, val)
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/param/parameterized.py", line 296, in _f
    return f(self, obj, val)
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/param/parameterized.py", line 861, in __set__
    obj.param._call_watcher(watcher, event)
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/param/parameterized.py", line 1456, in _call_watcher
    watcher.fn(event)
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/panel/pane/base.py", line 188, in _update_pane
    self._update_object(ref, doc, root, parent, comm)
  File "/home/balast/anaconda3/envs/panel/lib/python3.8/site-packages/panel/pane/base.py", line 166, in _update_object
    index = parent.children.index(old_model)
AttributeError: 'Tabs' object has no attribute 'children'

I think you need to change your layout instead of the plot_pane like so:

def update(event):
    layout[1][0] = hv.Curve(data, kdims=['index'], vdims=[radio_button.value])

The layout indexing: 1 for pointing to the tabs, and 0 for pointing to the first tab.

1 Like

Thanks, Leonidas, for your response. It was very helpful, and got me over the hurdle I’d hit. What you posted updates the plot, but the next issue I saw was that the tab label was also updated.

I went and re-read the Tabs documentation (https://panel.holoviz.org/reference/layouts/Tabs.html). It states not to modify the tabs object directly. I settled on the following for my use case which works as expected

import pandas as pd
import holoviews as hv
import panel as pn

pn.extension()
hv.extension('bokeh')

data = pd.DataFrame([[0,1],[2,3],[4,5]], columns=['a', 'b'])

radio_button = pn.widgets.RadioButtonGroup(
    name="radio_button", options=["a", "b"], value="a",
)

def plot_pane_func(radio_button):
    plot_pane = pn.pane.HoloViews(
                hv.Curve(data, kdims=['index'], vdims=radio_button.value),
                name='tab1'
    )
    return plot_pane

tabs = pn.Tabs(('tab1', plot_pane_func(radio_button)))
layout = pn.Column(
    radio_button,
    tabs,
)


def update(*events):
    tabs.__setitem__(0, plot_pane_func(radio_button))
    
radio_button.param.watch(update, 'value')

layout
3 Likes