247

I am having problems trying to make matplotlib plot a function without blocking execution.

I have tried using show(block=False) as some people suggest, but all I get is a frozen window. If I simply call show(), the result is plotted properly but execution is blocked until the window is closed. From other threads I've read, I suspect that whether show(block=False) works or not depends on the backend. Is this correct? My backend is Qt4Agg. Could you have a look at my code and tell me if you see something wrong? Here is my code.

from math import *
from matplotlib import pyplot as plt
print(plt.get_backend())


def main():
    x = range(-50, 51, 1)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4

        y = [Xi**pow for Xi in x]
        print(y)

        plt.plot(x, y)
        plt.draw()
        #plt.show()             #this plots correctly, but blocks execution.
        plt.show(block=False)   #this creates an empty frozen window.
        _ = raw_input("Press [enter] to continue.")


if __name__ == '__main__':
    main()

PS. I forgot to say that I would like to update the existing window every time I plot something, instead of creating a new one.

8
  • 5
    have you try matplotlib interactive mode with plt.ion() before plt.show()? It should then be non-blocking as each plot is spawned into a child thread.
    – Anzel
    Commented Feb 1, 2015 at 23:41
  • @Anzel I just tried it, but it seems to make no difference.
    – opetroch
    Commented Feb 1, 2015 at 23:52
  • 3
    How are you running your script? If I run your example code from the terminal/command prompt, it seems to work fine, but I think I've had trouble in the past when trying to do things like this from the IPython QtConsole or IDEs.
    – Marius
    Commented Feb 2, 2015 at 0:10
  • 1
    @Marius Aha!! You are right. Indeed I am running it from the console of my IDE (PyCharm). When running it from the cmd prompt, plt.show(block=False), works fine! Will I be asking too much if I ask you if you have found any idea/solution to that? Thanks a lot!
    – opetroch
    Commented Feb 2, 2015 at 0:19
  • I don't really know sorry. I don't really understand the details of how matplotlib interacts with the console, so I generally just switch to running from the command prompt if I need to do this stuff with matplotlib.
    – Marius
    Commented Feb 2, 2015 at 0:29

10 Answers 10

263

I spent a long time looking for solutions, and found this answer.

It looks like, in order to get what you (and I) want, you need the combination of plt.ion(), plt.show() (not with block=False) and, most importantly, plt.pause(.001) (or whatever time you want). The pause is needed because the GUI events happen while the main code is sleeping, including drawing. It's possible that this is implemented by picking up time from a sleeping thread, so maybe IDEs mess with that—I don't know.

Here's an implementation that works for me on python 3.5:

import numpy as np
from matplotlib import pyplot as plt

def main():
    plt.axis([-50,50,0,10000])
    plt.ion()
    plt.show()

    x = np.arange(-50, 51)
    for pow in range(1,5):   # plot x^1, x^2, ..., x^4
        y = [Xi**pow for Xi in x]
        plt.plot(x, y)
        plt.draw()
        plt.pause(0.001)
        input("Press [enter] to continue.")

if __name__ == '__main__':
    main()
10
  • 8
    Your answer helped me a lot in solving a similar issue I was having. Previously, I had plt.draw followed by plt.show(block = False) but then it stopped working: Figure not responding, closing it crashed iPython. My solution was removing every instance of plt.draw() and replacing it with plt.pause(0.001). Instead of having it followed by plt.show(block = False) like plt.draw was before, it was preceded by plt.ion() and plt.show(). I now have a MatplotlibDeprecationWarning but it let me plot my figures, so I'm happy with this solution.
    – blue_chip
    Commented Mar 8, 2016 at 6:06
  • 3
    Note that in python 2.7, you need to use raw_input not input. See here
    – Chris
    Commented Aug 17, 2016 at 23:11
  • Really useful workaround when the reactive "animate" approach is not possible! Anyone know how to get rid of the deprecation warning? Commented Sep 5, 2017 at 0:22
  • 4
    I had to increase the pause to 0.1 seconds for this to work
    – Eddy
    Commented Nov 15, 2019 at 10:06
  • 3
    @krs013: I remember this worked for me in the past. But, now this works without using either plt.ion() or plt.draw(). I don't know if something has changed in matplotlib versions (I am using the latest version 3.1.2)
    – s.ouchene
    Commented Dec 24, 2019 at 11:36
