From 47ccfe1abeeb86e10da967f361e531fa84827a78 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 12 Sep 2016 19:42:45 +0100 Subject: [PATCH 1/5] Added utilities to handle streamed dimensions --- holoviews/core/util.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/holoviews/core/util.py b/holoviews/core/util.py index b2ecb27981..6d90dc3e79 100644 --- a/holoviews/core/util.py +++ b/holoviews/core/util.py @@ -803,15 +803,23 @@ def dimensionless_contents(streams, kdims): with any of the key dimensions. """ names = stream_parameters(streams) - kdim_names = [kdim.name for kdim in kdims] - return [name for name in names if name not in kdim_names] + return [name for name in names if name not in kdims] + + +def streamless_dimensions(streams, kdims): + """ + Return a list of dimensions that have not been associated with + any streams. + """ + params = stream_parameters(streams) + return [d for d in kdims if d not in params] def wrap_tuple_streams(unwrapped, kdims, streams): """ Fills in tuple keys with dimensioned stream values as appropriate. """ - param_groups = [(s.params().keys(), s) for s in streams] + param_groups = [(s.contents.keys(), s) for s in streams] pairs = [(name,s) for (group, s) in param_groups for name in group] substituted = [] for pos,el in enumerate(wrap_tuple(unwrapped)): @@ -824,6 +832,16 @@ def wrap_tuple_streams(unwrapped, kdims, streams): return tuple(substituted) +def drop_streams(streams, keys, kdims): + """ + Drop any dimensionsed streams from the keys and kdims. + """ + stream_params = stream_parameters(streams) + inds, dims = zip(*[(ind, kdim) for ind, kdim in enumerate(kdims) + if kdim not in stream_params]) + return dims, [tuple(key[ind] for ind in inds) for key in keys] + + def itervalues(obj): "Get value iterator from dictionary for Python 2 and 3" return iter(obj.values()) if sys.version_info.major == 3 else obj.itervalues() From 11fda8bcd2ffa30d3e8858d63b547c9103f1a268 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 12 Sep 2016 19:46:27 +0100 Subject: [PATCH 2/5] Widgets now ignore streamed dimensions --- holoviews/plotting/bokeh/widgets.py | 4 ---- holoviews/plotting/mpl/widgets.py | 9 --------- holoviews/plotting/plot.py | 12 ++++++++---- holoviews/plotting/renderer.py | 7 ++++--- holoviews/plotting/widgets/__init__.py | 20 ++++++++++++++------ 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/holoviews/plotting/bokeh/widgets.py b/holoviews/plotting/bokeh/widgets.py index 0433ec4018..345710be1e 100644 --- a/holoviews/plotting/bokeh/widgets.py +++ b/holoviews/plotting/bokeh/widgets.py @@ -43,10 +43,6 @@ def _plot_figure(self, idx, fig_format='json'): msg = dict(patch=json_patch, root=self.plot.state._id) msg = serialize_json(msg) return msg - else: - self.plot.push() - return "Complete" - class BokehSelectionWidget(BokehWidget, SelectionWidget): pass diff --git a/holoviews/plotting/mpl/widgets.py b/holoviews/plotting/mpl/widgets.py index 6f26a4cb1c..07a48a6869 100644 --- a/holoviews/plotting/mpl/widgets.py +++ b/holoviews/plotting/mpl/widgets.py @@ -32,15 +32,6 @@ def _plot_figure(self, idx): return self.renderer.html(self.plot, figure_format, comm=False) - def update(self, key): - if self.plot.dynamic == 'bounded' and not isinstance(key, int): - key = tuple(dim.values[k] if dim.values else k - for dim, k in zip(self.mock_obj.kdims, tuple(key))) - self.plot[key] - self.plot.push() - return '' if self.renderer.mode == 'nbagg' else 'Complete' - - def get_frames(self): if self.renderer.mode == 'nbagg': manager = self.plot.comm.get_figure_manager() diff --git a/holoviews/plotting/plot.py b/holoviews/plotting/plot.py index 2e4fd3c73e..22678f6e01 100644 --- a/holoviews/plotting/plot.py +++ b/holoviews/plotting/plot.py @@ -484,10 +484,9 @@ def refresh(self, **kwargs): the updated data if the plot has an associated Comm. """ traverse_setter(self, '_force', True) - if self.current_key: - self.update(self.current_key) - else: - self.update(0) + key = self.current_key if self.current_key else self.keys[0] + stream_key = util.wrap_tuple_streams(key, self.dimensions, self.streams) + self.update(stream_key) if self.comm is not None: self.push() @@ -573,6 +572,8 @@ def __init__(self, element, keys=None, ranges=None, dimensions=None, **dict(params, **plot_opts)) if top_level: self.comm = self.init_comm(element) + if isinstance(self.hmap, DynamicMap): + self.streams = self.hmap.streams # Update plot and style options for batched plots if self.batched: @@ -920,6 +921,9 @@ def __init__(self, layout, keys=None, dimensions=None, **params): **params) if top_level: self.comm = self.init_comm(layout) + self.streams = [s for streams in layout.traverse(lambda x: x.streams, + [DynamicMap]) + for s in streams] def _get_frame(self, key): diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py index b2e9884e44..1e4c4856ec 100644 --- a/holoviews/plotting/renderer.py +++ b/holoviews/plotting/renderer.py @@ -11,7 +11,7 @@ import param from ..core.io import Exporter from ..core.options import Store, StoreOptions, SkipRendering -from ..core.util import find_file, unicode +from ..core.util import find_file, unicode, streamless_dimensions from .. import Layout, HoloMap, AdjointLayout from .widgets import NdWidget, ScrubberWidget, SelectionWidget @@ -201,9 +201,10 @@ def _validate(self, obj, fmt): holomap_formats = self.mode_formats['holomap'][self.mode] if fmt in ['auto', None]: - if ((len(plot) == 1 and not plot.dynamic) + if (((len(plot) == 1 and not plot.dynamic) or (len(plot) > 1 and self.holomap is None) or - (plot.dynamic and len(plot.keys[0]) == 0)): + (plot.dynamic and len(plot.keys[0]) == 0)) or + not streamless_dimensions(plot.streams, plot.dimensions)): fmt = fig_formats[0] if self.fig=='auto' else self.fig else: fmt = holomap_formats[0] if self.holomap=='auto' else self.holomap diff --git a/holoviews/plotting/widgets/__init__.py b/holoviews/plotting/widgets/__init__.py index c04755dffd..d1093cde46 100644 --- a/holoviews/plotting/widgets/__init__.py +++ b/holoviews/plotting/widgets/__init__.py @@ -8,7 +8,8 @@ from ...core import OrderedDict, NdMapping from ...core.options import Store from ...core.util import (dimension_sanitizer, safe_unicode, - unique_array, unicode, isnumeric) + unique_array, unicode, isnumeric, + wrap_tuple_streams, drop_streams) from ...core.traversal import hierarchical def escape_vals(vals, escape_numerics=True): @@ -106,9 +107,8 @@ def __init__(self, plot, renderer=None, **params): super(NdWidget, self).__init__(**params) self.id = plot.comm.target if plot.comm else uuid.uuid4().hex self.plot = plot - self.dimensions = plot.dimensions - self.keys = plot.keys - + dims, keys = drop_streams(drop_streams, plot.keys, plot.dimensions) + self.dimensions, self.keys = dims, keys self.json_data = {} if self.plot.dynamic: self.embed = False if renderer is None: @@ -194,7 +194,9 @@ def _plot_figure(self, idx): def update(self, key): - return self._plot_figure(key) + self.plot.update(key) + self.plot.push() + return 'Complete' @@ -370,4 +372,10 @@ def update(self, key): if self.plot.dynamic: key = tuple(dim.values[k] if dim.values else k for dim, k in zip(self.mock_obj.kdims, tuple(key))) - return self._plot_figure(key) + key = [key[self.dimensions.index(kdim)] if kdim in self.dimensions else None + for kdim in self.plot.dimensions] + key = wrap_tuple_streams(tuple(key), self.plot.dimensions, + self.plot.streams) + self.plot.update(key) + self.plot.push() + return 'Complete' From 75e63ccced713ce422a01a87ccbaa50ac1115324 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 12 Sep 2016 19:54:19 +0100 Subject: [PATCH 3/5] Small fix when defining Plot.streams --- holoviews/plotting/plot.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/holoviews/plotting/plot.py b/holoviews/plotting/plot.py index 22678f6e01..57e80bbdcb 100644 --- a/holoviews/plotting/plot.py +++ b/holoviews/plotting/plot.py @@ -572,8 +572,7 @@ def __init__(self, element, keys=None, ranges=None, dimensions=None, **dict(params, **plot_opts)) if top_level: self.comm = self.init_comm(element) - if isinstance(self.hmap, DynamicMap): - self.streams = self.hmap.streams + self.streams = self.hmap.streams if isinstance(self.hmap, DynamicMap) else [] # Update plot and style options for batched plots if self.batched: From 14e609b9e5ff0e5f2cbd1e54cdd1d4c65d4afdd6 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 12 Sep 2016 20:08:25 +0100 Subject: [PATCH 4/5] Fixed bug in widgets --- holoviews/plotting/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/holoviews/plotting/widgets/__init__.py b/holoviews/plotting/widgets/__init__.py index d1093cde46..b79226543a 100644 --- a/holoviews/plotting/widgets/__init__.py +++ b/holoviews/plotting/widgets/__init__.py @@ -107,7 +107,7 @@ def __init__(self, plot, renderer=None, **params): super(NdWidget, self).__init__(**params) self.id = plot.comm.target if plot.comm else uuid.uuid4().hex self.plot = plot - dims, keys = drop_streams(drop_streams, plot.keys, plot.dimensions) + dims, keys = drop_streams(plot.streams, plot.keys, plot.dimensions) self.dimensions, self.keys = dims, keys self.json_data = {} if self.plot.dynamic: self.embed = False From 51bb81d7868dde4cc99749ec05a697e67dc80a8d Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 12 Sep 2016 21:07:51 +0100 Subject: [PATCH 5/5] Minor cleanup --- holoviews/core/util.py | 4 ++-- holoviews/plotting/renderer.py | 4 ++-- holoviews/plotting/widgets/__init__.py | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/holoviews/core/util.py b/holoviews/core/util.py index 6d90dc3e79..040f3bc625 100644 --- a/holoviews/core/util.py +++ b/holoviews/core/util.py @@ -806,7 +806,7 @@ def dimensionless_contents(streams, kdims): return [name for name in names if name not in kdims] -def streamless_dimensions(streams, kdims): +def unbound_dimensions(streams, kdims): """ Return a list of dimensions that have not been associated with any streams. @@ -832,7 +832,7 @@ def wrap_tuple_streams(unwrapped, kdims, streams): return tuple(substituted) -def drop_streams(streams, keys, kdims): +def drop_streams(streams, kdims, keys): """ Drop any dimensionsed streams from the keys and kdims. """ diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py index 1e4c4856ec..860e98d306 100644 --- a/holoviews/plotting/renderer.py +++ b/holoviews/plotting/renderer.py @@ -11,7 +11,7 @@ import param from ..core.io import Exporter from ..core.options import Store, StoreOptions, SkipRendering -from ..core.util import find_file, unicode, streamless_dimensions +from ..core.util import find_file, unicode, unbound_dimensions from .. import Layout, HoloMap, AdjointLayout from .widgets import NdWidget, ScrubberWidget, SelectionWidget @@ -204,7 +204,7 @@ def _validate(self, obj, fmt): if (((len(plot) == 1 and not plot.dynamic) or (len(plot) > 1 and self.holomap is None) or (plot.dynamic and len(plot.keys[0]) == 0)) or - not streamless_dimensions(plot.streams, plot.dimensions)): + not unbound_dimensions(plot.streams, plot.dimensions)): fmt = fig_formats[0] if self.fig=='auto' else self.fig else: fmt = holomap_formats[0] if self.holomap=='auto' else self.holomap diff --git a/holoviews/plotting/widgets/__init__.py b/holoviews/plotting/widgets/__init__.py index b79226543a..4a903de91c 100644 --- a/holoviews/plotting/widgets/__init__.py +++ b/holoviews/plotting/widgets/__init__.py @@ -107,8 +107,10 @@ def __init__(self, plot, renderer=None, **params): super(NdWidget, self).__init__(**params) self.id = plot.comm.target if plot.comm else uuid.uuid4().hex self.plot = plot - dims, keys = drop_streams(plot.streams, plot.keys, plot.dimensions) - self.dimensions, self.keys = dims, keys + self.dimensions, self.keys = drop_streams(plot.streams, + plot.dimensions, + plot.keys) + self.json_data = {} if self.plot.dynamic: self.embed = False if renderer is None: