使用Matplotlib以非阻塞方式绘制


138

最近几天,我一直在玩Numpy和matplotlib。我在尝试使matplotlib绘制函数而不阻止执行时遇到问题。我知道这里已经有很多线程在问类似的问题,并且我已经在Google上搜索了很多,但是还没有成功完成这项工作。

我曾尝试按照某些人的建议使用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。我忘了说,我想在每次绘制图形时都更新现有窗口,而不是创建一个新窗口。


1
有你尝试matplotlib交互模式与plt.ion()之前plt.show()?然后,在将每个图生成到子线程中时,它应该是非阻塞的。
Anzel

@Anzel我刚刚尝试过,但似乎没有什么区别。
opetroch'2

3
您如何运行脚本?如果我从终端/命令提示符处运行示例代码,它似乎可以正常工作,但是我认为过去尝试从IPython QtConsole或IDE执行此类操作时遇到了麻烦。
Marius

1
@Marius Aha !! 你是对的。实际上,我是从IDE(PyCharm)的控制台运行它的。从cmd提示符下运行时,plt.show(block = False)运行正常!如果我问你是否有任何想法/解决方案,我会问得太多吗?非常感谢!
opetroch

我真的不知道抱歉 我真的不太了解matplotlib如何与控制台交互的详细信息,因此如果需要使用,我通常只是从命令提示符切换到运行matplotlib
Marius

Answers:


167

我花了很长时间寻找解决方案,并找到了答案

看起来,为了获得您(和我)想要的东西,您需要将plt.ion()plt.show()(而不是与block=False)结合在一起,最重要的是,plt.pause(.001)(或您想要的任何时间)结合在一起。该暂停是必须的,因为GUI事件,而主代码正在睡觉,包括绘图发生。这很可能是通过从休眠线程中获取时间来实现的,所以IDE可能会为此惹恼我不知道。

这是对我适用于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()

3
您的回答对解决我遇到的类似问题很有帮助。以前,我一直plt.draw关注,plt.show(block = False)但是后来它停止工作:图形没有响应,关闭它使iPython崩溃了。我的解决方案是删除的所有实例,plt.draw()然后将其替换为plt.pause(0.001)。它没有plt.show(block = False)plt.draw以前那样跟在后面,而是跟在plt.ion()和之前plt.show()。我现在有一个,MatplotlibDeprecationWarning但是它可以让我绘制我的数字,因此我对这种解决方案感到满意。
blue_chip

3
请注意,在python 2.7中,您需要使用raw_inputnot input。看到这里
克里斯(Chris)

当无法使用反应性“动画”方法时,这是非常有用的解决方法!有人知道如何摆脱弃用警告吗?
Frederic Fortier

请有人告诉我为什么在plt.show之前尝试添加plt.ion时会出现冻结的命令提示符吗?
加布里埃尔·奥古斯托

@GabrielAugusto我不确定是什么原因造成的,也不确定您的意思。我刚刚在Python 3.6中测试了此示例,但该示例仍然有效。如果使用相同的模式并且冻结,则可能是您的安装有问题。您应该先检查法线绘图是否起作用。如果您尝试了其他方法,则注释中没有太多要做。无论哪种情况,您都可以考虑问一个单独的问题。
krs013

23

一个对我有用的简单技巧如下:

  1. 在show内使用block = False参数:plt.show(block = False)
  2. 在.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()

注意plt.show()是我脚本的最后一行。


7
这会产生一个空白窗口(对我来说,在Linux,Anaconda,Python 2.7,默认后端上),直到执行完毕(直到最后填满)为止,该空白窗口一直保持空白。这对于在执行过程中更新绘图无用。:-(
sh37211 '17

@ sh37211不确定您的目标是什么。在某些情况下,您尝试绘制某些内容,但在plot命令之后还有其他命令,则这很有用,因为它允许您绘制并执行其他命令。有关更多信息,请参见此帖子:stackoverflow.com/questions/458209/…。如果要更新图,则应采用另一种方法。
seralouk

17

您可以通过将绘图写入数组,然后在另一个线程中显示该数组来避免阻塞执行。这是一个使用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()

结果:

正弦动画

免责声明:我是pypyulas的维护者。

参考:Matplotlib:将图保存到numpy数组


9

这些答案中有很多是超级夸张的,从我的发现中,答案并不是那么难理解。

您可以plt.ion()根据需要使用,但我发现使用plt.draw()同样有效

对于我的特定项目,我正在绘制图像,但是您可以使用plot()scatter()或其他任何一种来代替figimage(),这没关系。

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

要么

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

如果您使用的是实际数字。
我使用了@ krs013和@Default Picture的答案来解决这个问题,
希望这可以使某人不必在一个单独的线程上启动每个单独的角色,或者不必阅读这些小说就可以解决这个问题。


3

实时绘图

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

如果数据量太多,您可以通过一个简单的计数器降低更新率

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

程序退出后的保持图

这是我的实际问题,无法找到令人满意的答案,我想在脚本完成后未关闭的绘图(例如MATLAB),

如果您考虑一下,在脚本完成后,程序将终止,并且没有逻辑方式以这种方式保存绘图,因此有两个选择

  1. 阻止脚本退出(这是plt.show()而不是我想要的)
  2. 在单独的线程上运行图(太复杂)

这对我来说并不令人满意,所以我在盒子外面找到了另一个解决方案

SaveToFile和在外部查看器中查看

为此,保存和查看均应快速进行,查看器不应锁定文件,而应自动更新内容

选择保存格式

基于矢量的格式既小又快速

  • SVG不错,但是除了默认情况下需要手动刷新的Web浏览器之外,找不到合适的查看器
  • PDF可支持矢量格式,并且有支持实时更新的轻量级查看器

快速实时更新的轻量级查看器

对于PDF,有几个不错的选择

  • 在Windows上,我使用免费,快速,轻巧的SumatraPDF(我的机箱仅使用1.8MB RAM)

  • 在Linux上,有几种选择,例如Evince(GNOME)和Ocular(KDE)

示例代码和结果

用于将绘图输出到文件的示例代码

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")

第一次运行后,在上述其中一个查看器中打开输出文件并欣赏。

这是VSCode和SumatraPDF的屏幕截图,该过程也足够快以达到半实时更新率(我的设置可以time.sleep()在间隔之间使用时接近10Hz ) pyPlot,非阻塞


2

Iggy的答案对我来说是最容易遵循的,但是subplot当我执行刚在执行的后续命令时却遇到了以下错误show

MatplotlibDeprecationWarning:当前使用与先前轴相同的参数添加轴将重用较早的实例。在将来的版本中,将始终创建并返回一个新实例。同时,通过向每个轴实例传递唯一的标签,可以抑制此警告,并确保将来的行为。

为了避免此错误,它有助于在用户点击Enter后关闭(或清除)绘图。

这是对我有用的代码:

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()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.