Skip to content

Commit

Permalink
Fixed scrubber bug and add support for discrete DynamicMap scrubber (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Sep 13, 2017
1 parent 405be8f commit d671883
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 27 deletions.
6 changes: 5 additions & 1 deletion holoviews/core/spaces.py
Expand Up @@ -518,6 +518,10 @@ def __call__(self, *args, **kwargs):

try:
ret = self.callable(*args, **kwargs)
except KeyError:
# KeyError is caught separately because it is used to signal
# invalid keys on DynamicMap and should not warn
raise
except:
posstr = ', '.join(['%r' % el for el in self.args]) if self.args else ''
kwstr = ', '.join('%s=%r' % (k,v) for k,v in self.kwargs.items())
Expand Down Expand Up @@ -1055,7 +1059,7 @@ def _cache(self, key, val):
if len(self) >= cache_size:
first_key = next(k for k in self.data)
self.data.pop(first_key)
self.data[key] = val
self[key] = val


def map(self, map_fn, specs=None, clone=True):
Expand Down
7 changes: 6 additions & 1 deletion holoviews/core/traversal.py
Expand Up @@ -8,7 +8,7 @@
from operator import itemgetter

from .dimension import Dimension
from .util import merge_dimensions
from .util import merge_dimensions, cartesian_product

try:
import itertools.izip as zip
Expand Down Expand Up @@ -89,6 +89,11 @@ def unique_dimkeys(obj, default_dim='Frame'):
if not matches:
unique_keys.append(padded_key)

# Add cartesian product of DynamicMap values to keys
values = [d.values for d in all_dims]
if obj.traverse(lambda x: x, ['DynamicMap']) and values and all(values):
unique_keys += list(zip(*cartesian_product(values)))

with item_check(False):
sorted_keys = NdMapping({key: None for key in unique_keys},
kdims=all_dims).data.keys()
Expand Down
14 changes: 7 additions & 7 deletions holoviews/plotting/plot.py
Expand Up @@ -18,7 +18,7 @@
from ..core.options import Store, Compositor, SkipRendering
from ..core.overlay import NdOverlay
from ..core.spaces import HoloMap, DynamicMap
from ..core.util import stream_parameters
from ..core.util import stream_parameters, cartesian_product
from ..element import Table
from .util import (get_dynamic_mode, initialize_unbounded, dim_axis_label,
attach_streams, traverse_setter, get_nested_streams,
Expand Down Expand Up @@ -587,10 +587,15 @@ def __init__(self, element, keys=None, ranges=None, dimensions=None,
if self.batched and not isinstance(self, GenericOverlayPlot):
plot_element = plot_element.last

dynamic = isinstance(element, DynamicMap) and not element.unbounded
self.top_level = keys is None
if self.top_level:
dimensions = self.hmap.kdims
keys = list(self.hmap.data.keys())
values = [d.values for d in dimensions]
if dynamic and values and all(values):
keys = list(zip(*cartesian_product(values)))
else:
keys = list(self.hmap.data.keys())

self.style = self.lookup_options(plot_element, 'style') if style is None else style
plot_opts = self.lookup_options(plot_element, 'plot').options
Expand All @@ -600,7 +605,6 @@ def __init__(self, element, keys=None, ranges=None, dimensions=None,
defaults=False)
plot_opts.update(**{k: v[0] for k, v in inherited.items()})

dynamic = isinstance(element, DynamicMap) and not element.unbounded
super(GenericElementPlot, self).__init__(keys=keys, dimensions=dimensions,
dynamic=dynamic,
**dict(params, **plot_opts))
Expand Down Expand Up @@ -997,10 +1001,6 @@ def _get_frame(self, key):
return layout_frame


def __len__(self):
return len(self.keys)


def _format_title(self, key, dimensions=True, separator='\n'):
dim_title = self._frame_title(key, 3, separator) if dimensions else ''
layout = self.layout
Expand Down
7 changes: 4 additions & 3 deletions holoviews/plotting/renderer.py
Expand Up @@ -301,15 +301,16 @@ def get_widget(self_or_cls, plot, widget_type, **kwargs):
if not isinstance(plot, Plot):
plot = self_or_cls.get_plot(plot)
dynamic = plot.dynamic
# Whether dimensions define discrete space
discrete = all(d.values for d in plot.dimensions)
if widget_type == 'auto':
isuniform = plot.uniform
if not isuniform:
widget_type = 'scrubber'
else:
widget_type = 'widgets'
elif dynamic: widget_type = 'widgets'
elif widget_type == 'scrubber' and dynamic:
raise ValueError('DynamicMap do not support scrubber widget')
elif dynamic and not discrete:
widget_type = 'widgets'

if widget_type in [None, 'auto']:
holomap_formats = self_or_cls.mode_formats['holomap'][self_or_cls.mode]
Expand Down
19 changes: 4 additions & 15 deletions holoviews/plotting/widgets/widgets.js
Expand Up @@ -257,17 +257,7 @@ ScrubberWidget.prototype.get_loop_state = function(){


ScrubberWidget.prototype.next_frame = function() {
if (this.dynamic || !this.cached) {
if (this.wait) {
return
}
this.wait = true;
}
if (this.dynamic && this.current_frame + 1 >= this.length) {
this.length += 1;
document.getElementById(this.slider_id).max = this.length-1;
}
this.set_frame(Math.min(this.length - 1, this.current_frame + 1));
this.set_frame(Math.min(this.length - 1, this.current_frame + 1));
}

ScrubberWidget.prototype.previous_frame = function() {
Expand Down Expand Up @@ -295,7 +285,7 @@ ScrubberWidget.prototype.faster = function() {
}

ScrubberWidget.prototype.anim_step_forward = function() {
if(this.current_frame < this.length || (this.dynamic && !this.stopped)){
if(this.current_frame < this.length - 1){
this.next_frame();
}else{
var loop_state = this.get_loop_state();
Expand All @@ -312,9 +302,8 @@ ScrubberWidget.prototype.anim_step_forward = function() {
}

ScrubberWidget.prototype.anim_step_reverse = function() {
this.current_frame -= 1;
if(this.current_frame >= 0){
this.set_frame(this.current_frame);
if(this.current_frame > 0){
this.previous_frame();
} else {
var loop_state = this.get_loop_state();
if(loop_state == "loop"){
Expand Down
21 changes: 21 additions & 0 deletions tests/testtraversal.py
Expand Up @@ -27,6 +27,27 @@ def test_unique_keys_no_overlap_exception(self):
with self.assertRaisesRegexp(Exception, exception):
dims, keys = unique_dimkeys(hmap1+hmap2)

def test_unique_keys_dmap_complete_overlap(self):
hmap1 = DynamicMap(lambda x: Curve(range(10)), kdims=['x']).redim.values(x=[1, 2, 3])
hmap2 = DynamicMap(lambda x: Curve(range(10)), kdims=['x']).redim.values(x=[1, 2, 3])
dims, keys = unique_dimkeys(hmap1+hmap2)
self.assertEqual(hmap1.kdims, dims)
self.assertEqual(keys, [(i,) for i in range(1, 4)])

def test_unique_keys_dmap_partial_overlap(self):
hmap1 = DynamicMap(lambda x: Curve(range(10)), kdims=['x']).redim.values(x=[1, 2, 3])
hmap2 = DynamicMap(lambda x: Curve(range(10)), kdims=['x']).redim.values(x=[1, 2, 3, 4])
dims, keys = unique_dimkeys(hmap1+hmap2)
self.assertEqual(hmap2.kdims, dims)
self.assertEqual(keys, [(i,) for i in range(1, 5)])

def test_unique_keys_dmap_cartesian_product(self):
hmap1 = DynamicMap(lambda x, y: Curve(range(10)), kdims=['x', 'y']).redim.values(x=[1, 2, 3])
hmap2 = DynamicMap(lambda x, y: Curve(range(10)), kdims=['x', 'y']).redim.values(y=[1, 2, 3])
dims, keys = unique_dimkeys(hmap1+hmap2)
self.assertEqual(hmap1.kdims[:1]+hmap2.kdims[1:], dims)
self.assertEqual(keys, [(x, y) for x in range(1, 4) for y in range(1, 4)])

def test_unique_keys_no_overlap_dynamicmap_uninitialized(self):
dmap1 = DynamicMap(lambda A: Curve(range(10)), kdims=['A'])
dmap2 = DynamicMap(lambda B: Curve(range(10)), kdims=['B'])
Expand Down

0 comments on commit d671883

Please sign in to comment.