Add new plot from selection

I am trying to add a plot based on a selection.

import panel as pn
import holoviews as hv

pn.extension()

plots = {}
pipes = {}
def source_selected(event):                                                                                                                                                                                                                    
    if event.new not in plots:                                                                                                                                                                                                                 
        pipe = hv.streams.Pipe(data=[])                                                                                                                                                                                                        
        plot = hv.DynamicMap(hv.Curve, streams=[pipe])                                                                                                                                                                                         
        pipes[event.new] = pipe                                                                                                                                                                                                                
        plots[event.new] = plot                                                                                                                                                                                                                
        row.append(plot)                                                                                                                                                                                                                       

source_select = pn.widgets.Select(name='Source', options=['waveform', 'waveform2'])
source_select.param.watch(source_selected, 'value')
row = pn.Row(source_select)

pn.serve(row)

That results in the following error:

ERROR:tornado.application:Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOMainLoop object at 0x7f4aac9a47d0>>, <Task finished coro=<_needs_document_lock.<locals>._needs_document_lock_wrapper() done, defined at /home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/bokeh/server/session.py:51> exception=ValueError("failed to validate Figure(id='1021', ...).align: expected an element of either Enum('start', 'center', 'end') or Tuple(Enum('start', 'center', 'end'), Enum('start', 'center', 'end')), got None")>)
    Traceback (most recent call last):
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/tornado/ioloop.py", line 741, in _run_callback
        ret = callback()
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/tornado/ioloop.py", line 765, in _discard_future_result
        future.result()
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/bokeh/server/session.py", line 71, in _needs_document_lock_wrapper
        result = await result
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/tornado/gen.py", line 216, in wrapper
        result = ctx_run(func, *args, **kwargs)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/panel/reactive.py", line 249, in _change_coroutine
        self._change_event(doc)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/panel/reactive.py", line 259, in _change_event
        self._process_events(events)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/panel/reactive.py", line 242, in _process_events
        self.param.set_param(**self._process_property_change(events))
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/param/parameterized.py", line 1472, in set_param
        self_._batch_call_watchers()
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/param/parameterized.py", line 1611, in _batch_call_watchers
        self_._execute_watcher(watcher, events)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/param/parameterized.py", line 1573, in _execute_watcher
        watcher.fn(*args, **kwargs)
      File "test.py", line 14, in source_selected
        row.append(plot)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/panel/layout/base.py", line 264, in append
        self.objects = new_objects
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/param/parameterized.py", line 318, in _f
        return f(self, obj, val)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/param/parameterized.py", line 936, in __set__
        obj.param._call_watcher(watcher, event)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/param/parameterized.py", line 1591, in _call_watcher
        self_._execute_watcher(watcher, (event,))
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/param/parameterized.py", line 1573, in _execute_watcher
        watcher.fn(*args, **kwargs)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/panel/reactive.py", line 230, in _param_change
        self._update_model(events, msg, root, model, doc, comm)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/panel/layout/base.py", line 62, in _update_model
        msg[self._rename['objects']] = self._get_objects(model, old, doc, root, comm)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/panel/layout/base.py", line 103, in _get_objects
        child = pane._get_model(doc, root, model, comm)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/panel/pane/holoviews.py", line 239, in _get_model
        plot = self._render(doc, comm, root)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/panel/pane/holoviews.py", line 304, in _render
        return renderer.get_plot(self.object, **kwargs)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/holoviews/plotting/bokeh/renderer.py", line 73, in get_plot
        plot = super(BokehRenderer, self_or_cls).get_plot(obj, doc, renderer, **kwargs)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/holoviews/plotting/renderer.py", line 237, in get_plot
        plot.update(init_key)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/holoviews/plotting/plot.py", line 900, in update
        return self.initialize_plot()
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/holoviews/plotting/bokeh/element.py", line 1330, in initialize_plot
        plot = self._init_plot(key, style_element, ranges=ranges, plots=plots)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/holoviews/plotting/bokeh/element.py", line 495, in _init_plot
        **properties)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/bokeh/plotting/figure.py", line 165, in __init__
        super().__init__(*arg, **kw)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/bokeh/model.py", line 236, in __init__
        super().__init__(**kwargs)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/bokeh/core/has_props.py", line 268, in __init__
        setattr(self, name, value)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/bokeh/core/has_props.py", line 297, in __setattr__
        super().__setattr__(name, value)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/bokeh/core/property/descriptors.py", line 552, in __set__
        self._internal_set(obj, value, setter=setter)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/bokeh/core/property/descriptors.py", line 784, in _internal_set
        value = self.property.prepare_value(obj, self.name, value)
      File "/home/seshu/.virtualenvs/ami/lib/python3.7/site-packages/bokeh/core/property/bases.py", line 350, in prepare_value
        raise ValueError(f"failed to validate {obj_repr}.{name}: {error}")
    ValueError: failed to validate Figure(id='1021', ...).align: expected an element of either Enum('start', 'center', 'end') or Tuple(Enum('start', 'center', 'end'), Enum('start', 'center', 'end')), got None

It does not seem to like something about row.append(plot). What am I doing wrong?

1 Like

I believe this is a regression introduced recently. Could you file a bug on Github?

FYI. @philippjfr

Please provide some details on Panel, Bokeh and HoloViews versions you have installed. I can’t reproduce an issue with your example.

I realized my holoviews was a little old. I was on panel 0.11.1, bokeh 2.3.0, but holoviews 1.13.3.

It only worked on holoviews 1.13.3 when I added hv.extension() after pn.extension(). If I did hv.extension(‘bokeh’) I would get the error I saw above.
Upgrading to holoviews 1.14.2 it works with hv.extension(‘bokeh’) as well.

A slightly unrelated question, but if I want to update my plot from an asyncio task using pipe.send should I use panel.io.server.with_lock()?

I have something working right now, but I’m not sure if its safe. I dont really understand when you need to use either panel.io.server.unlocked() or panel.io.server.with_lock().

Hi @broken_symlink

The Panel documentation includes some (recent added) documentation on async. Does it help?

https://panel.holoviz.org/user_guide/Async_and_Concurrency.html

I guess using Pipe and Buffer is not considered modifying a Bokeh model directly so a lock is not needed?

1 Like

Hi @broken_symlink If you create some good examples feel very much free to share. There are not a lot of examples around. but I believe its something we as a community could learn a lot from. Thanks.

Here is an example I made to push data from the backend:

import tornado.ioloop
import asyncio
import numpy as np
import panel as pn
import holoviews as hv

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

class Plot():                                                                                                          
                                                                                                                       
    def __init__(self):                                                                                                
        self.pipe = hv.streams.Pipe(data=[])                                                                           
        self.plot = hv.DynamicMap(hv.Curve, streams=[self.pipe])                                                       
        self.row = pn.Row(self.plot)                                                                                   
                                                                                                                       
    async def run(self, loop):                                                                                         
        asyncio.create_task(self.update())                                                                             
        asyncio.create_task(self.start_server(loop))                                                                   
                                                                                                                       
    async def start_server(self, loop):                                                                                
        self.server = pn.serve(self.row, loop=loop)                                                                    
                                                                                                                       
    async def update(self):                                                                                            
        while True:                                                                                                    
            self.pipe.send((np.arange(10), np.random.randn(10)))                                                       
            await asyncio.sleep(1)                                                                                     


if __name__ == '__main__':                                                                                             
    loop = tornado.ioloop.IOLoop.current()                                                                             
    plot = Plot()                                                                                                      
    task = asyncio.ensure_future(plot.run(loop))                                                                       
    loop.start()

In my actual use case the update method is awaiting a zmq socket.

1 Like