diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index 814f1ffe46..b37e499770 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -14,6 +14,7 @@ from panel.io.state import state from panel.callbacks import PeriodicCallback from pyviz_comms import JS_CALLBACK +from tornado import gen from ...core import OrderedDict from ...core.options import CallbackError @@ -339,6 +340,8 @@ def resolve_attr_spec(cls, spec, cb_obj, model=None): resolved = getattr(resolved, p, None) return {'id': model.ref['id'], 'value': resolved} + def _schedule_callback(self, cb): + PeriodicCallback(callback=cb, period=50, count=1).start() def on_change(self, attr, old, new): """ @@ -346,31 +349,21 @@ def on_change(self, attr, old, new): value change at once rather than firing off multiple plot updates. """ self._queue.append((attr, old, new)) - if self._batched and not all(b in [q[0] for q in self._queue] for b in self._batched): - return # Skip until all batched events have arrived - if not self._active and self.plot.document: self._active = True - if self.plot.document.session_context: - self.plot.document.add_timeout_callback(self.process_on_change, 50) - else: - PeriodicCallback(callback=self.process_on_change, period=50, count=1).start() - + self._schedule_callback(self.process_on_change) def on_event(self, event): """ Process bokeh UIEvents adding timeout to process multiple concerted value change at once rather than firing off multiple plot updates. """ - self._queue.append((event)) + self._queue.append(event) if not self._active and self.plot.document: self._active = True - if self.plot.document.session_context: - self.plot.document.add_timeout_callback(self.process_on_event, 50) - else: - PeriodicCallback(callback=self.process_on_event, period=50, count=1).start() - + self._schedule_callback(self.process_on_event) + @gen.coroutine def process_on_event(self): """ Trigger callback change event and triggering corresponding streams. @@ -390,17 +383,16 @@ def process_on_event(self): model_obj = self.plot_handles.get(self.models[0]) msg[attr] = self.resolve_attr_spec(path, event, model_obj) self.on_msg(msg) + self._schedule_callback(self.process_on_event) - if self.plot.document.session_context: - self.plot.document.add_timeout_callback(self.process_on_event, 50) - else: - PeriodicCallback(callback=self.process_on_event, period=50, count=1).start() - - + @gen.coroutine def process_on_change(self): if not self._queue: self._active = False return + elif self._batched and not all(b in [q[0] for q in self._queue] for b in self._batched): + self._schedule_callback(self.process_on_change) + return # Skip until all batched events have arrived self._queue = [] msg = {} @@ -422,10 +414,7 @@ def process_on_change(self): self.on_msg(msg) self._prev_msg = msg - if self.plot.document.session_context: - self.plot.document.add_timeout_callback(self.process_on_change, 50) - else: - PeriodicCallback(callback=self.process_on_change, period=100, count=1).start() + self._schedule_callback(self.process_on_change) def set_server_callback(self, handle): @@ -798,7 +787,7 @@ class RangeXYCallback(Callback): on_changes = ['start', 'end'] _batched = on_changes - + def _process_msg(self, msg): data = {} if 'x0' in msg and 'x1' in msg: diff --git a/holoviews/plotting/plot.py b/holoviews/plotting/plot.py index d81a8304a3..2a6c68c243 100644 --- a/holoviews/plotting/plot.py +++ b/holoviews/plotting/plot.py @@ -143,11 +143,11 @@ def pane(self, pane): if not plot.root: continue for cb in getattr(plot, 'callbacks', []): - if hasattr(pane, '_on_error') and cb.comm: + if hasattr(pane, '_on_error') and getattr(cb, 'comm', None): cb.comm._on_error = partial(pane._on_error, plot.root.ref['id']) elif self.root: for cb in getattr(self, 'callbacks', []): - if hasattr(pane, '_on_error') and cb.comm: + if hasattr(pane, '_on_error') and getattr(cb, 'comm', None): cb.comm._on_error = partial(pane._on_error, self.root.ref['id']) @property