-
Notifications
You must be signed in to change notification settings - Fork 231
Change to "notebook"-like behavior #147
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
Conversation
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.
Thanks for doing it!
I looked a bit more into this. One of the big issues of incompatibility between matplotlib resizing and widget resizing is that the widget size currently corresponds to the figure with all of the buttons and labels, while matplotlib sizes refer to the figure itself. I looked into how bqplot addresses this, and their solution is to have the figure view refer to just the figure itself without the toolbar. Following a similar approach would make things a lot easier because you wouldn't have to calculate offsets including the toolbar. Unfortunately, his would require some restructuring of the codebase. |
I realized that the bqplot strategy still breaks all the widget layout that you might want to do because you want to specify the full widget size, including the figure and decorations. Instead, I wrote a couple of functions that calculate the size of the decorations and sync the matplotlib figure size and the widget figure size. With this commit, you can set the figsize and the widget size will then update, or you can set the widget size and the figsize will then update. All of the logic to go between these two sizes is contained in the "_calculate_decorations_size" function. With this commit, everything should work "as expected" for the most part for either the matplotlib interface or the widget interface. |
One minor issue is that when the widget size is set on the client (after a new matplotlib figure is generated), that size doesn't seem to be properly propagated back to the server. The only practical issue that I found because of this is that the server doesn't resend the size if it thinks that it didn't change, so if the server sets size A, the client sets size B and the server tries to set size A again, it won't go back since the server thinks that the client is still in size A. Setting to a different size is fine. I am currently setting the widget width using "this.el.style.width = ...". Is there a different way to set layout attribute that gets propagated back to the server? |
One way to address the issue may be to
|
@SylvainCorlay: this implementation basically does the first bullet point in your list. If the client sets the widget to a size in pixels (or any other units), then the client calculates how big the matplotlib figure should be in pixels on that specific client and sends that to the server to generate a new figure. Whenever a new figure comes back from matplotlib, the widget size is recalculated in pixels and updated accordingly on the client. This means that different clients will always share the same matplotlib figure size in pixels, but could end up with slightly different widget sizes if their buttons and text labels are slightly different sizes. |
This is the PR I am waiting for to adopt ipympl. |
Sorry for the long silence, and thanks so much for this @kboone! |
Testing more on the 0.4.0 release shows some issues with the resize. I will look into it tomorrow. |
Let me know if I can help with anything. I have been using juptyer-matplotlib with this patch since I wrote it. I haven't encountered any major issues, but I know that there is a lot of functionality of matplotlib that I don't use. If you provide me with examples of what is wrong and what you would like to have happen I'd be happy to look into them. One thing that I expect is likely broken currently is changing the DPI... I don't really understand how that is supposed to be handled in jupyter-matplotlib. |
Just thinking out loud... if matplotlib canvas+widget sizing and resize interactions with the notebook are something that take a bit of creativity to get right, such that looking at bqplot was helpful, I can't help but wonder if matplotlib, bqplot, ipyvolume, ipyleaflet, bokeh, and others are all solving the same problem. Is this possibly a jupyter widget API thing that could be abstracted up into the widgets ecosystem? Like provide a framework for a resizeable canvas+buttons widget that any plotting or mapping widget can inherit or utilize? I figure the obvious counterargument is that each of these canvas/plotting widgets has different goals and aesthetics. A generic plotting widget might either be so opinionated and narrowly implemented it's unsuitable for the different styles implemented by the different libraries, or so broadly and generically implemented it's equivalent to the Jupiter widget system we have now or something like Panel. So I guess I'm asking if the resizing stuff this PR is trying to get right is something Jupyter Widgets should provide or if it makes sense that each library that implements a rich interactive widget has to solve this problem. edit: maybe my discussion makes more since in the linked issue #117, catching up on all the discussion takes a minute... sorry for the noise |
This is a first pass at addressing #117. With this implementation, the matplotlib backend controls the figure size, and the canvas size is automatically adjusted in Jupyter Lab to fit the figure.
With this commit, you can specify the figsize in plt.figure(), and fig.set_size_inches() can be used to update the size of the figure.
Note that high DPI images are not properly displayed in this first commit. This also breaks setting the height and width via "fig.canvas.layout.height".