-
-
Notifications
You must be signed in to change notification settings - Fork 375
Description
Until release 0.6.9, Datashader shipped with a Bokeh-based example dashboard with widgets to control various aspects of the plotting process (dataset selection, aggregation method, colormaps, spreading, opacity of map and of data, etc.). In release 0.6.9, this 500-line file was replaced with dashboard.ipynb, which does nearly the same thing with 70 lines of Panel-based code.
The new Panel version is much easier to maintain and understand, but it is very densely packed and requires certain bits of magic that are difficult to explain, as you can see in the extended description at the end of the notebook. It would be great if we could further streamline this example, which would require improvements to HoloViews, Param, or Panel.
The complexity mainly comes from wanting to avoid re-running expensive computations, so that simple operations can be fast and responsive, forcing the user to wait only for things that genuinely require deeply re-computing everything. To do that, the various widgets and Bokeh plot interactions need to be hooked up directly to just the right bit of computation, triggering that computation if and only if it is required for the update. Doing so is sometimes simple, but often still very tricky. Can we eliminate any of the following gotchas, limitations, and tricky bits?
- (Minor) People will be confused if their parameters come out in a different order than they were defined, so I explicitly added
precedencevalues, but that's verbose and somewhat confusing. We could at least rename precedence to be shorter, but using definition order is probably even more useful. - (Minor) Can we make it simpler to take the first object as the default for an ObjectSelector? Right now the default of an ObjectSelector is None unless specified, but it's tricky to pull out one of the items from an arbitrary object list to set the default when None isn't appropriate. E.g. for an OrderedDict objects list, the first item in the dict is
next(iter(objs.items()))[1], which is very obscure and awkward. It would be nice if Param can compute the default, preferably given a list or an OrderedDict so that the default is well defined while still extractable automatically from the objects. There would be some implications for backwards compatibility, as right now if no default is specified I believe the default is None. If that's a major issue, we could require the user to passallow_None=Falseto get this automatic-default behavior, but that would be ugly, so hopefully we don't need that. - (Major) Can we avoid having to make the ds.rasterize() call have
dynamic=False? We're forced to do that because the rasterize method is wrapped in a DynamicMap and thus cannot return a DynamicMap itself, but doing that cuts off the RangeXY stream that normally makes the rasterization dependent on the zoom and pan events in the plot. As a result we have to explicitly add RangeXY as a stream later, inviewable(), which is mysterious and tricky for the user to figure out, as they otherwise never need to know RangeXY exists at all. - (Major) Can we make there be fewer ways to make DynamicMaps in this example? Right now we have various operations that build DynamicMaps (spread, shade, rasterize), the hv.DynamicMap constructor for zero-argument methods, and hv.util.Dynamic for methods with arguments. Can't we make hv.DynamicMap accept arguments with methods, using an optional argument to specify a HoloViews object to which the method will be applied if one is supplied? It's very hard to explain to people why they need these different mechanisms and how to use them.
- (Medium) Having to create c_stream is confusing. Can we avoid it altogether by having HoloViews operations accept a list of parameters to watch, e.g.
watch=[self.param.cmap,self.param.normalization]or maybestreams=[self.param.cmap,self.param.normalization]? Would presumably require per-instance Parameter objects, and not sure how the renaming required for s_stream would be achieved if so.