Skip to content
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

Panel does not update hv.Image alpha when using .grid() and dynamic maps #3945

Closed
pollackscience opened this issue Sep 5, 2019 · 7 comments
Labels
type: bug Something isn't correct or isn't working
Milestone

Comments

@pollackscience
Copy link

Hi All,

I'm using holoviews 1.12.5 and panel 0.6.2 on linux to produce this bug.

When using a panel slider widget to change the alpha of an overlay image, the active image does not change alpha if it is a dynamic map within a GridSpace. If the images are not dynamic, or are not using .grid(), the slider acts as expected. Here is a code segment that should reproduce this issue:

import xarray as xr
import holoviews as hv
import panel as pn
hv.extension('bokeh')

image = np.random.randint(0, 256, size=(2,3,10,100,100))
mask = np.random.randint(0, 256, size=(2,3,10,100,100))

# make xarray dataset to hold images
ds = xr.Dataset({'image':(['subject', 'sequence', 'z', 'x', 'y'], image),
                 'mask':(['subject', 'sequence', 'z', 'x', 'y'], mask)},
                coords={'subject': ['a', 'b'],
                       'sequence': ['1', '2', '3'],
                       'x': list(range(100)),
                       'y': list(range(100)),
                       'z': list(range(10))}
               )

# make slider
slider = pn.widgets.FloatSlider(start=0, end=1, value=0.5, name='mask_slider')

# make hv datasets and images
hv_ds = hv.Dataset(ds)
hv_ds_image = hv_ds.to(hv.Image, kdims=['x', 'y'], vdims='image', dynamic=True).opts(cmap='Blues')
hv_ds_mask= hv_ds.to(hv.Image, kdims=['x', 'y'], vdims='mask', dynamic=True).opts(cmap='Reds').apply.opts(alpha=slider.param.value)

# make grid on 'sequence' coord, implicit groupby for 'z' and 'subject'
grid = (hv_ds_image*hv_ds_mask).grid('sequence')

#return the panel Column
pn.Column(slider, grid)

image

Changing the slider does not affect the current active images, but will affect any images that have not yet been viewed. Once they are viewed, they are "locked in" to that value of alpha and will no longer change.

@philippjfr philippjfr added the type: bug Something isn't correct or isn't working label Sep 22, 2019
@philippjfr
Copy link
Member

Thanks for this, looks like something happens during the overlay operation.

@philippjfr
Copy link
Member

Seems to effectively be a duplicate of #1704

@pollackscience
Copy link
Author

No problem, I've found this general workflow to be extremely useful for visualizing multi-faceted image data. If there's anything else I can do to help pin down the underlying issues, let me know.

@philippjfr
Copy link
Member

The issue is now fixed.

@philippjfr philippjfr added this to the v1.12.6 milestone Oct 3, 2019
@pollackscience
Copy link
Author

pollackscience commented Oct 9, 2019

Not sure if I should open a new issue or piggy back off this one:

After updating to 1.12.6, a new issue arrises using the example in my initial comment:
The call to .grid('sequence') causes the following (pretty long) error:

TypeError                                 Traceback (most recent call last)
~/conda_envs/new_mre/lib/python3.6/site-packages/IPython/core/formatters.py in __call__(self, obj, include, exclude)
    968 
    969             if method is not None:
--> 970                 return method(include=include, exclude=exclude)
    971             return None
    972         else:

~/conda_envs/new_mre/lib/python3.6/site-packages/panel/viewable.py in _repr_mimebundle_(self, include, exclude)
    292         comm = state._comm_manager.get_server_comm()
    293         doc = _Document()
--> 294         model = self._render_model(doc, comm)
    295         if config.embed:
    296             return render_model(model)

~/conda_envs/new_mre/lib/python3.6/site-packages/panel/viewable.py in _render_model(self, doc, comm)
    261         if comm is None:
    262             comm = state._comm_manager.get_server_comm()
--> 263         model = self.get_root(doc, comm)
    264 
    265         if config.embed:

~/conda_envs/new_mre/lib/python3.6/site-packages/panel/viewable.py in get_root(self, doc, comm)
    417         doc = doc or _curdoc()
--> 418         root = self._get_model(doc, comm=comm)
    419         self._preprocess(root)
    420         ref = root.ref['id']

~/conda_envs/new_mre/lib/python3.6/site-packages/panel/layout.py in _get_model(self, doc, root, parent, comm)
    113         if root is None:
    114             root = model
--> 115         objects = self._get_objects(model, [], doc, root, comm)
    116         props = dict(self._init_properties(), objects=objects)
    117         model.update(**self._process_param_change(props))

~/conda_envs/new_mre/lib/python3.6/site-packages/panel/layout.py in _get_objects(self, model, old_objects, doc, root, comm)
    105                 child, _ = pane._models[root.ref['id']]
    106             else:
--> 107                 child = pane._get_model(doc, root, model, comm)
    108             new_models.append(child)
    109         return new_models

~/conda_envs/new_mre/lib/python3.6/site-packages/panel/layout.py in _get_model(self, doc, root, parent, comm)
    113         if root is None:
    114             root = model
--> 115         objects = self._get_objects(model, [], doc, root, comm)
    116         props = dict(self._init_properties(), objects=objects)
    117         model.update(**self._process_param_change(props))

