ipython / matplotlib-inline

Inline Matplotlib backend for Jupyter
BSD 3-Clause "New" or "Revised" License
19 stars 29 forks source link

difference in plt.gca() behaviour between inline and notebook backends #13

Open petercorke opened 2 years ago

petercorke commented 2 years ago

I have one notebook cell that creates a 3D axes, which I want to plot into in the next cell.

With the inline backend

Screen Shot 2022-01-26 at 3 18 55 pm

we see that the "current plot" has been forgotten, and plt.gca() creates a new 2D plot.

With the notebook backend

The behaviour is different

Screen Shot 2022-01-26 at 3 23 57 pm

and plt.gca() remembers the 3D plot across the cell boundaries.

Expected behaviour

The same in both cases, ideally the remembering the current plot across the cell boundary. I'm not sure which behaviour is "correct" but the difference is a problem when I'm running code in an ipython kernel from a client.

fperez commented 2 years ago

@petercorke, the issue you're encountering is indeed an inconsistency in behavior between the inline and notebook/ipympl/widget (let's call it widget from now on) backends.

Unfortunately, it's not completely trivial to fix. The problem is that, in order to achieve a "good user experience" in each case, over the years we had to make slightly different decisions:

To achieve both of these goals, the inline backend aggressively closes figures once it's done displaying them, so you don't get "cross-cell side effects".

On the other hand, the widget one leaves them alone, for you to manage manually, so you can decide which ones you want to be interactive and which not.

Now, I don't think this state of affairs is ideal, but I've never sat down with enough time to see how to have a better experience across the two (keep in mind, the widget backend didn't exist when the inline behavior was designed, which was pre-jupyter notebook, back in 2010 for the IPython qt console).

It's possible there's a better long-term solution that @tacaswell or others have come up with. I know this is a nag for many, myself included.

But in the meantime, may I suggest you use this pattern instead. For the inline backend, when you need to manage your figures persistently (keep them alive across cells):

fig, ax = plt.subplots(...)
# do what you need with figure and axis objects
#...
fig  # last line to display a figure

and you can use the same code with the widget backend. The only thing you need to do in this case is manually close figures you don't need anymore. But otherwise it's reasonably clean code that, while a bit more verbose, works well for both backends.

I hope this helps!

fperez commented 2 years ago

BTW @petercorke - there's a longer discussion of this very topic in this issue on the ipympl repo, which you might find useful.