天天看點

PyQt5 pyqtgraph 實時繪制資料1. pyqtgraph 簡介2. 實時繪制學習3. 小結

提到 GUI 繪圖,大家可能第一反應是 OpenGL 和 Matplotlib,但其實基于 Qt 平台還有個功能強大的 pyqtgraph 繪圖庫,不僅支援豐富的圖形種類,還能實時更新繪圖資料并進行互動式操作。

不同于網上其他文章或代碼講解,今天我們集中隻關注實時繪制資料功能的實作。為了更精準學習該 pyqtgraph 子產品功能,我們将參考官方給出的執行個體來邊學邊練。

1. pyqtgraph 簡介

1.1 pyqtgraph 特點

關于 pyqtgraph 與 Matplotlib 的對比,大緻要點如下:

  1. pyqtgraph 在畫圖方面不如 Matplotlib 功能完整和成熟,但運作更快
  2. Matplotlib 旨在繪制高品質圖像,pyqtgraph 則主要面向資料抓取和資料分析的應用
  3. 相比 Matplotlib,pyqtgraph 對 python 和 qt 程式設計更親和
  4. pyqtgraph 具備更好的圖像互動、3D展示等

1.2 pyqtgraph 安裝

一般配合 PyQt5 使用,這些都要預先安裝好,我們這裡隻提 pyqtgraph 相關:

pip install pyqtgraph
           

1.3 pyqtgraph 執行個體全集

官方專門給出了一個執行個體集合,包含了展示與源碼,非常友善學習,通過以下代碼來運作:

import pyqtgraph.examples
pyqtgraph.examples.run()
           

運作後,會出現如下 GUI 界面

PyQt5 pyqtgraph 實時繪制資料1. pyqtgraph 簡介2. 實時繪制學習3. 小結

今天我們主要關注實時繪制資料,找到左側目錄中的 “Scrolling plots”,單擊右側可以看到源碼

PyQt5 pyqtgraph 實時繪制資料1. pyqtgraph 簡介2. 實時繪制學習3. 小結

輕按兩下或者點選下方的 “Run Example” 便可展示運作效果:

PyQt5 pyqtgraph 實時繪制資料1. pyqtgraph 簡介2. 實時繪制學習3. 小結

特定截圖:

PyQt5 pyqtgraph 實時繪制資料1. pyqtgraph 簡介2. 實時繪制學習3. 小結

2. 實時繪制學習

結合着執行個體代碼和示範效果,我們可以看到有如下不同實時展示模式:

  • 模式1: 從 0 開始固定 x 軸數值範圍,資料在該範圍内向左移動展示
  • 模式2: 資料帶着 x 軸坐标一起向左移動展示
  • 模式3: 固定 x 軸數值右側範圍到 0,資料左移展示
  • 模式4: 左側固定從 0 開始,資料累積展示
  • 模式5: 資料範圍右側截止到 0,但仍可檢視大于 0 範圍

2.1 模式1: 固定 x 範圍,左移展示資料

2.1.1 模式1效果

PyQt5 pyqtgraph 實時繪制資料1. pyqtgraph 簡介2. 實時繪制學習3. 小結

2.1.2 執行個體1代碼

我們可以在執行個體彙總的代碼中将該部分代碼抽離出來,大緻如下:

import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np

win = pg.GraphicsLayoutWidget(show=True)
win.setWindowTitle('Scrolling Plots Mode 1')

p1 = win.addPlot()
data1 = np.random.normal(size=300)

curve1 = p1.plot(data1)


def update1():
    global data1, ptr1
    data1[:-1] = data1[1:]  # shift data in the array one sample left
    # (see also: np.roll)
    data1[-1] = np.random.normal()
    curve1.setData(data1)


timer = pg.QtCore.QTimer()
timer.timeout.connect(update1)
timer.start(50)


## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
    import sys

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()
           

注意,模式 1 中實時繪制效果的實作,是通過将資料清單中的資料整體左移實作的,關鍵語句就是 data1[:-1] = data1[1:],再通過計時器來綁定該左移資料的函數,最終達到了展示中的資料動态展示效果。

2.1.3 寫成 PlotWidget 形式

總結下模式 1 的原理:x 坐标資料不變化,對應的 y 資料設定個左移變換的函數,計時器信号綁定該左移資料的函數,把 y 資料能實時設定到圖中即可。

執行個體 1 中繪制圖的寫法比較少見,通常應用是通過 pyqtgraph.PlotWidget.plot() 來實作在控件中作圖再添加到 GUI 控件中,是以我們将采用 PlotWidget 的寫法來實作模式1的繪制,代碼如下:

__author__ = 'Ted'

from PyQt5.Qt import *
from pyqtgraph import PlotWidget
from PyQt5 import QtCore
import numpy as np
import pyqtgraph as pq


class Window(QWidget):
    def __init__(self):
        super().__init__()
        # 設定下尺寸
        self.resize(600,600)
        # 添加 PlotWidget 控件
        self.plotWidget_ted = PlotWidget(self)
        # 設定該控件尺寸和相對位置
        self.plotWidget_ted.setGeometry(QtCore.QRect(25,25,550,550))

        # 仿寫 mode1 代碼中的資料
        # 生成 300 個正态分布的随機數
        self.data1 = np.random.normal(size=300)

        self.curve1 = self.plotWidget_ted.plot(self.data1, name="mode1")

        # 設定定時器
        self.timer = pq.QtCore.QTimer()
        # 定時器信号綁定 update_data 函數
        self.timer.timeout.connect(self.update_data)
        # 定時器間隔50ms,可以了解為 50ms 重新整理一次資料
        self.timer.start(50)

    # 資料左移
    def update_data(self):
        self.data1[:-1] = self.data1[1:]
        self.data1[-1] = np.random.normal()
        # 資料填充到繪制曲線中
        self.curve1.setData(self.data1)


