前言
matplotlib中可以显示各种各种的图标,如折线、直方图、散点图、柱状图、饼图等,还可以显示3D图像,所以在很多场合都可以使用。
那么如果我们想要使图表动态化显示起来,该如何实现呢?
如下效果:
配置:
平台:windows
工具:visual studio code
语言:python
库:matplotlib、PyQt5
1、显示静态图表
用matplotlib显示一张图表非常简单,如下:
def on_btn_chart1_click(self):
"""显示图表"""
fig,ax=plt.subplots()
fig.suptitle('pic1')
data=np.random.randn(100)
self.ln=plt.plot(data)
plt.show()
显示效果:
matplotlib利用subplots创建了一个Figure的实例,Figure就是用于显示图表或图像的窗口。
而plot产生了一个二维折线图表格式,折线图表的数据来自于numpy产生的随机数。
2、动态图表
如果我们要实现动态图表,其实有多种方式,因为matplotlib产生的图表其实就是一帧帧图片,麻烦的方式是我们用循环产生多张图片,然后将图片组合起来生成动画。
但matplotlib本身提供了直接产生动画的类,即Animation。
A base class for Animations.
This class is not usable as is, and should be subclassed to provide needed
behavior.
但是Animation不能直接用,它是一个基类,我们可以使用matplotlib提供的FuncAnimation类。
TimedAnimation
subclass that makes an animation by repeatedly calling a function func.
Fun从Animation是在TimedAnimation的基础上,可以直接调用函数来更新数据。
#实例化Figure动画
self.ani=FuncAnimation(self.fig,self.update,
frames=range(50),
#init_func=self.init,
interval=100,
repeat=False,
blit=True)
如上,我们新建FuncAnimation的实例,注意其参数,我们一一解释下:
fig:即Figure,我们可以创建一个Figure,然后填入此参数
func:函数,即更新数据的函数,动态图表的每一帧图表的数据,由此函数返回,但注意,返回的不仅仅是数据,而是一个Artist:
artists : list
Each list entry is a collection of.Artist
objects that are made visible on the corresponding frame. Other artists are made invisible.
比如本文中的示例中,我们返回的是matplotlib.plot()是折线数据:
大致如下:
list[Line2D]:
return gca().plot(
*args,
scalex=scalex,
scaley=scaley,
**({"data": data} if data is not None else {}),
**kwargs,
)
可以看到,其返回的是一个Line2D的数据格式。
def update(self,frame):
"""Figure图表更新数据"""
print(frame)
data=np.random.randn(100)
self.ln=plt.plot(data)
return self.ln
init_func:初始函数,这是可选项,即动画图表开始时的函数,其返回格式与func是一样的。
frames:
frames参数可以理解是动画中帧的数量,比如设置为一个整数50,那么在每一次间隔后,都会传递一个整数给func,传递的数字是当前帧的索引,通常情况是递增。如果设置了动画循环播放,那么在帧索引达到最大后又会从头开始。
frames: 可迭代对象、整数、生成器函数或None,可选
这是传递给func和动画每个帧的数据源。
如果是一个可迭代对象,那么只需使用提供的值。如果可迭代对象有长度,它将覆盖save_count关键字参数。
如果是一个整数,则等同于传递“range(frames)”。
如果是一个生成器函数,则必须具有以下签名:
def gen_function() -> obj
如果为None,则等同于传递“itertools.count”。
在所有这些情况下,frames中的值只是简单地传递给用户提供的func,因此可以是任何类型。
interval:动画间隔,默认为200ms,可以自定义。
repeat:标志位,如果设为True,则动画重复,反之,设为False即可。
blit:标志位,其作用如下:
在Matplotlib的FuncAnimation中,blit参数用于控制动画的绘制方式。Blit,全称为“位块传输”(bitmap block transfer),是一种图形处理技术,用于将一个图像的一部分拷贝到另一个位置。
当blit参数设置为True时,Matplotlib只会重新绘制那些改变的部分,而不是每次都重新绘制整个图像。这可以提高动画的绘制速度,特别是对于大规模数据集或复杂的图形。
然而,对于某些图形元素,如文本或线条,blit可能无法正确地更新它们。在这种情况下,你可能需要将blit设置为False,以便于Matplotlib能够正确地更新这些元素。
总的来说,选择是否使用blit取决于你的具体需求和图形的复杂性。在大多数情况下,使用blit可以提高动画的性能。
看一下启用blit和不启用blit的区别:
blit=True:
blit=False:
基本上就是以上参数,下面看一下完整程序:
import sys,os,time
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.animation import FuncAnimation
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
class Example(QWidget):
def __init__(self):
super().__init__()
print(matplotlib.__version__)
self.initUI()
def initUI(self):
layout=QVBoxLayout()
self.setLayout(layout)
self.btn0=QPushButton('开始动画',self)
self.btn0.setGeometry(40,40,100,20)
self.btn0.clicked.connect(self.on_btn0_click)
self.btn1=QPushButton('停止',self)
self.btn1.setGeometry(40,70,100,20)
self.btn1.clicked.connect(self.on_btn1_click)
self.btn2=QPushButton('继续',self)
self.btn2.setGeometry(40,100,100,20)
self.btn2.clicked.connect(self.on_btn2_click)
self.btn_chart1=QPushButton('显示图表',self)
self.btn_chart1.setGeometry(150,40,100,20)
self.btn_chart1.clicked.connect(self.on_btn_chart1_click)
#fig=plt.figure()
#canvas=FigureCanvas(fig)
#layout.addWidget(canvas)
self.setWindowTitle('plot')
self.setGeometry(300,300,600,400)
self.show()
matplotlib.rcParams['font.sans-serif'] = 'SimHei' # 设置默认字体为黑体
matplotlib.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
def init(self):
"""Figure图表初始参数"""
#设置图像的X轴的范围
self.ax.set_xlim(0, 2 * np.pi)
self.ax.set_ylim(-5,5)
data=np.random.randn(500)
self.ln=plt.plot(data)
return self.ln
def update(self,frame):
"""Figure图表更新数据"""
print(frame)
data=np.random.randn(100)
self.ln=plt.plot(data)
return self.ln
def on_btn0_click(self,frame):
self.fig, self.ax = plt.subplots()
#设置Figure的窗口标题
self.fig.canvas.manager.set_window_title('动态化图表')
#设置Figure中的图像的标题
self.fig.suptitle('动态化图表')
#实例化Figure动画
self.ani=FuncAnimation(self.fig,self.update,
frames=50,
#init_func=self.init,
interval=100,
repeat=False,
blit=False)
plt.show()
#self.ani.save('AnyCAD-3DUI\\mymovie.mp4',fps=10)
def on_btn2_click(self):
"""resume:继续动画"""
try:
self.ani.resume()
except Exception as e:
QMessageBox.warning(self,'警告',str(e))
def on_btn1_click(self,frame):
"""pause:暂停动画"""
try:
self.ani.pause()
except Exception as e:
QMessageBox.warning(self,'警告',str(e))
def on_btn_chart1_click(self):
"""显示图表"""
fig,ax=plt.subplots()
fig.suptitle('pic1')
data=np.random.randn(100)
self.ln=plt.plot(data)
plt.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
注:说明一下,本文是笔者记录为以后涉及到相同问题时可作参考使用,但如果能对你有所帮助,那么也是极好的。