【问题标题】:Matplotlib animated plot includes previous dataMatplotlib 动画图包含以前的数据
【发布时间】:2023-04-09 03:32:01
【问题描述】:

我正在尝试使用 pylab 的 blitting 以快速帧速率为我的绘图设置动画;下面的代码似乎工作正常,但在旧数据之上绘制新数据而不是重新绘制,因此我最终得到一个填充线条而不是一条动画线条的图形(在每个子图中)。任何建议以尽可能快的帧速率获得一条动画线(在每个子图中)都非常感谢。

import pylab
import time
import threading
import random
import math

time_series, cos_series, sin_series = [], [], []
MAX = 100

# This generates new data for the plot

def data_generator():

    while True:
        time.sleep(0.1)
        ts = time.time()
        time_series.append(ts)
        cos_series.append(math.sin( ts ))
        sin_series.append(math.cos( ts ))

        if len(cos_series) > MAX:
            cos_series.pop(0)

        if len(sin_series) > MAX:
            sin_series.pop(0)

        if len(time_series) > MAX:
            time_series.pop(0)

if __name__ == '__main__':

    # Run the receiving function in a separate thread.

    thread = threading.Thread(target=data_generator)
    thread.daemon = True
    thread.start()

    fig = pylab.figure()

    ax = fig.add_subplot(211)
    bx = fig.add_subplot(212)

    ax.grid(True)
    bx.grid(True)

    print(len(time_series),len(sin_series),len(cos_series))

    fig.show()
    fig.canvas.draw()

    line1, = ax.plot(time_series, sin_series, '-.k', animated=True)
    line2, = bx.plot(time_series, cos_series, 'r+-', animated=True)

    ax.legend(['Sin(x)'])
    bx.legend(['Cos(x)'])

    ax.set_ylim([-1,1])
    bx.set_ylim([-1,1])

    background_a = [fig.canvas.copy_from_bbox(ax.bbox)]
    background_b = [fig.canvas.copy_from_bbox(bx.bbox)]

    timer = 0
    t_start = time.time()

    # Continuously update plot

    while True:

        time.sleep(0.1)

        line1.set_data(time_series,sin_series)
        ax.set_xlim([time_series[0],time_series[-1]])
        line2.set_data(time_series,cos_series)
        bx.set_xlim([time_series[0],time_series[-1]])

        ax.draw_artist(line1)
        bx.draw_artist(line2)

        fig.canvas.restore_region(background_a)
        fig.canvas.restore_region(background_b)

        fig.canvas.blit(ax.bbox)
        fig.canvas.blit(bx.bbox)

        timer += 1
        print('FPS = ',timer/(time.time() - t_start))

【问题讨论】:

  • 有什么理由不使用matplotlib.animation
  • 还有什么办法可以减少这里的代码量?
  • 没有充分的理由不使用 matplotlib.animation - 这是更快,还是更简洁的代码?
  • 所有的 blitting 和 timer 细节都已经处理好了,并且有一种干净的方法可以保存到 mp4。只需向它抛出一个函数和一些数据,你就可以开始了

标签: python animation matplotlib


【解决方案1】:

您的代码有两个问题。

首先,当你这样做时:

background_a = [fig.canvas.copy_from_bbox(ax.bbox)]
background_b = [fig.canvas.copy_from_bbox(bx.bbox)]

您不应该将缓冲区对象放在列表中 - restore_region 只是直接获取缓冲区对象,因此您应该这样做:

background_a = fig.canvas.copy_from_bbox(ax.bbox)
background_b = fig.canvas.copy_from_bbox(bx.bbox)

其次,在渲染循环中,您需要先恢复背景,然后再在顶部绘制任何更新的线条艺术家,否则您将始终在移动线条的顶部绘制背景。将这些行移到您的 draw_artist 调用上方,如下所示:

fig.canvas.restore_region(background_a)
fig.canvas.restore_region(background_b)

ax.draw_artist(line1)
bx.draw_artist(line2)

fig.canvas.blit(ax.bbox)
fig.canvas.blit(bx.bbox)

现在一切正常。

更新

如果您希望在动画期间也更新 x 轴,事情会变得有点复杂。首先,您需要将 x 轴设置为为两组轴设置动画:

ax = fig.add_subplot(211)
bx = fig.add_subplot(212)
ax.xaxis.set_animated(True)
bx.xaxis.set_animated(True)

轴边界框 (ax.bbox) 不包含刻度标签,因此为了在渲染循环期间获得足够大的区域来恢复,您需要缓存图形画布的更大区域,例如整个图形边界框:

figbackground = fig.canvas.copy_from_bbox(fig.bbox)

并恢复背景:

fig.canvas.restore_region(figbackground)

在每个时间点,您需要强制重新绘制 x 轴以及线条:

ax.draw_artist(line1)
bx.draw_artist(line2)
ax.xaxis.draw(fig.canvas.renderer)
bx.xaxis.draw(fig.canvas.renderer)

最后,当您执行 blitting 时,您需要使用包含刻度标签的轴剪贴框,而不是边界框,后者不包含:

fig.canvas.blit(ax.clipbox)
fig.canvas.blit(bx.clipbox)

随着这些更改,刻度标签和 x-gridlines 将得到更新,但到目前为止,我还没有弄清楚如何准确地获取 y-gridlines 和要正确绘制的图例。希望这能让您对如何执行此操作有所了解。

tcaswell 建议查看 Animation 类也是正确的 - 对于您的情况,它可能会变得更简单,尽管我认为了解 blitting 在引擎盖下的工作原理也很好。

【讨论】:

  • 这很好用,谢谢,但轴刻度标签不会更新,即它们固定在初始值 - 知道如何动态更新这些吗?
猜你喜欢
  • 1970-01-01
  • 2015-07-17
  • 2021-04-11
  • 1970-01-01
  • 1970-01-01
  • 2021-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多