51

A simple trick that works for me is the following:

  1. Use the block = False argument inside show: plt.show(block = False)
  2. Use another plt.show() at the end of the .py script.

Example:

import matplotlib.pyplot as plt

plt.imshow(add_something)
plt.xlabel("x")
plt.ylabel("y")

plt.show(block=False)

#more code here (e.g. do calculations and use print to see them on the screen

plt.show()

Note: plt.show() is the last line of my script.

6
  • 22
    This produces (for me, on Linux, Anaconda, Python 2.7, default backend) a blank window which remains blank until the very end of execution, when it finally gets filled in. Not useful for updating a plot in the midst of execution. :-(
    – sh37211
    Commented May 4, 2017 at 3:05
  • @sh37211 Not sure what your goal. In some cases that you try to plot something but after the plot command you have other commands, then this is useful since it allows you to plot and get the other commands executed. See this post for more on this: stackoverflow.com/questions/458209/…. If you want to update a plot then it should be another way.
    – seralouk
    Commented May 4, 2017 at 13:48
  • 1
    @sh37211 I get the same symptom (windows10/python 3.8.7, & Linux mint/python 3.8.5) using just plt.show(block=False). In both cases I get the desired plot update using plt.show(block=False);plt.pause(0.01).
    – marzetti
    Commented Mar 31, 2021 at 1:53
  • only method worked for me like a charm Commented Dec 17, 2021 at 0:34
  • Same as @sh37211 on Windows(10). I didn't need the last plt.show() Still, an interesting solution. Thank you.
    – El Bachir
    Commented Dec 20, 2021 at 0:47
22

You can avoid blocking execution by writing the plot to an array, then displaying the array in a different thread. Here is an example of generating and displaying plots simultaneously using pf.screen from pyformulas 0.2.8:

import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()

canvas = np.zeros((480,640))
screen = pf.screen(canvas, 'Sinusoid')

start = time.time()
while True:
    now = time.time() - start

    x = np.linspace(now-2, now, 100)
    y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
    plt.xlim(now-2,now+1)
    plt.ylim(-3,3)
    plt.plot(x, y, c='black')

    # If we haven't already shown or saved the plot, then we need to draw the figure first...
    fig.canvas.draw()

    image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
    image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))

    screen.update(image)

#screen.close()

Result:

Sine animation

Disclaimer: I'm the maintainer for pyformulas.

Reference: Matplotlib: save plot to numpy array

15

Live Plotting

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
# plt.axis([x[0], x[-1], -1, 1])      # disable autoscaling
for point in x:
    plt.plot(point, np.sin(2 * point), '.', color='b')
    plt.draw()
    plt.pause(0.01)
# plt.clf()                           # clear the current figure

if the amount of data is too much you can lower the update rate with a simple counter

cnt += 1
if (cnt == 10):       # update plot each 10 points
    plt.draw()
    plt.pause(0.01)
    cnt = 0

Holding Plot after Program Exit

This was my actual problem that couldn't find satisfactory answer for, I wanted plotting that didn't close after the script was finished (like MATLAB),

