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

前言

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
from collections import deque

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 [time.strftime("%H:%M:%S", time.localtime(value/1000)) for value in values]

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

        self.start_timestamp = int(time.time()*1000)

        #### 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 = deque([], 50)
        self.Y = deque([], 50)

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

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

    def _update(self):
        
        self.X.append(int(time.time()*1000))
        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.xdata, self.ydata)
        self.p2.setData(self.xdata, 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

Ref:
python matplotlib模块: FuncAnimation(动态模拟)

posted @ 2023-04-24 09:45  可乐芬达  阅读(2213)  评论(0编辑  收藏  举报