I have been playing with Numpy and matplotlib in the last few days. I am having problems trying to make matplotlib plot a function without blocking execution. I know there are already many threads here on SO asking similar questions, and I ‘ve googled quite a lot but haven’t managed to make this work.
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 back end is Qt4Agg. Could you have a look at my code and tell me if you see something wrong? Here is my code. Thanks for any help.
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.
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()
回答 1
一个对我有用的简单技巧如下:
在show内使用block = False参数:plt.show(block = False)
在.py脚本的末尾使用另一个plt.show() 。
范例:
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()
A simple trick that works for me is the following:
Use the block = False argument inside show: plt.show(block = False)
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()
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()whileTrue:
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()
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()
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
回答 4
实时绘图
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 autoscalingfor point in x:
plt.plot(point, np.sin(2* point),'.', color='b')
plt.draw()
plt.pause(0.01)# plt.clf() # clear the current figure
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
block the script from exiting (that’s plt.show() and not what I want)
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)
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()
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()
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.