if __name__ == '__main__':
    import sys
    # PyQt5 程式固定寫法
    app = QApplication(sys.argv)

    # 将綁定了繪圖控件的視窗執行個體化并展示
    window = Window()
    window.show()

    # PyQt5 程式固定寫法
    sys.exit(app.exec())
           

我們在自己寫的代碼中重新設定了下視窗尺寸位置,資料還是按照執行個體中的寫法來完成的。

2.1.4 自寫模式1效果

PyQt5 pyqtgraph 實時繪制資料1. pyqtgraph 簡介2. 實時繪制學習3. 小結

2.2 資料随 x 軸一起左移

2.2.1 模式2效果

PyQt5 pyqtgraph 實時繪制資料1. pyqtgraph 簡介2. 實時繪制學習3. 小結

2.2.2 執行個體2代碼

該模式代碼抽離出來大緻如下:

import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np

win = pg.GraphicsLayoutWidget(show=True)
win.setWindowTitle('pyqtgraph example: Scrolling Plots')


p2 = win.addPlot()
data1 = np.random.normal(size=300)

curve2 = p2.plot(data1)
ptr1 = 0


def update1():
    global data1, ptr1
    data1[:-1] = data1[1:]  # shift data in the array one sample left

    data1[-1] = np.random.normal()

    ptr1 += 1
    curve2.setData(data1)
    curve2.setPos(ptr1, 0)


timer = pg.QtCore.QTimer()
timer.timeout.connect(update1)
timer.start(50)


if __name__ == '__main__':
    import sys

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()
           

對比模式1代碼,此部分多了個 curve2.setPos(ptr1, 0),通過 Qt 官網中搜尋查閱,setPos(x,y) 是将原點設定到 (x,y):

Sets the position of the item to pos, which is in parent coordinates. For items with no parent, pos is in scene coordinates.

The position of the item describes its origin (local coordinate (0, 0)) in parent coordinates.

這樣我們可以大緻了解為,通過設定坐标系相對原點位置來産生 x 軸移動的效果。

2.2.3 寫成 PlotWidget 形式

總結下模式 2 的原理: y 資料與模式1相同,設定左移變換的函數,計時器信号綁定該左移資料的函數,把 y 資料能實時設定到圖中;x 資料則通過 setPos() 函數随着 y 的變化同步進行設定,産生 x 軸同步移動的效果。

我們繼續采用 PlotWidget 的寫法來實作模式2的繪制,在模式1基礎上添加幾行代碼即可,為作區分我們把曲線定義為 curve2:

__author__ = 'Ted'

from PyQt5.Qt import *
from pyqtgraph import PlotWidget
from PyQt5 import QtCore
import numpy as np
import pyqtgraph as pq


class Window(QWidget):
    def __init__(self):
        super().__init__()
        # 設定下尺寸
        self.resize(600,600)
        # 添加 PlotWidget 控件
        self.plotWidget_ted = PlotWidget(self)
        # 設定該控件尺寸和相對位置
        self.plotWidget_ted.setGeometry(QtCore.QRect(25,25,550,550))

        # 仿寫 mode1 代碼中的資料
        # 生成 300 個正态分布的随機數
        self.data1 = np.random.normal(size=300)

        self.curve2 = self.plotWidget_ted.plot(self.data1, name="mode2")
        self.ptr1 = 0

        # 設定定時器
        self.timer = pq.QtCore.QTimer()
        # 定時器信号綁定 update_data 函數
        self.timer.timeout.connect(self.update_data)
        # 定時器間隔50ms,可以了解為 50ms 重新整理一次資料
        self.timer.start(50)

    # 資料左移
    def update_data(self):
        self.data1[:-1] = self.data1[1:]
        self.data1[-1] = np.random.normal()
        # 資料填充到繪制曲線中
        self.curve2.setData(self.data1)
        # x 軸記錄點
        self.ptr1 += 1
        # 重新設定 x 相關的坐标原點
        self.curve2.setPos(self.ptr1,0)


if __name__ == '__main__':
    import sys
    # PyQt5 程式固定寫法
    app = QApplication(sys.argv)

    # 将綁定了繪圖控件的視窗執行個體化并展示
    window = Window()
    window.show()

    # PyQt5 程式固定寫法
    sys.exit(app.exec())
           

我們在自己寫的代碼中重新設定了下視窗尺寸位置,資料還是按照執行個體中的寫法來完成的。

2.2.4 自寫模式1效果

PyQt5 pyqtgraph 實時繪制資料1. pyqtgraph 簡介2. 實時繪制學習3. 小結

3. 小結

今天先隻簡單整理這兩個較簡單的實時繪制模式,給定的代碼中資料是用的随機正态分布資料,我們結合着模式 1 和 2 的執行個體代碼來分析其原理算法來仿寫了常用版本的代碼。

掌握模式 1 和模式 2 的用法後,我們便可以對更多的資料來進行動态展示,比如 CPU 占用率、股票實時價格等,配合着 PyQt5 的 GUI 圖形界面,那麼完全可以用 Python 來寫出看着高大上的資料可視化界面了,這個後續我們再繼續研究。