Python 使用 Matplotlib 以非阻塞方式绘图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28269157/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Plotting in a non-blocking way with Matplotlib
提问by opetroch
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.
最近几天我一直在玩 Numpy 和 matplotlib。我在尝试使 matplotlib 绘图成为一个函数而不阻止执行时遇到问题。我知道这里已经有很多线程在问类似的问题,我在谷歌上搜索了很多,但还没有设法使这项工作成功。
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.
我曾尝试按照某些人的建议使用 show(block=False) ,但我得到的只是一个冻结的窗口。如果我只是调用 show(),结果会正确绘制,但执行会被阻止,直到窗口关闭。从我读过的其他线程中,我怀疑 show(block=False) 是否有效取决于后端。这样对吗?我的后端是 Qt4Agg。你能看看我的代码并告诉我你是否看到了错误吗?这是我的代码。谢谢你的帮助。
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.
附注。我忘了说我想在每次绘制某些东西时更新现有窗口,而不是创建一个新窗口。
采纳答案by krs013
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 pauseis 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.
看起来,为了获得您(和我)想要的东西,您需要组合plt.ion()
, plt.show()
(而不是block=False
),最重要的是,plt.pause(.001)
(或您想要的任何时间)。该暂停是必须的,因为GUI事件,而主代码正在睡觉,包括绘图发生。这可能是通过从睡眠线程中获取时间来实现的,所以 IDE 可能会搞砸——我不知道。
Here's an implementation that works for me on python 3.5:
这是一个在 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()
回答by seralouk
A simple trick that works for me is the following:
一个对我有用的简单技巧如下:
- Use the block = Falseargument inside show: plt.show(block = False)
- Use another plt.show()at the endof the .py script.
- 在 show 中使用block = False参数:plt.show(block = False)
- 在 .py 脚本的末尾使用另一个 plt.show()。
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.
注意:plt.show()
是我脚本的最后一行。
回答by Default picture
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:
您可以通过将绘图写入数组,然后在不同线程中显示该数组来避免阻塞执行。这是使用pyformulas 0.2.8 中的pf.screen 同时生成和显示绘图的示例:
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:
结果:
Disclaimer: I'm the maintainer for pyformulas.
免责声明:我是 pyformulas 的维护者。
Reference: Matplotlib: save plot to numpy array
回答by Ali80
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),
这是我无法找到满意答案的实际问题,我想要在脚本完成后没有关闭的绘图(如 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)
- 阻止脚本退出(这是 plt.show() 而不是我想要的)
- 在单独的线程上运行绘图(太复杂了)
this wasn't satisfactory for me so I found another solution outside of the box
这对我来说并不令人满意,所以我在盒子外找到了另一个解决方案
SaveToFile and View in external viewer
SaveToFile 和在外部查看器中查看
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
基于矢量的格式既小又快
- SVGis good but coudn't find good viewer for it except the web browser which by default needs manual refresh
- PDFcan support vector formats and there are lightweight viewers which support live updating
- SVG很好,但除了默认需要手动刷新的 Web 浏览器之外,找不到合适的查看器
- PDF可以支持矢量格式,并且有支持实时更新的轻量级查看器
Fast Lightweight Viewer with Live Update
带实时更新的快速轻量级查看器
For PDFthere are several good options
对于PDF,有几个不错的选择
On Windows I use SumatraPDFwhich 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)
在 Windows 上,我使用免费、快速且轻便的SumatraPDF(我的情况仅使用 1.8MB RAM)
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)
这是 VSCode 与 SumatraPDF 的屏幕截图,该过程也足够快以获得半实时更新率(我的设置可以接近 10Hz,只需time.sleep()
在间隔之间使用)
回答by iggy12345
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
plt.ion()
如果你愿意,你可以使用,但我发现使用plt.draw()
同样有效
For my specific project I'm plotting images, but you can use plot()
or scatter()
or whatever instead of figimage()
, it doesn't matter.
对于我的特定项目,我正在绘制图像,但是您可以使用plot()
或scatter()
或其他任何东西来代替figimage()
,这无关紧要。
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
如果您使用的是实际数字。
我使用@krs013 和@Default Picture 的答案来解决这个问题
希望这可以避免有人在单独的线程上发布每个数字,或者不必阅读这些小说来解决这个问题
回答by Ismael EL ATIFI
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.
Python 包 drawow 允许以非阻塞方式实时更新绘图。
它还可以与网络摄像头和 OpenCV 配合使用,例如为每一帧绘制度量。
见原帖。
回答by Pro Q
Iggy's answerwas 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
:
Iggy 的回答对我来说是最容易subplot
理解的,但是在执行我刚刚执行时不存在的后续命令时出现以下错误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.
MatplotlibDeprecationWarning:使用与先前轴相同的参数添加轴当前会重用较早的实例。在未来的版本中,将始终创建并返回一个新实例。同时,通过将唯一标签传递给每个轴实例,可以抑制此警告并确保未来的行为。
In order to avoid this error, it helps to close (or clear) the plot after the user hits enter.
为了避免这个错误,它有助于在用户点击 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()