-
-
Notifications
You must be signed in to change notification settings - Fork 404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-blocking periodic_events method on DynamicMap #1429
Changes from 12 commits
09a9192
39e6e24
640baf0
c51c275
987af8e
40562a1
4f35932
2430cba
dcc9cb4
16e41e2
ce138a1
b28d134
1e7c73c
cadac5b
95537cc
3bd2bbd
395717a
fa983f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,8 @@ | |
from .options import Store, StoreOptions | ||
from ..streams import Stream | ||
|
||
|
||
|
||
class HoloMap(UniformNdMapping, Overlayable): | ||
""" | ||
A HoloMap can hold any number of DataLayers indexed by a list of | ||
|
@@ -557,18 +559,23 @@ def __call__(self): | |
raise | ||
|
||
|
||
def get_nested_dmaps(dmap): | ||
""" | ||
Get all DynamicMaps referenced by the supplied DynamicMap's callback. | ||
""" | ||
dmaps = [dmap] | ||
for o in dmap.callback.inputs: | ||
if isinstance(o, DynamicMap): | ||
dmaps.extend(get_nested_dmaps(o)) | ||
return list(set(dmaps)) | ||
|
||
|
||
def get_nested_streams(dmap): | ||
""" | ||
Get all (potentially nested) streams from DynamicMap with Callable | ||
callback. | ||
""" | ||
layer_streams = list(dmap.streams) | ||
if not isinstance(dmap.callback, Callable): | ||
return list(set(layer_streams)) | ||
for o in dmap.callback.inputs: | ||
if isinstance(o, DynamicMap): | ||
layer_streams += get_nested_streams(o) | ||
return list(set(layer_streams)) | ||
return list({s for dmap in get_nested_dmaps(dmap) for s in dmap.streams}) | ||
|
||
|
||
@contextmanager | ||
|
@@ -589,6 +596,53 @@ def dynamicmap_memoization(callable_obj, streams): | |
callable_obj.memoize = memoization_state | ||
|
||
|
||
|
||
class periodic(object): | ||
""" | ||
Implements the utility of the same name on DynamicMap. | ||
|
||
Used to defined periodic event updates that can be started and | ||
stopped. | ||
""" | ||
_periodic_util = util.periodic | ||
|
||
def __init__(self, dmap): | ||
self.dmap = dmap | ||
self.instance = None | ||
|
||
def __call__(self, period, count, param_fn=None): | ||
""" | ||
Run a non-blocking loop that updates the stream parameters using | ||
the event method. Runs count times with the specified period. If | ||
count is None, runs indefinitely. | ||
|
||
If param_fn is not specified, the event method is called without | ||
arguments. If it is specified, it must be a callable accepting a | ||
single argument (the iteration count) that returns a dictionary | ||
of the new stream values to be passed to the event method. | ||
""" | ||
|
||
if self.instance is not None and not self.instance.completed: | ||
raise RuntimeError('Periodic process already running. ' | ||
'Wait until it completes or call ' | ||
'stop() before running a new periodic process') | ||
def inner(i): | ||
kwargs = {} if param_fn is None else param_fn(i) | ||
self.dmap.event(**kwargs) | ||
|
||
instance = self._periodic_util(period, count, inner) | ||
instance.start() | ||
self.instance= instance | ||
|
||
def stop(self): | ||
"Stop the periodic process." | ||
self.instance.stop() | ||
|
||
def __str__(self): | ||
return "<holoviews.core.spaces.periodic method>" | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a comment explaining what this representation is meant to achieve? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you have another idea of what the repr should be, I would be happy to implement it! What sort of comment are you thinking of? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just something to indicate why the default representation was not suitable. I.e. why was this method necessary? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It isn't strictly necessary, but I think it is a bit nicer than the default which is something like:
Instead of a random memory address, I say that it is a method which reflects how this utility is used (i.e it lives on Happy to remove it if you think the default is better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to remove it, just put a docstring saying what you say here (that this repr provides additional information that should be more useful). |
||
|
||
|
||
class DynamicMap(HoloMap): | ||
""" | ||
A DynamicMap is a type of HoloMap where the elements are dynamically | ||
|
@@ -661,6 +715,7 @@ def __init__(self, callback, initial_items=None, **params): | |
if stream.source is None: | ||
stream.source = self | ||
self.redim = redim(self, mode='dynamic') | ||
self.periodic = periodic(self) | ||
|
||
@property | ||
def unbounded(self): | ||
|
@@ -754,7 +809,6 @@ def event(self, **kwargs): | |
|
||
Stream.trigger(self.streams) | ||
|
||
|
||
def _style(self, retval): | ||
""" | ||
Use any applicable OptionTree of the DynamicMap to apply options | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,9 +16,10 @@ | |
|
||
from ...core import Store, HoloMap | ||
from ..comms import JupyterComm, Comm | ||
from ..plot import GenericElementPlot | ||
from ..renderer import Renderer, MIME_TYPES | ||
from .widgets import BokehScrubberWidget, BokehSelectionWidget, BokehServerWidgets | ||
from .util import compute_static_patch, serialize_json | ||
from .util import compute_static_patch, serialize_json, attach_periodic | ||
|
||
|
||
|
||
|
@@ -122,9 +123,11 @@ def server_doc(self, plot, doc=None): | |
if doc is None: | ||
doc = curdoc() | ||
if isinstance(plot, BokehServerWidgets): | ||
plot.plot.document = doc | ||
else: | ||
plot.document = doc | ||
plot = plot.plot | ||
plot.document = doc | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a comment as a reminder of what |
||
plot.traverse(lambda x: attach_periodic(plot), | ||
[GenericElementPlot]) | ||
|
||
doc.add_root(plot.state) | ||
return doc | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to refactor it this way.