这方面很多人没接触过,所以我们先讲背景,再说说其他博客的情况,接着才是上效果图与代码,最后解析。
五年前,我接触到一个项目,本想用matplotlib做不断迭代的数据的监控,但是没有成功,折线图更新速度很慢,转而使用pyqtgraph库来实现数据的快速更新。
这次的切入点是matplotlib的animation模块,该模块一般会被用来做动画演示。各个论坛上讲解该模块时使用的例子往往是构造一个横坐标x的序列,再写出一个f(x),使用动画模块将x快速更新,生成不同的y=f(x),高速刷新窗口中的点(x,y),其中写的比较好的是Python+Matplotlib制作动画。
但显然,动画的长度是有限的,函数是确定的。而物联网数据监控的数据长度是无限的,函数可能有,也可能没有,本着有问题找文档的原则,我查到了官方示波器满足我们的需求。本文会对例程做一些简化,加些注释,讲解如何通过animation将实时数据以高刷新率显示到界面上。
import numpy as np
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import matplotlib.animation as animation
class Scope:
def __init__(self, ax):
#导入一个坐标系
self.ax = ax
#确定t与y的初始值
self.tdata = [0]
self.ydata = [0]
#以t为横坐标,以y为纵坐标画2D折线图
self.line = Line2D(self.tdata, self.ydata)
#将折线图导入坐标系中
self.ax.add_line(self.line)
def update(self, y):
#t每时每刻都在增加,为了使曲线完整,
#需要在每次数据更新时重新设置xlim,并更新
self.maxt=self.tdata[-1]+1
self.ax.set_xlim(0, self.maxt)
#重绘子图画布,使坐标轴的改变生效
#绘图程序很浪费时间,最好不要在update里加入其他绘图函数
#大家可以尝试隐藏这段代码对比速度
self.ax.figure.canvas.draw()
#将新的t与y放到数列中,重写折线数值
self.tdata.append(self.maxt)
self.ydata.append(y)
self.line.set_data(self.tdata,self.ydata)
#返回被更新的折线
return self.line,
#构造生成器,每次被调用都会产生一个随机值
#这个随机值可以用各种设备输出值来替代
def emitter():
while True:
yield np.random.rand(1)
#生成一个包含子图的figure,并返回figure,axes元组
fig, ax = plt.subplots()
#使用ax作为被不断更新的坐标系
scope = Scope(ax)
# 使用动画模块,图像为fig保持不变(可以简单地认为是背景),
# 每过一个interval(ms),会调用一次生成器emitter,并将生成的值传入scope.update中
# scope.update会返回一条新的折线,该模块会将新的折线绘制在图像中并显示出来。
# blit用来开启某种动画的渲染
ani = animation.FuncAnimation(fig, scope.update, emitter, interval=10,
blit=True)
#制作gif
#ani.save('test.gif',writer='pillow',fps=10)
plt.show()
Emmm,其实该说的都已经写在备注里了,如果您对yield,生成器等相关概念有疑惑,请尝试努力学习这方面内容。需要注意的是,之所以动画更新速度很快,是因为动画只更新图像的一小部分内容。如果在upadte()中加入self.ax.figure.canvas.draw()等函数来更新图像的其他部分,会增加很大的时间消耗,所以无特别需求,请勿添加。
matplotlib动画可以应用在很多地方,掌握了动画的核心要点,就可以让自己的演示图片骚到飞起。下图为动画在机器学习可视化方面的应用。