Skip to content

Commit

Permalink
Implement PeriodicCallback (#348)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr authored Apr 2, 2019
1 parent 7bf4b9f commit 00e3227
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
76 changes: 76 additions & 0 deletions panel/callbacks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
Defines callbacks to be executed on a thread or by scheduling it
on a running bokeh server.
"""
from __future__ import absolute_import, division, unicode_literals


import time
import param

from bokeh.io import curdoc as _curdoc


class PeriodicCallback(param.Parameterized):
"""
Periodic encapsulates a periodic callback which will run both
in tornado based notebook environments and on bokeh server. By
default the callback will run until the stop method is called,
but count and timeout values can be set to limit the number of
executions or the maximum length of time for which the callback
will run.
"""

callback = param.Callable(doc="""
The callback to execute periodically.""")

count = param.Integer(default=None, doc="""
Number of times the callback will be executed, by default
this is unlimited.""")

period = param.Integer(default=500, doc="""
Period in milliseconds at which the callback is executed.""")

timeout = param.Integer(default=None, doc="""
Timeout in seconds from the start time at which the callback
expires""")

def __init__(self, **params):
super(PeriodicCallback, self).__init__(**params)
self._counter = 0
self._start_time = None
self._timeout = None
self._cb = None
self._doc = None

def start(self):
if self._cb is not None:
raise RuntimeError('Periodic callback has already started.')
self._start_time = time.time()
if _curdoc().session_context:
self._doc = _curdoc()
self._cb = self._doc.add_periodic_callback(self._periodic_callback, self.period)
else:
from tornado.ioloop import PeriodicCallback
self._cb = PeriodicCallback(self._periodic_callback, self.period)
self._cb.start()

def _periodic_callback(self):
self.callback()
self._counter += 1
if self._timeout is not None:
dt = (time.time() - self._start_time)
if dt > self._timeout:
self.stop()
if self._counter == self.count:
self.stop()

def stop(self):
self._counter = 0
self._timeout = None
if self._doc:
self._doc.remove_periodic_callback(self._cb)
else:
self._cb.stop()
self._cb = None

31 changes: 31 additions & 0 deletions panel/viewable.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from bokeh.models import CustomJS
from pyviz_comms import JupyterCommManager

from .callbacks import PeriodicCallback
from .config import config, panel_extension
from .io.embed import embed_state
from .io.model import add_to_doc
Expand Down Expand Up @@ -753,6 +754,36 @@ def link(*events):
self._callbacks.append(cb)
return cb

def add_periodic_callback(self, callback, period=500, count=None,
timeout=None, start=True):
"""
Schedules a periodic callback to be run at an interval set by
the period. Returns a PeriodicCallback object with the option
to stop and start the callback.
Arguments
---------
callback: callable
Callable function to be executed at periodic interval.
period: int
Interval in milliseconds at which callback will be executed.
count: int
Maximum number of times callback will be invoked.
timeout: int
Timeout in seconds when the callback should be stopped.
start: boolean (default=True)
Whether to start callback immediately.
Returns
-------
Return a PeriodicCallback object with start and stop methods.
"""
cb = PeriodicCallback(callback=callback, period=period,
count=count, timeout=timeout)
if start:
cb.start()
return cb

def jslink(self, target, code=None, **links):
"""
Links properties on the source object to those on the target
Expand Down

0 comments on commit 00e3227

Please sign in to comment.