使用Matplotlib以非阻塞方式绘图

时间:2015-02-01 23:23:01

标签: python matplotlib plot

过去几天我一直在玩Numpy和matplotlib。我在尝试使matplotlib绘制函数而不阻塞执行时遇到问题。我知道在这里已经有很多主题提出了类似的问题,而且我已经搜索了很多但是还没有成功地完成这项工作。

我尝试过使用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。我忘了说我想在每次绘制内容时更新现有窗口,而不是创建一个新窗口。

8 个答案:

答案 0 :(得分:123)

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

看起来,为了得到你(和我)想要的东西,你需要plt.ion()plt.show()(不是blocking=False的组合,不推荐使用),大多数重要的是,plt.pause(.001)(或任何你想要的时间)。需要pause,因为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()

答案 1 :(得分:15)

对我有用的一个简单方法如下:

  1. 在show中使用 block = False 参数: plt.show(block = False)
  2. 在.py脚本的末尾使用另一个plt.show()
  3. 示例

    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() 是我脚本的最后一行。

答案 2 :(得分:11)

您可以通过将绘图写入数组,然后在不同的线程中显示数组来避免阻止执行。以下是使用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()

结果:

Sine animation

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

参考:Matplotlib: save plot to numpy array

答案 3 :(得分:2)

我发现plt.pause(0.001)命令是唯一需要的东西,别无其他。

plt.show()和plt.draw()是不必要的和/或以一种或另一种方式阻塞。因此,这是一个绘制,更新图形并继续运行的代码。本质上plt.pause(0.001)似乎与matlab的drawow最接近。

不幸的是,除非您插入input()命令,否则这些图不会是交互式的(冻结),但是代码将停止。

plt.pause(interval)命令的文档说明:

如果有活动图形,它将在暂停之前更新并显示...... 这可以用于原始动画。

这几乎正是我们想要的。尝试以下代码:

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

答案 4 :(得分:1)

实时绘图

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,Non-Blocking

答案 5 :(得分:0)

这些答案很多都是夸张的,据我所知,答案并不是那么难理解。

如果需要,您可以使用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的答案来解决这个问题
希望这可以使某人不必在一个单独的线程上启动每个人物,也不必从阅读这些小说中找出答案

答案 6 :(得分:0)

Python包drawow允许以非阻塞方式实时更新绘图。
它还可以与网络摄像头和OpenCV配合使用,例如绘制每个帧的度量。
参见original post

答案 7 :(得分:0)

Iggy's answer是我最容易遵循的,但是当我执行subplot时不存在的后续show命令出现以下错误:

  

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

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

以下是对我有用的代码:

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