3

I am writing a python script with interspersed plots and computations. I am confused about the behavior of matplotlib and in particular the necessity of plt.pause. Consider the following snippets:

import matplotlib.pyplot as plt
import time
fig,ax=plt.subplots()
ax.plot([1,2])
fig.show()
time.sleep(5) #This is a substitute for real computations

-> Nothing happens for five seconds

import matplotlib.pyplot as plt
import time
fig,ax=plt.subplots()
ax.plot([1,2])
plt.pause(0.1)
fig.show()
time.sleep(5) #This is a substitute for real computations

-> Window displays the desired plot for five seconds

It seems that plt.pause is required to see anything. Why then does the documentation say "This function is experimental; its behavior may be changed or extended in a future release." and why did I not see plt.pause in any tutorials?

Also, why would such an essential function be designed so strangely that the user has to input a small enough time, yet not zero? I get that some people actually want to pause exeuction, but I don't, I just want to see the plots. Is this so unusual?


By the way, I noticed that I can also do plt.show(), which, for reasons unknown to me, behaves different than plt.gcf().show()[=fig.show] and blocks the execution until the user closes the window. While this does show the plot when I want it, I do not want the execution to be stopped and I want the user to keep seeing the plot during the subsequent computations. Using plt.show(block=False) DOES seem to behave like plt.gcf().show()[=fig.show()], so it is also useless.

Furthermore, I read somewhere that plt.ion should help, but it doesn't. Adding plt.ion() before fig,ax=plt.subplots() in the snippets above doesn't change anything.

Finally, I heard that different backends might behave differently. I am using python 3.6 (anaconda) on Ubuntu 18 with matplotlib 2.2.2. If I add import matplotlib; matplotlib.use('Qt5Agg') at the beginning of the snippets, not much changes, but instead of showing nothing for five seconds the first snippets shows a garbage window for five seconds (the window shows whatever was shown on the screen at the location where it popped up).

Bananach
  • 2,016
  • 26
  • 51

1 Answers1

5

From the documentation of plt.pause()

Pause for interval seconds.
If there is an active figure, it will be updated and displayed before the pause, and the GUI event loop (if any) will run during the pause.
This can be used for crude animation. For more complex animation, see matplotlib.animation.

Hence the pause will actually draw the figure.

The key why plt.pause is "required" is that it starts the mock-up event-loop such that it has time to run at least once and produce the figure in completeness. After that your code can continue. While the code is running, no further events are processed. Therefore the figure may appear unresponsive. This means you should call pause repeatedly afterwards to not let the window freeze. In this way you emulate an event-loop while still being able to run other code in between.

plt.pause() is mentionned in this example, which does what is described above.

fig.show() does not run the event-loop. So without a running event loop, and without pause it will only show the figure window; but if you don't give any time for any events to be processed, it will freeze instantly. Here you may experience differences between operating systems and backends. Maybe you only see the window border and nothing painted in between, or you may see the toolbars and a white surface.

In general, the key of all of this is to understand that python (like other programming languages) processes code linearly. The desire to have an event loop running, meaning a responsive GUI window, and some other code being executed in parallel contradicts this principle. The usual way to circumvent this is to run any other code in a different thread. However, matplotlib cannot know what code that is and how to synchronize it to the main thread. Hence such solution would require the user to implement it.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • 2
    "The usual way to circumvent this is to run any other code in a different thread. However, matplotlib cannot know what code that is and how to synchronize it to the main thread." Why can the solution not be that matplotlib is the part that runs in another thread? It doesn't need to know anything about my other code; it would just have to react to whatever the user tells it from my main thread. This way the user wouldn't have to implement any threading himself (he wouldn't even have to know anything about threads at all). – Bananach Dec 13 '18 at 13:00
  • 1
    I guess that is answered [here](http://doc.qt.io/qt-5/thread-basics.html#gui-thread-and-worker-thread) for pyqt, *"[...] Each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread."* Similar arguments would apply to other backends. – ImportanceOfBeingErnest Dec 13 '18 at 13:13
  • Do you know *why* the Qt GUI (and others) need to run in the main thread? Seems like a serious inconvenience to me – Bananach Nov 12 '19 at 15:55
  • It's a quasi-standard in many programming languages. Maybe [this](https://stackoverflow.com/questions/3794420/why-is-only-the-ui-thread-allowed-to-modify-the-ui) is helpful here. – ImportanceOfBeingErnest Nov 12 '19 at 16:04