PyQt5記錄筆記之使用Matplotlib繪圖

2020-09-26 09:00:11

@PyQt5記錄筆記之使用Matplotlib繪圖

PyQt5上建立Matplotlib繪圖的基本流程

1,要在PyQt5介面上顯示,首先要建立介面元件FigureCanvas,一個畫布(相當於用於顯示Matplotlib的Widget元件);
2,接著需要建立繪圖相關元件圖表類Figure,表示畫布上的整個圖,管理圖形視窗上的子圖以及各種圖表元件的繪製;
3,然後建立Axes子圖類繪製子圖,一個Figure上可以有多個子圖,一個子圖上所有元素都是由Axes管理,包括座標軸類(Axis),曲線類(Line2D),文字類(Text),圖例類等(Legend),當建立一個Axes物件時,會同時自動建立x軸y軸,對子圖上各個子物件進行操作。
例如:
axes = self.MainUI.widget.figure.add_subplot(1,1,1)#在已經建立的UI上建立一個子圖
axes.set_xlim(0,9)#x軸範圍
axes.set_xlabel(「distance[m]」)#橫座標標籤
axes.grid(b = True,which = ‘major’,axis = ‘both’)網格
t = []
s = []
for i in range(10):
t.append(i)
s.append(random.randint(0,1500))
axes.plot(t, s,picker=True)#畫曲線圖
axes.cla()#清除子圖內容
self.MainUI.widget.redraw()#對子圖執行完各種操作之後,及時重新整理
#self.figure.canvas.draw()
具體介面以及說明見:matplotlib.axes介面使用說明

互動功能

NavigationToolbar工具列

建立NavigationToolbar工具列時,傳遞一個FigureCanvas物件作為引數,關聯FigureCanvas類物件。
naviBar = NavigationToolbar(figCanvas,self)#建立工具列
NavigationToolbar父類別是QToolbar,可以使用QToolbar的一些介面函數改造工具列。

FigureCanvas提供的事件

事件名稱觸發動作
button_press_event滑鼠按鍵按下
button_release_event滑鼠按鍵釋放
motion_notify_event滑鼠移動
scroll_event滑鼠滾輪動作
key_press_event鍵盤按鍵按下
key_release_event鍵盤按鍵釋放
pick_event物件被拾取
figure_enter_event滑鼠進入一個Figure物件
figure_leave_event滑鼠離開一個Figure物件
axes_enter_event滑鼠進入一個子圖
axes_leave_event滑鼠離開一個子圖

滑鼠和鍵盤操作產生的事件,有如下屬性:
x,y:畫布上x,y的位置,單位畫素;
inaxes:產生滑鼠事件的Axes子圖物件;
xdata,ydata:滑鼠遊標處x,y的座標值

FigureCanvas類中,有兩個函數用來建立和解除事件與槽函數之間的關聯:
1,將事件與槽函數關聯mpl_connect(eventName,function),eventName型別為字串,為上述事件名稱,function為連線到的槽函數,返回一個編號,用來標誌此事件:
self.__cid = figCanvas.mpl_connect(「scroll_event」,self.do_scrollZoom)#支援滑鼠滾輪縮放
2,解除事件關聯mpl_disconnect(self.__cid)

自定義一個QmyFigureCanvas類

下面程式碼自定義了一個QmyFigureCanvas類,繼承於QWidget類,作為一個繪圖元件,即在Qt Designer設計介面時,將一個QWidget元件提升為QmyFigureCanvas即可。

from PyQt5.QtWidgets import QWidget
from PyQt5 import QtCore
from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg as FigureCanvas,NavigationToolbar2QT as NavigationToolbar)#使用者介面後端渲染,用來以繪圖的形式輸出
from PyQt5.QtWidgets import QVBoxLayout
from matplotlib.figure import Figure#圖表類
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy

