From 21fec7f75199a1c36932dfd25411462dad3d286d Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Fri, 13 Nov 2020 17:46:11 -0500 Subject: [PATCH 1/3] Support link selections on dynamic overlays (e.g. tiles * datashade(...)) --- holoviews/selection.py | 5 +++ holoviews/tests/testselection.py | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/holoviews/selection.py b/holoviews/selection.py index 1eb77d9bb0..c92a9d090c 100644 --- a/holoviews/selection.py +++ b/holoviews/selection.py @@ -164,6 +164,11 @@ def _selection_transform(self, hvobj, operations=()): self._selection_streams, hvobj, operations, self._selection_expr_streams.get(hvobj, None), cache=self._cache ) + elif issubclass(hvobj.type, Overlay): + return Overlay([ + self._selection_transform(el, operations=operations) + for el in callback.inputs + ]).collate() else: # This is a DynamicMap that we don't know how to recurse into. return hvobj diff --git a/holoviews/tests/testselection.py b/holoviews/tests/testselection.py index 33354463d9..68109a4059 100644 --- a/holoviews/tests/testselection.py +++ b/holoviews/tests/testselection.py @@ -278,6 +278,71 @@ def test_datashade_selection(self): )[()] ) + @ds_skip + def test_datashade_in_overlay_selection(self): + points = Points(self.data) + layout = points * dynspread(datashade(points)) + + lnk_sel = link_selections.instance(unselected_color='#ff0000') + linked = lnk_sel(layout) + current_obj = linked[()] + + # Check base points layer + self.check_base_points_like(current_obj[()].Points.I, lnk_sel) + + # Check selection layer + self.check_overlay_points_like(current_obj[()].Points.II, lnk_sel, self.data) + + # Check RGB base layer + self.assertEqual( + current_obj[()].RGB.I, + dynspread( + datashade(points, cmap=lnk_sel.unselected_cmap, alpha=255) + )[()] + ) + + # Check RGB selection layer + self.assertEqual( + current_obj[()].RGB.II, + dynspread( + datashade(points, cmap=lnk_sel.selected_cmap, alpha=255) + )[()] + ) + + # Perform selection of second and third point + selectionxy = TestLinkSelections.get_value_with_key_type( + lnk_sel._selection_expr_streams, hv.Points + ).input_streams[0].input_stream.input_streams[0] + + self.assertIsInstance(selectionxy, SelectionXY) + selectionxy.event(bounds=(0, 1, 5, 5)) + current_obj = linked[()] + + # Check that base points layer is unchanged + self.check_base_points_like(current_obj[()].Points.I, lnk_sel) + + # Check points selection layer + self.check_overlay_points_like(current_obj[()].Points.II, lnk_sel, + self.data.iloc[1:]) + + # Check that base RGB layer is unchanged + self.assertEqual( + current_obj[()].RGB.I, + dynspread( + datashade(points, cmap=lnk_sel.unselected_cmap, alpha=255) + )[()] + ) + + # Check selection RGB layer + self.assertEqual( + current_obj[()].RGB.II, + dynspread( + datashade( + points.iloc[1:], cmap=lnk_sel.selected_cmap, alpha=255 + ) + )[()] + ) + def test_points_selection_streaming(self): buffer = hv.streams.Buffer(self.data.iloc[:2], index=False) points = hv.DynamicMap(Points, streams=[buffer]) From f5df1434a2ab06e50f0eae877667e756d3ce28dc Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Fri, 13 Nov 2020 17:46:55 -0500 Subject: [PATCH 2/3] More robust None handling in SelectionExprSequence. --- holoviews/streams.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/holoviews/streams.py b/holoviews/streams.py index f44f854410..f303344898 100644 --- a/holoviews/streams.py +++ b/holoviews/streams.py @@ -1020,7 +1020,7 @@ def transform_function(cls, stream_values, constants): return selection_expr, bbox, region_element = selection - + if selection_expr is not None: break @@ -1107,6 +1107,8 @@ def transform_function(cls, stream_values, constants): combined_region_element = None for selection_contents in stream_values[0]["values"]: + if selection_contents is None: + continue selection_expr = selection_contents['selection_expr'] if not selection_expr: continue From f729df7fc6782e8dea7ab536676f44a33927a38d Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Fri, 13 Nov 2020 17:52:54 -0500 Subject: [PATCH 3/3] Be more conservative and only perform transformations on layers combined by the `dynamic_mul` callback. --- holoviews/selection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/holoviews/selection.py b/holoviews/selection.py index c92a9d090c..2e58903034 100644 --- a/holoviews/selection.py +++ b/holoviews/selection.py @@ -164,7 +164,8 @@ def _selection_transform(self, hvobj, operations=()): self._selection_streams, hvobj, operations, self._selection_expr_streams.get(hvobj, None), cache=self._cache ) - elif issubclass(hvobj.type, Overlay): + elif (issubclass(hvobj.type, Overlay) and + getattr(hvobj.callback, "name", None) == "dynamic_mul"): return Overlay([ self._selection_transform(el, operations=operations) for el in callback.inputs