This is because of the way you start your server, in this scenario for the Document
instance you store in __init__
is not the same as the actual current document when you start the server:
If I add these print statements in the _do_calc
method:
def _do_calc(self):
print(self.doc)
print(curdoc())
data = list(np.random.randint(0, 2 ** 31, 10))
cb = partial(self.update_source, data)
curdoc().add_next_tick_callback(cb)
Gives
<bokeh.document.document.Document object at 0x0000021D38068308>
<bokeh.document.document.Document object at 0x0000021D34E6AAC8>
This means that your periodic callback is added to the wrong Document
instance. The _do_calc
button press was working because in your example before you add callback you call curdoc()
and store it in self.doc
so you get the correct document instance.The _do_calc
method get executed in the main thread so this all works fine.
If you want to get this working you need to somehow get the callback on the correct Document
. The code below does that when you press the ‘add_cb’ button. This is probably not the solution you want, but I tried a few things and I couldn’t figure out a way to do it better. Basically what I think you need to to somehow add the callback after you start the io loop.
Also added is missing numpy import and call to super
import panel as pn
import param
from tornado.ioloop import IOLoop
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.io import curdoc
from functools import partial
import numpy as np
class Application(param.Parameterized):
do_calc = param.Action(lambda self: self._do_calc())
add_cb = param.Action(lambda self:self._add_cb())
def __init__(self, **params):
super(Application, self).__init__(**params)
self.doc = None
self.source = ColumnDataSource({"x": range(10), "y": range(10)})
self.figure = figure()
self.figure.line(x="x", y="y", source=self.source)
self.bk_pane = pn.pane.Bokeh(self.figure)
self.col = pn.Column(
pn.pane.Markdown("## Title"),
pn.Param(self, parameters=["do_calc", "add_cb"], show_name=False,),
self.bk_pane,
)
def update_source(self, data):
self.source.data.update({"y": data})
def _add_cb(self):
self.doc = curdoc()
self.doc.add_periodic_callback(self._do_calc, 200) # <- this line does not work properly
def _do_calc(self):
data = list(np.random.randint(0, 2 ** 31, 10))
cb = partial(self.update_source, data)
curdoc().add_next_tick_callback(cb)
def panel(self):
return self.col
app = Application()
loop = IOLoop().current()
server = pn.serve(app.panel(), show=False, loop=loop, start=False, port=5006)
# Do stuff
server.io_loop.start()