~/conda_envs/new_mre/lib/python3.6/site-packages/panel/layout.py in _get_objects(self, model, old_objects, doc, root, comm)
    105                 child, _ = pane._models[root.ref['id']]
    106             else:
--> 107                 child = pane._get_model(doc, root, model, comm)
    108             new_models.append(child)
    109         return new_models

~/conda_envs/new_mre/lib/python3.6/site-packages/panel/pane/holoviews.py in _get_model(self, doc, root, parent, comm)
    210                 plot = self.object
    211             else:
--> 212                 plot = self._render(doc, comm, root)
    213             plot.pane = self
    214             backend = plot.renderer.backend

~/conda_envs/new_mre/lib/python3.6/site-packages/panel/pane/holoviews.py in _render(self, doc, comm, root)
    254             kwargs = {}
    255 
--> 256         return renderer.get_plot(self.object, **kwargs)
    257 
    258     def _cleanup(self, root):

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/bokeh/renderer.py in get_plot(self_or_cls, obj, doc, renderer, **kwargs)
    133             curdoc().theme = self_or_cls.theme
    134         doc.theme = self_or_cls.theme
--> 135         plot = super(BokehRenderer, self_or_cls).get_plot(obj, renderer, **kwargs)
    136         plot.document = doc
    137         return plot

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/renderer.py in get_plot(self_or_cls, obj, renderer, **kwargs)
    201                              **kwargs)
    202             plot = self_or_cls.plotting_class(obj)(obj, renderer=renderer,
--> 203                                                    **plot_opts)
    204             defaults = [kd.default for kd in plot.dimensions]
    205             init_key = tuple(v if d is None else d for v, d in

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/bokeh/plot.py in __init__(self, layout, ranges, layout_num, keys, **params)
    595                                        ranges=ranges, keys=keys, **params)
    596         self.cols, self.rows = layout.shape
--> 597         self.subplots, self.layout = self._create_subplots(layout, ranges)
    598         self.set_root(params.pop('root', None))
    599         if self.top_level:

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/bokeh/plot.py in _create_subplots(self, layout, ranges)
    682                                          renderer=self.renderer,
    683                                          ranges=frame_ranges, uniform=self.uniform,
--> 684                                          keys=self.keys, **dict(opts, **kwargs))
    685                 collapsed_layout[coord] = (subplot.layout
    686                                            if isinstance(subplot, GenericCompositePlot)

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/bokeh/element.py in __init__(self, overlay, **params)
   1905 
   1906     def __init__(self, overlay, **params):
-> 1907         super(OverlayPlot, self).__init__(overlay, **params)
   1908         self.set_root(params.pop('root', None))
   1909 

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/plot.py in __init__(self, overlay, ranges, batched, keys, group_counter, **params)
   1164         self.cyclic_index_lookup = {}
   1165         self.zoffset = 0
-> 1166         self.subplots = self._create_subplots(ranges)
   1167         self.traverse(lambda x: setattr(x, 'comm', self.comm))
   1168         self.top_level = keys is None

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/plot.py in _create_subplots(self, ranges)
   1215         if isinstance(self.hmap, DynamicMap):
   1216             dmap_streams = [get_nested_streams(layer) for layer in
-> 1217                             split_dmap_overlay(self.hmap)]
   1218         else:
   1219             dmap_streams = [None]*len(keys)

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/util.py in split_dmap_overlay(obj, depth)
    223             if obj.callback.inputs and is_dynamic_overlay(obj):
    224                 for inp in obj.callback.inputs:
--> 225                     layers += split_dmap_overlay(inp, depth+1)
    226             else:
    227                 for v in obj.last.values():

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/util.py in split_dmap_overlay(obj, depth)
    223             if obj.callback.inputs and is_dynamic_overlay(obj):
    224                 for inp in obj.callback.inputs:
--> 225                     layers += split_dmap_overlay(inp, depth+1)
    226             else:
    227                 for v in obj.last.values():

~/conda_envs/new_mre/lib/python3.6/site-packages/holoviews/plotting/util.py in split_dmap_overlay(obj, depth)
    217     layers = []
    218     if isinstance(obj, DynamicMap):
--> 219         if issubclass(obj.type, NdOverlay) and not depth:
    220             for v in obj.last.values():
    221                 layers.append(obj)

TypeError: issubclass() arg 1 must be a class

@pollackscience
Copy link
Author

So I was able to get around this error by adding a dirty cludge to this chunk of code: https://github.com/pyviz/holoviews/blob/b5088cc59e7b6028c435fd2fb8f6b19417357700/holoviews/plotting/util.py#L218-L222

         ...
         if (obj.type is None or issubclass(obj.type, NdOverlay)) and not depth:
             for v in obj.last.values():
                 layers.append(obj)
         elif obj.type is None or issubclass(obj.type, Overlay):
         ...

My guess is that somewhere upstream, the obj.type is being set to None instead of the real type, which is causing the error I posted. After applying this patch, I can confirm that the fix that @philippjfr added in works, and the DynamicMap groupby operation works with the panel sliders.

@philippjfr
Copy link
Member

Hmm, thanks. I've come across this error a few times now, so it would be good to figure out what's actually going on there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't correct or isn't working
Projects
None yet
Development

No branches or pull requests

2 participants