class QmyFigureCanvas(QWidget):
    mouseMove = QtCore.pyqtSignal(numpy.float64,mpl.lines.Line2D)#自定義觸發訊號,用於與UI互動
    # mousePress = QtCore.pyqtSignal(numpy.float64,numpy.float64)
    # mouseRelease = QtCore.pyqtSignal(numpy.float64,numpy.float64)
    def __init__(self,parent=None,toolbarVisible=True,showHint=False):
        super().__init__(parent)
        # self.figure = mpl.figure.Figure()#公共屬性figure
        self.figure = Figure()#公共屬性figur
        figCanvas = FigureCanvas(self.figure)#建立FigureCanvas物件
        self.naviBar = NavigationToolbar(figCanvas,self)#建立工具列
        
        actList = self.naviBar.actions()
        count = len(actList)
        self.__lastActtionHint = actList[count-1]
        self.__showHint = showHint#是否顯示座標提示
        self.__lastActtionHint.setVisible(self.__showHint)
        self.__showToolbar = toolbarVisible#是否顯示工具列
        self.naviBar.setVisible(self.__showToolbar)

        layout = QVBoxLayout(self)
        layout.addWidget(self.naviBar)#新增工具列
        layout.addWidget(figCanvas)#新增FigureCanvas物件
        layout.setContentsMargins(0,0,0,0)
        layout.setSpacing(0)

        self.__cid = figCanvas.mpl_connect("scroll_event",self.do_scrollZoom)#支援滑鼠滾輪縮放
        self.__cid1 = figCanvas.mpl_connect("pick_event",self.do_series_pick)#支援曲線抓取
        # self.__cid2 = figCanvas.mpl_connect("button_press_event",self.do_pressMouse)#支援滑鼠按下
        self.__cid3 = figCanvas.mpl_connect("button_release_event",self.do_releaseMouse)#支援滑鼠釋放
        self.__cid4 = figCanvas.mpl_connect("motion_notify_event",self.do_moveMouse)#支援滑鼠移動
        self.mouseIsPress = False
        self.pickStatus = False

	#公共函數介面
    def setToolbarVisible(self,isVisible=True):#是否顯示工具列
        self.__showToolbar = isVisible
        self.naviBar.setVisible(isVisible)

    def setDataHintVisible(self,isVisible=True):#是否顯示座標提示
        self.__showHint = isVisible
        self.__lastActtionHint.setVisible(isVisible)

    def redraw(self):#重繪曲線,快速呼叫
        self.figure.canvas.draw()

    def do_scrollZoom(self,event):#通過滑鼠滾輪縮放
        ax = event.inaxes    #產生事件axes物件
        if ax == None:
            return
        self.naviBar.push_current()
        xmin,xmax = ax.get_xbound()
        xlen = xmax - xmin
        ymin,ymax = ax.get_ybound()
        ylen = ymax - ymin

        xchg = event.step * xlen / 20
        xmin = xmin + xchg
        xmax = xmax - xchg
        ychg = event.step * ylen / 20
        ymin = ymin + ychg
        ymax = ymax - ychg
        ax.set_xbound(xmin,xmax)
        ax.set_ybound(ymin,ymax)
        event.canvas.draw()
        
	def do_series_pick(self,event):#picker事件獲取抓取曲線
	        self.series = event.artist
	        # index = event.ind[0]
	        # print("series",event.ind)
	        if isinstance(self.series,mpl.lines.Line2D):
	             self.pickStatus = True
        
    def do_releaseMouse(self,event):#滑鼠釋放,釋放抓取曲線
        if event.inaxes == None:
            return
        if self.pickStatus == True:
            self.series.set_color(color = "black")
            self.figure.canvas.draw()
            self.pickStatus = False
        # self.mouseRelease.emit(event.xdata,event.ydata)

    def do_moveMouse(self,event):#滑鼠移動,重繪抓取曲線
        if event.inaxes == None:
            return
        if self.pickStatus == True:
            self.series.set_xdata([event.xdata,event.xdata])
            self.series.set_color(color = "red")
            self.figure.canvas.draw()
            self.mouseMove.emit(event.xdata,self.series)#自定義觸發訊號,用於與UI互動

程式碼部分建立了一個包含工具列的Figure()圖表元件,並且自定義了滑鼠滾輪縮放事件,以及當滑鼠抓取直線後,進行直線在座標範圍內平移操作。

應用效果事例

將自定義的QmyFigureCanvas應用於UI中,在UI中隨機繪製曲線,並新增axvline直線作為標誌線,可拖動標誌線進行其他操作顯示,如圖:
在這裡插入圖片描述

參考圖書:Python Qt GUI與資料視覺化程式設計