Skip to content

fig.show() doesn't block #13101

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

Closed
dstansby opened this issue Jan 4, 2019 · 20 comments
Closed

fig.show() doesn't block #13101

dstansby opened this issue Jan 4, 2019 · 20 comments

Comments

@dstansby
Copy link
Member

dstansby commented Jan 4, 2019

Bug report

Calling fig.show() on a figure doesn't block. Commenting out the input() line below does block, and the figure shows fine. Also using plt.show() instead works fine.

Code for reproduction

import matplotlib
matplotlib.use('qt5agg')
from matplotlib import pyplot as plt

fig, ax = plt.subplots()
fig.show()
# input()

Matplotlib version

  • Operating system: MacOS
  • Matplotlib version: 3.0.2
  • Matplotlib backend (print(matplotlib.get_backend())): qt5agg
  • Python version: 3.7.1
@WeatherGod
Copy link
Member

WeatherGod commented Jan 4, 2019 via email

@dstansby
Copy link
Member Author

dstansby commented Jan 4, 2019

Doh, that makes sense - is there not a way to do some stuff, open a figure that stays open, then do some other stuff and open another figure whilst keeping the first one open?

@jklymak
Copy link
Member

jklymak commented Jan 4, 2019

You mean interactively in ipython?

plt.ion()

@dstansby
Copy link
Member Author

dstansby commented Jan 4, 2019

No, just in a shell

@jklymak
Copy link
Member

jklymak commented Jan 4, 2019

plt.ion() works in a vanilla python shell as well. Am I not understanding your problem?

In [9]: plt.ion()

In [10]: plt.plot(range(10))
Out[10]: [<matplotlib.lines.Line2D at 0x11d6326a0>]

In [12]: plt.figure()
Out[12]: <Figure size 1280x960 with 0 Axes>

In [13]: plt.plot(range(20))
Out[13]: [<matplotlib.lines.Line2D at 0x1234c5080>]

@dstansby
Copy link
Member Author

dstansby commented Jan 7, 2019

I'm clearly just very confused, plt.ion() worked fine... Thanks all!

@dstansby dstansby closed this as completed Jan 7, 2019
@tacaswell
Copy link
Member

To follow up a bit, there is an unfortunate name collision here, but plt.show() and fig.show() map to very different things at the GUI level

plt.show -> "make sure all the figures are visible and then run the main loop to block the python process (unless we are interactive, then rely on something else to make sure the event loop gets run every-so-often)"

fig.show -> "tell the GUI framework to put the window on the screen but do not try to manage the event loop"

In general you don't have to actually use fig.show as plt.show will call it on all figures it knows about for you. This is convenient and clearly the correct behavior, but does add to the confusion.

@timhoffm
Copy link
Member

This should probably be mentioned in the documentation.

Not sure I understand all this correctly. Is there any use-case for and end-user to call fig.show?

@tacaswell
Copy link
Member

Yes, but they involve actively managing running the event loop.

@timhoffm
Copy link
Member

Is that the use case of embedding matplotlib in your own GUI application?

Users from the command line/ipython/jupyter notebooks do not have to bother with fig.show(), right?

@ImportanceOfBeingErnest
Copy link
Member

ImportanceOfBeingErnest commented Jan 10, 2019

It seems, fig.show() is useful in IPython. While plt.ion() + plt.show() result in an unresponsive window, fig.show() works fine for me. I'm not a regular IPython user myself, but I used (i.e. py27, matplotlib < 2.2) to use it like

Python 3.6.6 |Anaconda, Inc.| (default, Jun 28 2018, 11:27:44) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import matplotlib.pyplot as plt

In [2]: fig, ax = plt.subplots()

In [3]: ax.plot([1,2])
Out[3]: [<matplotlib.lines.Line2D at 0x7c19976a20>]

In [4]: fig.show()

In [5]: ax.plot([1,3])
Out[5]: [<matplotlib.lines.Line2D at 0x7c1c5aa320>]

In [6]: fig.show()

Here in cell 4, the window would open up and show a responsive(!) figure. Without closing the window I could continue to plot stuff and call fig.show() again to have the figure updated.

Nowadays (py36, matplotlib 3), the second fig.show() doesn't seem to work any more. I would instead need to use fig.canvas.draw() in cell 6 from above.

In [6]: fig.canvas.draw()

In any case that is still more useful than getting unresponsive windows with plt.show() in case one wants a non-blocking environment.

@timhoffm
Copy link
Member

I get a responsive window with

In [1]: import matplotlib.pyplot as plt

In [2]: plt.ion()

In [3]: fig, ax = plt.subplots()

Figure window is immediately displayed and the command line is not blocked (no show() needed).
I can then add elements to the plot

In [4]: ax.plot([1,2])

which immediately updates the plot.

@ImportanceOfBeingErnest
Copy link
Member

Yes, your code works for me too. The problem is when you close the GUI window, and then want to get it back. plt.show() doesn't work in that case.

@jklymak
Copy link
Member

jklymak commented Jan 12, 2019

...? But fig.show() does? I'd have thought closing the window would destroy the figure.

@ImportanceOfBeingErnest
Copy link
Member

ImportanceOfBeingErnest commented Jan 12, 2019

Surprisinigly perhaps, yes. In the case you closed it you'd also need to draw it though (fig.canvas.draw()). Maybe worth noting that this is really only working in IPython. I suppose somehow IPython itself runs some kind of event loop or so(?) for this to work.

@tacaswell
Copy link
Member

I'd have thought closing the window would destroy the figure.

The visibility / non-visibility of the window is decoupled from it's existence in most GUI frameworks. This is really helpful for things like showing / hiding complex panes etc. In our case the continued existence also depends on a hard-ref to the underlying python object (but for pyplot, we register a callback on 'close' to remove it from pyplot's cache, which is why re-showing with plt.show() does not work, but fig.show does).

@timhoffm
Copy link
Member

Hm, quite elaborate. I have no idea how to describe the effect and intended way of usage in a concise way in the docstring. ☹️

@tacaswell
Copy link
Member

#4779 <- that is my attempt to be both concise and complete, it has gone badly :/

@Guimoute
Copy link

Guimoute commented Apr 3, 2022

I just stumbled upon a situation where fig.show() not blocking can be an issue.

fig, ax = plt.subplots()
manager = plt.get_current_fig_manager()
manager.window.showMaximized() # For Qt5 backend.
fig.show()

... # Plotting objects in data coordinates: works fine.

#%%
... # Plotting objects in figure coordinates: fails because the objects fall in the pre-fullscreen positions.

The current work-around is to add a code section with #%% so that Spyder can execute everything that is not based on figure coordinates first, then run the rest.

Another option is to add plt.pause(0.001) after the show + fullscreen lines.

@prabhuiitdhn
Copy link

fig.show() does not block the window for showing plot multiple times. so, Quick fixes using self.fig.waitforbuttonpress() it waits user to press the button for next plot visualisation.

      self.fig.show()
      plt.pause(0.1)
      self.fig.waitforbuttonpress()

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

8 participants