If you think about it, after the script is finished, the program is terminated and there is no logical way to hold the plot this way, so there are two options

  1. block the script from exiting (that's plt.show() and not what I want)
  2. run the plot on a separate thread (too complicated)

this wasn't satisfactory for me so I found another solution outside of the box

SaveToFile and View in external viewer

For this the saving and viewing should be both fast and the viewer shouldn't lock the file and should update the content automatically

Selecting Format for Saving

vector based formats are both small and fast

  • SVG is good but coudn't find good viewer for it except the web browser which by default needs manual refresh
  • PDF can support vector formats and there are lightweight viewers which support live updating

Fast Lightweight Viewer with Live Update

For PDF there are several good options

  • On Windows I use SumatraPDF which is free, fast and light (only uses 1.8MB RAM for my case)

  • On Linux there are several options such as Evince (GNOME) and Ocular (KDE)

Sample Code & Results

Sample code for outputing plot to a file

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(2 * x)
plt.plot(x, y)
plt.savefig("fig.pdf")

after first run, open the output file in one of the viewers mentioned above and enjoy.

Here is a screenshot of VSCode alongside SumatraPDF, also the process is fast enough to get semi-live update rate (I can get near 10Hz on my setup just use time.sleep() between intervals) pyPlot,Non-Blocking

14

A lot of these answers are super inflated and from what I can find, the answer isn't all that difficult to understand.

You can use plt.ion() if you want, but I found using plt.draw() just as effective

For my specific project I'm plotting images, but you can use plot() or scatter() or whatever instead of figimage(), it doesn't matter.

plt.figimage(image_to_show)
plt.draw()
plt.pause(0.001)

Or

fig = plt.figure()
...
fig.figimage(image_to_show)
fig.canvas.draw()
plt.pause(0.001)

If you're using an actual figure.
I used @krs013, and @Default Picture's answers to figure this out
Hopefully this saves someone from having launch every single figure on a separate thread, or from having to read these novels just to figure this out

3
  • When using this in a loop ( to update the image constantly) I noticed bad executions times. Adding plt.clf() , clearing all the drawn images at each iterations seems to fix the problem.
    – Axl
    Commented Apr 13, 2021 at 2:18
  • 2
    That is because not clearing the image is a memory leak and will cause matplotlib to try and plot all of your existing iterations which will slow down your execution
    – iggy12345
    Commented Apr 13, 2021 at 12:10
  • This worked for me in VS Code. Though, it wouldn't let me close any of the plots by clicking on the X for some reason.
    – swimfar2
    Commented Mar 6, 2023 at 17:45
10

I figured out that the plt.pause(0.001) command is the only thing needed and nothing else.

plt.show() and plt.draw() are unnecessary and / or blocking in one way or the other. So here is a code that draws and updates a figure and keeps going. Essentially plt.pause(0.001) seems to be the closest equivalent to matlab's drawnow.

Unfortunately those plots will not be interactive (they freeze), except you insert an input() command, but then the code will stop.

The documentation of the plt.pause(interval) command states:

If there is an active figure, it will be updated and displayed before the pause...... This can be used for crude animation.

and this is pretty much exactly what we want. Try this code:

import numpy as np
from matplotlib import pyplot as plt

x = np.arange(0, 51)               # x coordinates  
         
for z in range(10, 50):

    y = np.power(x, z/10)          # y coordinates of plot for animation

    plt.cla()                      # delete previous plot
    plt.axis([-50, 50, 0, 10000])  # set axis limits, to avoid rescaling
    plt.plot(x, y)                 # generate new plot
    plt.pause(0.1)                 # pause 0.1 sec, to force a plot redraw

1
  • nice ! worked for me. Commented Mar 22, 2024 at 0:15
3

Iggy's answer was the easiest for me to follow, but I got the following error when doing a subsequent subplot command that was not there when I was just doing show:

MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance. In a future version, a new instance will always be created and returned. Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.

In order to avoid this error, it helps to close (or clear) the plot after the user hits enter.

Here's the code that worked for me:

def plt_show():
    '''Text-blocking version of plt.show()
    Use this instead of plt.show()'''
    plt.draw()
    plt.pause(0.001)
    input("Press enter to continue...")
    plt.close()
2

The Python package drawnow allows to update a plot in real time in a non blocking way.
It also works with a webcam and OpenCV for example to plot measures for each frame.
See the original post.

1

For subplots, I modified @krs013's example as below. One has to clear the panel in each loop and then draw.

from matplotlib import pyplot as plt

plt.ion()

def plot():
    x = plt.np.arange(-50, 51)
    fig, [ax1, ax2] = plt.subplots(ncols=2)
    fig.canvas.toolbar.zoom()
    for pow in range(1, 5):   # plot x^1, x^2, ..., x^4
        ax1.clear()
        ax1.plot(x, x**pow)
        plt.draw()
        input("Press [enter] to continue.")

plot()

The pause is not needed. enter image description here

0

Substitute the backend of matplotlib can solve my problem.
Write the bellow command before import matplotlib.pyplot as plt.
Substitute backend command should run first.

import matplotlib
matplotlib.use('TkAgg')

My answer come from Pycharm does not show plot

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.