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

[Bug]: plt.interactive(False) not preventing figure generation in jupyter unless %matplotlib inline is run first #23766

Closed
nvaytet opened this issue Aug 29, 2022 · 10 comments
Labels
status: needs clarification Issues that need more information to resolve.

Comments

@nvaytet
Copy link

nvaytet commented Aug 29, 2022

Bug summary

Using plt.interactive(False) or plt.ioff() in Jupyter (Lab and classic notebook) does not seem to prevent automatic figure generation, unless the magic %matplotlib inline in run first, even though the inline backend is the default.

Code for reproduction

import matplotlib.pyplot as plt

plt.interactive(False)

fig, ax = plt.subplots()
a = 0

Actual outcome

Screenshot at 2022-08-29 14-25-08

Expected outcome

Explicitly running %matplotlib inline block figure creation:
Screenshot at 2022-08-29 14-26-48

Note that running %matplotlib inline has to be done BEFORE the first figure was created, if not it has no effect
Screenshot at 2022-08-29 14-28-29

Additional information

Using the widget backend works in the same way as inline: if called before any figures are created, figures are not automatically generated. If called after one figure was made, then plt.interactive(False) has no effect.

I also tried plt.ioff() instead of plt.interactive(False) but it gives the same results.

Operating system

Ubuntu 22.04

Matplotlib Version

3.5.2

Matplotlib Backend

inline (jupyterlab)

Python version

3.9

Jupyter version

3.4.2

Installation

conda

@timhoffm
Copy link
Member

timhoffm commented Sep 7, 2022

The non-interactive mode is mainly targeted at command line shells where it is cumbersome to write multi-line commands. I don't know whether anybody has looked into that topic for the inline backend, which is tied to notebooks and thus makes it inherently easy to write code blocks into one cell and execute them together.

That said, what your motivation to spread the figure creation across multiple cells? In general I would not recommend this approach because it's more brittle - the figure will likely only work if you call all related cells in order.

@timhoffm timhoffm added the status: needs clarification Issues that need more information to resolve. label Sep 7, 2022
@nvaytet
Copy link
Author

nvaytet commented Sep 8, 2022

My motivation is that I want to create my figure, do stuff to it in cells below (e.g. add lines, or set the axis range), and only when my figure is ready I want to display it. I want to stop Matplotlib from automatically displaying it in the notebook as soon as it's created.

@timhoffm
Copy link
Member

timhoffm commented Sep 8, 2022

You'll be running against the inline backend concepts here.

At least you have to deactivate auto-closing of figures at the end of cells https://github.com/ipython/matplotlib-inline/blob/fbf0ab8f2d81993ff8e319a003f58896c77f2443/matplotlib_inline/backend_inline.py#L289

There are likely more pitfalls here, but I'm not an expert for the inline backend.

I still would suggest to separate data manipulation and plotting:

  1. do the data manipulation in one or more cells
  2. finally create and display the plot in one cell. Figure creation should happen in the cell that will also show the plot as a result. You may use parametrized helpers functions to create the desired plot efficiently from your data.

This should make your code much clearer than spreading the figure code across multiple cells.

@nvaytet
Copy link
Author

nvaytet commented Sep 8, 2022

Issue with automatically displaying figures:

I have my own class Plot, that takes in some input, makes a matplotlib figure.
My class also has its own _repr_mimebundle_ so that when I do

p = Plot(<some input>)
p

this will currently display the figure twice, once from the auto-displaying and once from the repr.

I want a Plot object because I could either want to apply more steps to it, e.g.

p = Plot(<some input>)
p.ylim(10, 20)
p

(this would also display two figures).

I might also want to make multiple figures, and then place them inside widget containers, arranging them as I want, e.g.

p1 = Plot(<some input1>)
p2 = Plot(<some input2>)
from ipywidgets import ipw
ipw.HBox([p1, p2])

which now would display first the two matplotlib figures from p1 and p2, and then the two figures again side by side inside the HBox.

Basically I just want to be able to control when the figure is displayed, which I can if I run %matplotlib inline explicitly in the notebook. My question was just about why does plt.ioff() behave differently when I don't write %matplotlib inline in my notebook, even though the %matplotlib inline is the backend which is activated by default?

@jklymak
Copy link
Member

jklymak commented Sep 11, 2022

Notebooks in the inline backend are not interactive. If @nvaytet, if you really want different behaviour here for an important reason, then the appropriate place to report is https://github.com/ipython/matplotlib-inline. Thanks!

@jklymak jklymak closed this as not planned Won't fix, can't repro, duplicate, stale Sep 11, 2022
@nvaytet
Copy link
Author

nvaytet commented Sep 11, 2022

@jklymak my question wasn't specifically about the inline backend, my question was Why does plt.ioff() have no effect? Note, as I wrote, that the behavior is the same with inline and widget backends.

@jklymak
Copy link
Member

jklymak commented Sep 11, 2022

Probably your best place to discuss is https://discourse.matplotlib.org. Widgets are discussed at https://github.com/matplotlib/ipympl and inline https://github.com/ipython/matplotlib-inline.

My question was just about why does plt.ioff() behave differently when I don't write %matplotlib inline in my notebook, even though the %matplotlib inline is the backend which is activated by default?

Again, this is better asked at https://github.com/ipython/matplotlib-inline.

@timhoffm
Copy link
Member

One final remark / recommendation: @nvaytet you are wrapping Matplotlib both on the API side as well as the output side. In that case, you're most likely better off using the 'agg' backend internally to write image data and use this image in your output. In your case inline or widget backends would only come into the way and you'll have a hard time fighting and pressing them into your desired behavior, which they are not designed for.

@nvaytet
Copy link
Author

nvaytet commented Sep 14, 2022

@timhoffm thanks for the comment. But my users still want to have plots that they can zoom/pan onto in their notebooks. So I cannot avoid using the widget backend...

@timhoffm
Copy link
Member

Ok, but then we're not talking about the inline backend but specifically about the widget backend, which is maintained in a separate project at https://github.com/matplotlib/ipympl. I think you want this to have additional capabilities, which it was not designed for. Discussion should move there. - However, I'm a bit sceptical that it gets extended in that direction, because ipympl does not have enough development resources. An alternative approach on your side could be to store all the configuration in your Plot object and only create the figure in the _repr_mimebundle_ call. That way, the figure is still only created in the cell, after which it is displayed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs clarification Issues that need more information to resolve.
Projects
None yet
Development

No branches or pull requests

3 participants