Python 实时生成曲线的两种方法-Matplotlib/Pyqtgraph

发布时间 2023-04-24 09:49:12作者: 可乐芬达

前言

Matplotlib 更倾向于制作出版质量的图形,对 matlab 程序员来说更直观。
pyqtgraph 不像 matplotlib 那样完整/成熟,但运行速度要快得多,而且pyqtgraph 旨在用于数据采集和分析应用程序,对于 python/qt 程序员来说更直观。
Matplotlib(据我所知)不包括许多 pyqtgraph 的功能,例如图像交互、体积渲染、参数树、流程图等。

第一种方式 Matplotlib

import threading
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.lines as line
import numpy as np
import random
import time
 
# 定义画布
fig = plt.figure(figsize=(10,6), dpi=80)
ax = plt.subplot(211,ylim=(0,5))
bx = plt.subplot(212,ylim=(0,5))
line = line.Line2D([], [])  # 绘制直线

obsX = []
obsY = []

# 初始化图像
def plot_init():
    ax.add_line(line)
    return line, # 必须加逗号,否则会报错(TypeError: 'Line2D' object is not iterable)
 
# 更新图像(animation会不断调用此函数刷新图像,实现动态图的效果)
def plot_update(i):
    global obsX
    global obsY

    if len(obsX) < 100:
        ax.set_xlim(min(obsX),max(obsX)+30)  # 横坐标范围(横坐标的范围和刻度可根据数据长度更新)
    else:
        ax.set_xlim(obsX[-80],max(obsX) * 1.2)  # 横坐标范围(横坐标的范围和刻度可根据数据长度更新)
    
    ax.set_ylim(min(obsY), max(obsY)+10)  # 纵坐标范围(横坐标的范围和刻度可根据数据长度更新)
    ax.set_title("CPU Loading",fontsize=8)  # 设置title

    bx.set_ylim(min(obsY), max(obsY)+10)  # 纵坐标范围(横坐标的范围和刻度可根据数据长度更新)
    bx.set_title("Memory Loading",fontsize=8)  # 设置title

    line.set_xdata(np.array(obsX))  # 更新直线的数据
    line.set_ydata(np.array(obsY))  # 更新直线的数据
	# 大标题(若有多个子图,可为其设置大标题)
    plt.suptitle('System Loading',fontsize=8)
    # 重新渲染子图
    ax.figure.canvas.draw()  # 必须加入这一行代码,才能更新title和坐标!!!
    return line,  # 必须加逗号,否则会报错(TypeError: 'Line2D' object is not iterable)
 
# 绘制动态图
ani = animation.FuncAnimation(fig,   # 画布
							  plot_update,  # 图像更新
                              init_func=plot_init,  # 图像初始化
                              frames=1,
                              interval=30,  # 图像更新间隔
                              blit=True)
 
# 数据更新函数
def dataUpdate_thead():
    global obsX
    global obsY
    i = 1
    while True:  # 为了方便测试,让数据不停的更新
        obsX.append(i)
        obsY.append(random.randrange(100, 200))
        i += 1
        time.sleep(1)
 
# 为数据更新函数单独创建一个线程,与图像绘制的线程并发执行
ad_rdy_ev = threading.Event()
ad_rdy_ev.set()  # 设置线程运行
t = threading.Thread(target=dataUpdate_thead, args=()) # 更新数据,参数说明:target是线程需要执行的函数,args是传递给函数的参数)
t.daemon = True
t.start()  # 线程执行
 
plt.show() # 显示图像

image

第二种方式 Pyqtgraph

import sys
import time
import datetime
import numpy as np
import pyqtgraph as pg
import random
from pyqtgraph.Qt import QtCore, QtWidgets

class TimeAxisItem(pg.AxisItem):
    """Internal timestamp for x-axis"""
    def __init__(self, *args, **kwargs):
        super(TimeAxisItem, self).__init__(*args, **kwargs)

    def tickStrings(self, values, scale, spacing):
        """Function overloading the weak default version to provide timestamp"""

        return [QtCore.QTime().currentTime().addMSecs(value).toString('hh:mm:ss') for value in values]

class App(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(App, self).__init__(parent)

        #### Create Gui Elements ###########
        self.mainbox = QtWidgets.QWidget()
        self.setCentralWidget(self.mainbox)
        self.mainbox.setLayout(QtWidgets.QVBoxLayout())

        self.canvas = pg.GraphicsLayoutWidget(show=True, size=(600,300))
        self.mainbox.layout().addWidget(self.canvas)

        self.label = QtWidgets.QLabel()
        self.mainbox.layout().addWidget(self.label)

        #  line plot
        self.cpuplot = self.canvas.addPlot(title="CPU Loading", axisItems={'bottom': TimeAxisItem(orientation='bottom')})
        self.p1 = self.cpuplot.plot(pen='y')

        self.canvas.nextRow()

        self.memplot = self.canvas.addPlot(title="Memory Loading", axisItems={'bottom': TimeAxisItem(orientation='bottom')})
        self.p2 = self.memplot.plot(pen='r')

        #### Set Data  #####################
        self.X = []
        self.Y = []

        self.counter = 0
        self.fps = 0.
        self.lastupdate = time.time()

        #### Start  #####################
        self._update()

    def _update(self):
        
        self.X.append(datetime.datetime.now().strftime('%H:%M:%S').replace(':','.'))
        self.Y.append(random.randrange(100, 200))

        self.xdata = np.array(self.X)
        self.ydata = np.array(self.Y)
        
        # self.img.setImage(self.data)
        self.p1.setData(self.ydata)
        self.p2.setData(self.ydata)

        now = time.time()
        dt = (now-self.lastupdate)
        if dt <= 0:
            dt = 0.000000000001
        fps2 = 1.0 / dt
        self.lastupdate = now
        self.fps = self.fps * 0.9 + fps2 * 0.1
        tx = 'Mean Frame Rate:  {fps:.3f} FPS'.format(fps=self.fps )
        self.label.setText(tx)
        QtCore.QTimer.singleShot(1, self._update)
        self.counter += 1
        # time.sleep(1)


if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    thisapp = App()
    thisapp.setWindowTitle(u'System Loading')
    thisapp.show()    
    sys.exit(app.exec())

image