Skip to content

Commit

Permalink
Fix linking elements that are transformed by a Compositor (#6003)
Browse files Browse the repository at this point in the history
  • Loading branch information
maximlt authored Dec 12, 2023
1 parent 5308bb5 commit e23c3e9
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 8 deletions.
2 changes: 2 additions & 0 deletions holoviews/plotting/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,8 @@ def hex2rgb(hex):

class apply_nodata(Operation):

link_inputs = param.Boolean(default=True)

nodata = param.Integer(default=None, doc="""
Optional missing-data value for integer data.
If non-None, data with this value will be replaced with NaN so
Expand Down
39 changes: 34 additions & 5 deletions holoviews/tests/plotting/bokeh/test_links.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import numpy as np
import pytest
from bokeh.models import ColumnDataSource
from bokeh.models import ColumnDataSource, RangeTool

from holoviews.core.spaces import DynamicMap
from holoviews.element import Curve, Image, Path, Points, Polygons, Scatter, Table
Expand All @@ -12,7 +12,6 @@
class TestLinkCallbacks(TestBokehPlot):

def test_range_tool_link_callback_single_axis(self):
from bokeh.models import RangeTool
array = np.random.rand(100, 2)
src = Curve(array)
target = Scatter(array)
Expand All @@ -26,7 +25,6 @@ def test_range_tool_link_callback_single_axis(self):
self.assertIs(range_tool.y_range, None)

def test_range_tool_link_callback_single_axis_overlay_target(self):
from bokeh.models import RangeTool
array = np.random.rand(100, 2)
src = Curve(array)
target = Scatter(array, label='a') * Scatter(array, label='b')
Expand All @@ -40,7 +38,6 @@ def test_range_tool_link_callback_single_axis_overlay_target(self):
self.assertIs(range_tool.y_range, None)

def test_range_tool_link_callback_single_axis_overlay_target_image_source(self):
from bokeh.models import RangeTool
data = np.random.rand(50, 50)
target = Curve(data) * Curve(data)
source = Image(np.random.rand(50, 50), bounds=(0, 0, 1, 1))
Expand All @@ -53,8 +50,40 @@ def test_range_tool_link_callback_single_axis_overlay_target_image_source(self):
self.assertEqual(range_tool.x_range, tgt_plot.handles['x_range'])
self.assertIs(range_tool.y_range, None)

def test_range_tool_link_callback_single_axis_curve_target_image_dmap_source(self):
# Choosing Image to exert the apply_nodata compositor
src = DynamicMap(
lambda a: Image(a*np.random.random((20, 20)), bounds=[0, 0, 9, 9]),
kdims=['a']
).redim.range(a=(0.1,1))
target = Curve(np.arange(10))
RangeToolLink(src, target)
layout = target + src
plot = bokeh_renderer.get_plot(layout)
tgt_plot = plot.subplots[(0, 0)].subplots['main']
src_plot = plot.subplots[(0, 1)].subplots['main']
range_tool = src_plot.state.select_one({'type': RangeTool})
assert range_tool.x_range == tgt_plot.handles['x_range']
assert range_tool.y_range is None

def test_range_tool_link_callback_single_axis_overlay_target_image_dmap_source(self):
# Choosing Image to exert the apply_nodata compositor
src = DynamicMap(
lambda a: Image(a*np.random.random((20, 20)), bounds=[0, 0, 9, 9]),
kdims=['a']
).redim.range(a=(0.1,1))
data = np.random.rand(50, 50)
target = Curve(data) * Curve(data)
RangeToolLink(src, target)
layout = target + src
plot = bokeh_renderer.get_plot(layout)
tgt_plot = plot.subplots[(0, 0)].subplots['main']
src_plot = plot.subplots[(0, 1)].subplots['main']
range_tool = src_plot.state.select_one({'type': RangeTool})
assert range_tool.x_range == tgt_plot.handles['x_range']
assert range_tool.y_range is None

def test_range_tool_link_callback_both_axes(self):
from bokeh.models import RangeTool
array = np.random.rand(100, 2)
src = Curve(array)
target = Scatter(array)
Expand Down
10 changes: 7 additions & 3 deletions holoviews/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@ class Dynamic(param.ParameterizedFunction):

link_inputs = param.Boolean(default=True, doc="""
If Dynamic is applied to another DynamicMap, determines whether
linked streams attached to its Callable inputs are
linked streams and links attached to its Callable inputs are
transferred to the output of the utility.
For example if the Dynamic utility is applied to a DynamicMap
Expand Down Expand Up @@ -900,8 +900,12 @@ def __call__(self, map_obj, **params):
callback = self._dynamic_operation(map_obj)
streams = self._get_streams(map_obj, watch)
if isinstance(map_obj, DynamicMap):
dmap = map_obj.clone(callback=callback, shared_data=self.p.shared_data,
streams=streams)
kwargs = dict(
shared_data=self.p.shared_data, callback=callback, streams=streams
)
if self.p.link_inputs:
kwargs['plot_id'] = map_obj._plot_id
dmap = map_obj.clone(**kwargs)
if self.p.shared_data:
dmap.data = dict([(k, callback.callable(*k))
for k, v in dmap.data])
Expand Down

0 comments on commit e23c3e9

Please sign in to comment.