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

Simplify bare-widgets usage compared to Parameterized classes #648

Closed
1 of 5 tasks
jbednar opened this issue Sep 20, 2019 · 4 comments
Closed
1 of 5 tasks

Simplify bare-widgets usage compared to Parameterized classes #648

jbednar opened this issue Sep 20, 2019 · 4 comments

Comments

@jbednar
Copy link
Member

jbednar commented Sep 20, 2019

To make a comparison, I've translated the Datashader Dashboard example from a Parameterized class with Parameters and decorated methods (http://datashader.org/dashboard.html) to a set of explicitly instantiated widgets in the main namespace and decorated functions (https://anaconda.org/jbednar/dashboard_barewidgets). As of latest HoloViews master and Panel master, this approach works, but some things are still much more verbose and awkward in the bare-function approach. I'm enumerating those here in the hopes that at least some of them can be improved or eliminated.

  • Using widgets requires passing .param.value in many places to find the underlying parameter; it would be great if the widget itself can be passed and HoloViews can find the parameter inside automatically (Support passing widgets to operations holoviews#3976)
  • Instantiating a widget is typically a lot more verbose than instantiating a parameter:
plot          = pm.Selector(plots)
spreading     = pm.Integer(0, bounds=(0, 5))
data_opacity  = pm.Magnitude(1.00)
show_labels   = pm.Boolean(True)

vs.

plot          = pn.widgets.Select(name='Plot', options=plots)
spreading     = pn.widgets.DiscreteSlider(name="Spreading", value=0, options=list(range(0,6)))    
data_opacity  = pn.widgets.FloatSlider(name="Data opacity", value=1.00, start=0, end=1)
show_labels   = pn.widgets.Checkbox(name="Show labels", value=True)

Here we have to explicitly pass the name of a widget, while for Parameters the name defaults to the attribute name, presumably implemented by the Parameterized metaclass. Trying to do that for a bare widget would require too much introspection magic, right? I think so, but just checking.

  • Also, widgets appear to accept only keyword arguments, and so even though in all cases there is an obvious primary argument which is what the Parameter's first positional argument accepts, the widgets still need the "value" or "options" argument specified every single time. Addressing this seems painful, requiring adding positional arguments to all the widget constructors?
  • In the Parameterized case, it's simple to refer to the whole set of widgets for the class, which are displayed in definition order (potentially modified with precedence values if present). For the bare widget case, the widgets have to be collected and ordered explicitly: widgets = pn.Column(plot, field, agg_fn, normalization, cmap, spreading, basemap, data_opacity, map_opacity, show_labels). As for the name, I don't think there is any solution to this issue that wouldn't involve magic introspection, but I'm listing it for completeness.
  • Even if we remove the requirement for .param.value, there are differences in how we define and work with decorated methods and decorated functions:
    @pm.depends('map_opacity', 'basemap')
    def tiles(self):
        return self.basemap.opts(gopts).opts(alpha=self.map_opacity)

vs.

@pn.depends(map_opacity=map_opacity, basemap=basemap)
def base(map_opacity, basemap):
    return basemap.opts(gopts).opts(alpha=map_opacity)

Here decorated methods list dependencies as strings of parameter names and refer to the values using self., while decorated functions declare mappings from widgets to keyword argument names, and refer to the values using argument attributes. These differences are difficult to explain, but make sense. Can we simplify and/or unify it any? Supporting @pm.depends('map_opacity', 'basemap') for a function seems like it would require looking up "map_opacity" in the namespace and connecting it to that keyword value, which may or may not be too much magic for a decorator. Mapping by positional argument rather than keyword seems like too much of a change now. Does that cover it?

@philippjfr
Copy link
Member

philippjfr commented Sep 20, 2019

Trying to do that for a bare widget would require too much introspection magic, right? I think so, but just checking.

Yes, basically impossible.

Also, widgets appear to accept only keyword arguments

I would consider making value the positional argument but generally I'm tending towards -1 on this.

Even if we remove the requirement for .param.value, there are differences in how we define and work with decorated methods and decorated functions

This is not a panel restriction, this is merely a restriction in HoloViews due to the way DynamicMap works, positional arguments work just fine with panel. Accepting strings is definitely not something I'm willing to support.

would require looking up "map_opacity" in the namespace and connecting it to that keyword value, which may or may not be too much magic for a decorator.

Yes absolutely too much magic.

@philippjfr
Copy link
Member

To be clear, the only suggestion I'm happy to have in Panel is the one about accepting widgets, which was already merged in #639. So unless you strongly insist on positional arguments on widgets I'm therefore going to close this issue.

@jbednar
Copy link
Member Author

jbednar commented Sep 20, 2019

Don't worry; I'm not strongly insisting on anything, just trying to see if there are any appropriate approaches we are missing to make things be more parallel, more intuitive, and simpler. Ideally, people could switch between widgets and Parameters without much fuss and without having to learn entirely new concepts, but in each case above the differences seem unavoidable. Ok, unless you can come up with some brilliant workaround!

Anyway, I don't think accepting strings for decorated functions is simpler, and in fact I'd rather not accept them for decorated methods either, but I seem to recall that was a limitation not possible to eliminate. Whether to have widgets have positional arguments (value in most cases, but options for a Selector) is your call as Panel lead author, based on what examples you want to show and how you think people should be using things, what's more readable, etc.

But note that #639 isn't sufficient for the example here; it still needs holoviz/holoviews#3976 for it to be able to avoid .param.value in most cases. Of course, that's an issue on HoloViews and not Panel itself, but I had to file the issue in only one repo, and I chose Panel since it's about widgets...

@philippjfr
Copy link
Member

Thanks for opening the issue in any case. I do think we've done everything we could here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants