-
Notifications
You must be signed in to change notification settings - Fork 7
Add support for custom widget layouts #66
base: master
Are you sure you want to change the base?
Conversation
How about:
or (with a rule for promotion of streams if that's feasible):
(Where |
Personally my vote is against such magic in this case, I think people are likely to get confused what is and isn't a HoloViews object and it won't be very transparent what kind of object they end up with. |
That's a reasonable concern, but in this case I would think that it doesn't matter a whole lot, as long as the result will be visualizable, which it should be (adding a HoloViews object to any of this should result in a ParamBokeh row, etc.). Once it's viewable and the representation can be printed, I would think people can figure things out. |
I haven't played with this yet, but just thinking through how I would apply this in a more complicated grid pattern makes my head hurt. Even the quick example here required me to stop and think about how this was being done. I'm wondering about handling layout in a similar way to say, matplotlib subtplots, e.g. You declare a grid then you declare objects which span a certain number of grid cells. For me and my user-base, that's a common, easily interpreted method. |
In terms of the low level implementation there is a fundamental reason why a gridspec type pattern will not work well here, a gridspec type pattern is predicated on the idea of defining the grid and then assigning what to plot where. Bokeh on the other hand is predicated on a compositional approach where you define each subplot, give them a certain size and then build up your layout by composing the plots together into rows and columns, i.e. a grid is a row of columns or a column of rows. So at least at the lowest level it has to be implemented in this way. That all being said we could probably build a gridspec style API on top of this column/row based approach, which resizes the plots you feed it based on the number of grid cells they should span. |
So I've been thinking about the gridspec approach and playing around with it. It's not entirely straightforward because we need an algorithm that iteratively breaks up the grid into rows and columns depending on the specified layout. In terms of API I'm imagining something like this:
Unlike matplotlib's GridSpec this works by assignment rather than by indexing for the reasons I discussed above. |
My vote is that we do implement the gridspec approach although not necessarily in this PR. |
The grid-based layout approach is fine and a good thing to offer. E.g. it seems to work well for Dashing. Still, it's unwieldy for simpler cases where we're essentially building figures rather than dashboards.
I've been thinking about this further, and I think we should be working towards a goal of having a layout be something that works transparently between HoloViews and ParamBokeh, with either library able to work with the other library's objects smoothly and easily, without ParamBokeh necessarily depending on HoloViews or vice versa. I don't know if that means putting something into Bokeh itself to make this work (as Bokeh Server is the key shared element), or just having conditional imports and some duplicate code. But from a user perspective, I want all these things to be true:
As a user, I really don't want to dwell a lot on what comes from which library -- I just want to assemble things into a figure, a dashboard, an app, or whatever I'm working on. If we can have each library provide a set of objects that mix and match in this way, then we're golden! |
This is true now.
Also true now, where "other things" includes bokeh models.
Also true now.
I disagree very strongly here. Mixing up HoloViews Layouts, which, while row based, may declare rows and columns, with these new layouts is imo a recipe for horrible confusion. At the very minimum the syntax cannot be the same. Let's take an example here:
while
produces
That seems incredibly non-obvious and there will be many other weird cases. Additionally a HoloViews Layout and a parambokeh Row/Column have very different APIs, so while you want this particular API to work transparently across both, a user will immediately get confused if they do:
because none of the standard HoloViews methods will work on the returned My strong preference is therefore not to add such magic, at least in the short term, and make the user be explicit about the objects they are declaring. We absolutely do need to consider how we can combine |
@jlstevens and @ceball Would be good if could chime in here if you have opinions about this. |
I think I agree with Philipp: at the very least I don't want to introduce any more compositional operators such as the proposed Although such syntax is concise for the advanced user who has a very clear mental model of what is going on, this can get very confusing for everyone else. We need to use our compositional operators sparingly at the level where we can get the most power out of them, which imho is at the holoviews and not the parambokeh level. I say that our goal is to make parambokeh work well with holoviews which does not imply that we have to try to make the API such that it gets confusing between the two projects (which should be kept separate as they are now, unless we get around to doing the major widget refactor in holoviews). Philipp's suggestion of
seems entirely reasonable to me for the typical contexts where this flexibility is really required (primarily dashboards). The gridspec proposal also seems perfectly reasonable to me and I think it would be quite flexible if it can be made to work well. Lastly, I'll note that I agree that we have to improve how we lay things out (which this PR helps address) but I think we need to have an overall plan for how to improve layouts at the holoviews level before considering adding any new special syntax. |
Thanks, I obviously agree with all of that. The main thing that we have to figure out before this PR can be merged is backwards compatibility. In the past Do we have a major release and break backward compatibility, offer some compatibility mode, or maintain backward compatibility for now and offer a switch to enable the new behavior? |
Just to be clear, I am not proposing that this would return anything in particular, at least not in my most recent message. I'm just trying to outline a set of principles for how I think things should work. I love what this PR achieves, but the principle here (#5) is that I don't think we should have two totally separate ways of combining things into layouts between ParamBokeh and HoloViews (and maybe Bokeh?). I think we should have one way, used by both libraries, because anything else will perpetually be confusing about what's supported, what limitations there are, and so on. But that comes back to needing to do our own layout to make that work, because of Matplotlib, right? Plus it sounds like we have to wait on the widget refactor to get anywhere close to that? Sigh. I was hoping we could get some of the way there with this PR, but it sounds like instead it's going to introduce a different, incompatible API, a different way of doing layouts, and a different type of object that can be laid out, when what I think we should be aiming for is a unified way of laying things out (as both HoloViews and Parambokeh equally need the ability to have things in rows and columns, in gridspecs, and so on), unified types of objects that can be laid out between both libraries (not totally separate types that we have to keep track of, as here), and so on. I just want "chunk" and "nested rows and columns of chunks"! |
Any replacement for HoloViews' I can work on improving the capabilities in this PR to allow laying out matplotlib and plotly objects alongside the HoloViews, Bokeh and parambokeh objects that are already supported. Secondly, if you think it's a good idea we can move these components into an entirely new package, which parambokeh would then depend on. Also let me quickly summarize how this
I'll also quickly lay out the options for providing an overall vision for layouts (as I see them):
Personally I would argue that 1. is completely out of the question, abandoning native matplotlib/plotly layouts would be a huge step back in HoloViews' capabilities. Therefore I argue 2. is the correct thing to do right now, and if we do ever find the time/funding we could extend 2. with 3. by writing native plotting implementations for Row/Column based layouts and thereby replace In the short term the functionality in this PR would then become a high-level layout engine meant for building dashboards or complex plots restricted to bokeh based layouts. In the longterm it could then be extended to provide a high-level layout engine which works across backends. |
I think there is too much to discuss to lay it all out in a github issue: we should have a meeting to decide what to do. |
Yes, we'll meet about it Monday. I can follow most of your description and analysis, but I still have one big question in the meantime:
I don't see how this con follows from option 1. Can't we unify layouts while having some features that apply only to a subset of the "chunks" involved? That's the part I'm not getting; how does allowing everything to mix and match have to hurt any existing functionality? E.g. for normalization and linking axes, can't what we are laying out have a certain level of base functionality that applies to all chunks (large-scale arrangements on the screen), but then some extended functionality that is supported only between some types of chunks? I.e., if we lay out these items all in a single notebook cell or dashboard:
Can't items 1, 2, 3 all be normalized together (before any zooming) regardless of items 4, 5, 6 existing? If we are implementing a new layout system as we are here, I don't see why we necessarily have to give up the features we rely on from HoloViews+Bokeh layouts. Can't those simply be optional features that can be used when needed? |
Ah, I think I misunderstood your option 1. You aren't saying that using the new layout system would have to work in this way, just that it's the way it works right now, and that these would be the consequences of abandoning hv Layouts now. Ok, yes, I agree, we can't do that! And your option 3 sounds essentially like what I'm suggesting in my previous comment, i.e. a generalization of what this PR does, adding special HoloViews-based support to make it achieve feature parity with HoloViews layouts, with an eye to eventually supplanting them. Sounds good. |
I'm not sure how best to break up packages, but it does seem to me like this functionality is turning into something that makes sense separately from Param and from Bokeh, given the range of types that can be composed. Using the name |
parambokeh/util.py
Outdated
@@ -118,7 +120,15 @@ def process_plot(plot, doc, plot_id, comm): | |||
from holoviews import renderer | |||
renderer = renderer('bokeh').instance(mode='server' if comm is None else 'default') | |||
plot = renderer.get_plot(plot, doc=doc) | |||
|
|||
elif plot.__class__.__name__ == 'Figure' and hasattr(plot, '_cachedRenderer'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this elif should have a comment above it saying "Matplotlib figure", as that's not at all obvious from what's being tested.
parambokeh/util.py
Outdated
src = "data:image/png;base64,{b64}".format(b64=b64) | ||
html = "<img src='{src}'></img>".format(src=src) | ||
width, height = plot.canvas.get_width_height() | ||
return Div(text=html, width=width, height=height) | ||
if not hasattr(plot, '_update_callbacks'): | ||
raise ValueError('Can only render bokeh models or HoloViews objects.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably this error message is out of date. Maybe "Not one of the currently supported displayable objects (Bokeh models, HoloViews objects, or Matplotlib figures)."?
Would it be possible to have a fallback that allows any object with a _repr_html_
method? That would cover Pandas .head()
and possibly streamz objects, and would let users make their own wrappers around other things...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to have a fallback that allows any object with a repr_html method?
Sure.
and possibly streamz objects
Definitely not, those work using ipywidgets.
|
This PR is a prototype of a major new feature in parambokeh. It adds a number of
Viewable
andLayout
classes which wrap around bokeh models, parambokeh widgets and holoviews objects and allow laying them out in nested rows and columns and allow them to display themselves (in the same way HoloViews objects do). This allows for far more flexibility for laying out widgets and bokeh plots than the existing mechanisms, i.e. passing inplots
to parambokeh.Widgets or definingView
parameters and the standard HoloViews layouts.The approach rearchitects the API quite a bit since the
parambokeh.Widgets
function now returns these newViewable
objects, however, overall this makes things much simpler and more flexible.Here is a simple example of laying out two widgets:
And here is an example of an complex dashboard composed of two sets of parambokeh widgets and a HoloViews plot:
This is very much a proof of concept and I'm very open to improving the API here. A full walkthrough through the functionality contained within this PR can be seen here: https://anaconda.org/philippjfr/parambokeh_layouts/notebook