Python3.x+pyqtgraph實現資料視覺化教程
1、pyqtgraph庫資料視覺化效果還不錯,特別是窗體程式中影象互動性較好;安裝也很方便,用 pip 安裝。
2、在Python中新建一個 .py 檔案,然後寫入如下程式碼並執行可以得到官方提供的很多案例(含程式碼),出現如下介面影象:
import pyqtgraph.examples
pyqtgraph.examples.run()
圖1
圖2
圖3
4、程式預設是黑色背景,這個是可以修改的。比如,在程式的開頭部分寫入如下程式碼就可以修改背景:
pg.setConfigOption('background','w')
pg.setConfigOption('foreground','k')
更多說明,見 pyqtgraph 官網:http://www.pyqtgraph.org/documentation/style.html,“Line,Fill,and Color”部分的“Default Background and Foreground Colors”部分。
5、一個修改背景顏色的完整案例如下,可以直接執行程式:
import numpy as np import pyqtgraph as pg from pyqtgraph.Qt import QtGui,QtCore # 如下2行程式碼是我自己加入的,目的是修改預設的黑色背景為其它顏色背景 pg.setConfigOption('background','w') pg.setConfigOption('foreground','k') from pyqtgraph.Point import Point #generate layout app = QtGui.QApplication([]) win = pg.GraphicsWindow() win.setWindowTitle('pyqtgraph example: crosshair') label = pg.LabelItem(justify='right') win.addItem(label) p1 = win.addPlot(row=1,col=0) p2 = win.addPlot(row=2,col=0) region = pg.LinearRegionItem() region.setZValue(10) # Add the LinearRegionItem to the ViewBox,but tell the ViewBox to exclude this # item when doing auto-range calculations. p2.addItem(region,ignoreBounds=True) #pg.dbg() p1.setAutoVisible(y=True) #create numpy arrays #make the numbers large to show that the xrange shows data from 10000 to all the way 0 data1 = 10000 + 15000 * pg.gaussianFilter(np.random.random(size=10000),10) + 3000 * np.random.random(size=10000) data2 = 15000 + 15000 * pg.gaussianFilter(np.random.random(size=10000),10) + 3000 * np.random.random(size=10000) p1.plot(data1,pen="r") p1.plot(data2,pen="g") p2.plot(data1,pen="w") def update(): region.setZValue(10) minX,maxX = region.getRegion() p1.setXRange(minX,maxX,padding=0) region.sigRegionChanged.connect(update) def updateRegion(window,viewRange): rgn = viewRange[0] region.setRegion(rgn) p1.sigRangeChanged.connect(updateRegion) region.setRegion([1000,2000]) #cross hair vLine = pg.InfiniteLine(angle=90,movable=False) hLine = pg.InfiniteLine(angle=0,movable=False) p1.addItem(vLine,ignoreBounds=True) p1.addItem(hLine,ignoreBounds=True) vb = p1.vb def mouseMoved(evt): pos = evt[0] ## using signal proxy turns original arguments into a tuple if p1.sceneBoundingRect().contains(pos): mousePoint = vb.mapSceneToView(pos) index = int(mousePoint.x()) if index > 0 and index < len(data1): label.setText("<span style='font-size: 12pt'>x=%0.1f,<span style='color: red'>y1=%0.1f</span>,<span style='color: green'>y2=%0.1f</span>" % (mousePoint.x(),data1[index],data2[index])) vLine.setPos(mousePoint.x()) hLine.setPos(mousePoint.y()) proxy = pg.SignalProxy(p1.scene().sigMouseMoved,rateLimit=60,slot=mouseMoved) #p1.scene().sigMouseMoved.connect(mouseMoved) ## 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_()
知識補充:python圖形化例項分享--pyqt5與pyqtgraph嵌入繪圖
序言
之前也寫過一些圖形化介面的程式,基本上都是用wxPython寫的,確實簡單粗暴易上手。這次的任務是要寫一個繪圖的程式,wx模組就顯得不太友好了,我就去網上找了一些資料,發現PyQtGraph畫這種K線圖、波形圖等圖形真是太簡單了,更多的關於wx、qt等模組的細節學習可以看我後面的參考資料,我這裡就分享一下我本程式的心得,和對有些方法使用上自己的理解
專案開始
引用標頭檔案
pyqt5_draw_1 這是主程式檔案,負責主視窗圖形化介面
import sys # 與PyQt5配合使用 from PyQt5.QtWidgets import (QApplication,QMainWindow,QWidget,QGridLayout,QHBoxLayout,QVBoxLayout,QLabel,QComboBox,QPushButton,QDateEdit,QSpacerItem,QFrame,QSizePolicy,QSplitter,QRadioButton,QGroupBox,QCheckBox,QLineEdit,QAction) # 上面是QT圖形化要引用的所有包 from PyQt5.QtCore import Qt,QDate,QRect # 對齊、時間等 from PyQt5.QtCore import QThread,pyqtSignal # 多執行緒管理 import pyqtgraph as pg # 繪圖包 from Tmp_Data import * # 自定義檔案,下面有介紹 from Mythreading import * # 自定義檔案,下面有介紹 from pyqt5_graph import * # 自定義檔案,下面有介紹
如果PyQt5、pyqtgraph未安裝的,最簡單的安裝方式就用python自帶的pip工具安裝,如果沒有pip的或不會安裝可直接百度
c:\> pip install PyQt5 pyqtgraph
圖形化主介面搭建
# pyqt5_draw_1.py 檔名 import sys import cgitb from PyQt5.QtWidgets import (QApplication,QAction) from PyQt5.QtCore import Qt,QRect from TmpData import * from Mythreading import * from pyqt5_graph import * class Qt_Test_Frame(QMainWindow): Items = [] def __init__(self): #super(Qt_Test_Frame,self).__init__(*args,**kw) super().__init__() # 初始化介面 self._initUI() self.show() def _initUI(self): self.setWindowTitle("QT圖形介面測試") self.resize(800,600) wwg = QWidget() # 全域性佈局 wlayout = QVBoxLayout() h1_wlayout = QHBoxLayout() h2_wlayout = QHBoxLayout() h3_wlayout = QHBoxLayout() v4_wlayout = QVBoxLayout() v5_wlayout = QVBoxLayout() self.statusBar().showMessage("狀態列") # 第一層 self._frist_story(h1_wlayout) # 第二層 self._second_story(h2_wlayout) # 第三層 左 self._third_left(v4_wlayout,v5_wlayout) # 第三層 右 self._fouth_right(v5_wlayout) # 載入 splt = self._my_line() splt2 = self._my_line(False) wlayout.addSpacing(10) # 增加布局間距 wlayout.addLayout(h1_wlayout) wlayout.addSpacing(10) # 增加布局間距 wlayout.addLayout(h2_wlayout) wlayout.addSpacing(10) # 增加布局間距 wlayout.addWidget(splt) wlayout.addLayout(h3_wlayout) wlayout.addWidget(self.statusBar()) h3_wlayout.addLayout(v4_wlayout,0) h3_wlayout.addWidget(splt2) h3_wlayout.addLayout(v5_wlayout,2) #wlayout.setAlignment(Qt.AlignTop) wwg.setLayout(wlayout) self.setCentralWidget(wwg) def _frist_story(self,h1_wlayout): # 第一層佈局 self.h1_combox1 = QComboBox(minimumWidth=100) self.h1_combox1.addItems(wind_field) self.h1_combox2 = QComboBox(minimumWidth=100) self.h1_combox2.addItems(wind_mach_chooice(self.h1_combox1.currentText())) self.h1_combox3 = QComboBox(minimumWidth=100) self.h1_combox3.addItems(wind_blade) self.h1_combox4 = QComboBox(minimumWidth=100) self.h1_combox4.addItems(signal_type) # 行為測試 暫時無法使用 h1_cb1_action = QAction("風場選擇",self) h1_cb1_action.setStatusTip("請選擇風場") self.h1_combox1.addAction(h1_cb1_action) h1_wlayout.addItem(QSpacerItem(20,20)) h1_wlayout.addWidget(QLabel("風場"),0) h1_wlayout.addWidget(self.h1_combox1,0) h1_wlayout.addItem(QSpacerItem(40,20)) h1_wlayout.addWidget(QLabel("風機"),0) h1_wlayout.addWidget(self.h1_combox2,20)) h1_wlayout.addWidget(QLabel("葉片ID"),0) h1_wlayout.addWidget(self.h1_combox3,20)) h1_wlayout.addWidget(QLabel("訊號型別"),0) h1_wlayout.addWidget(self.h1_combox4,0) h1_wlayout.setAlignment(Qt.AlignLeft) # 事件繫結 self.h1_combox1.currentIndexChanged.connect(self._wind_chooice) def _second_story(self,h2_wlayout): # 第二層佈局 self.h2_date1 = QDateEdit(QDate.currentDate()) self.h2_date1.setCalendarPopup(True) self.h2_date2 = QDateEdit(QDate.currentDate()) self.h2_date2.setCalendarPopup(True) self.h2_button = QPushButton("執行") self.h2_button2 = QPushButton("停止") h2_wlayout.addItem(QSpacerItem(20,20)) h2_wlayout.addWidget(QLabel("起始"),0) h2_wlayout.addWidget(self.h2_date1) h2_wlayout.addItem(QSpacerItem(50,20)) h2_wlayout.addWidget(QLabel("結束"),0) h2_wlayout.addWidget(self.h2_date2) h2_wlayout.addItem(QSpacerItem(70,20)) h2_wlayout.addWidget(self.h2_button) h2_wlayout.addWidget(self.h2_button2) h2_wlayout.setAlignment(Qt.AlignLeft) # 事件繫結 self.h2_button.clicked.connect(lambda: self._start_func()) self.h2_button2.clicked.connect(lambda: self._stop_func()) def _third_left(self,v4_wlayout,v5_wlayout): # 第三層佈局 # 分量佈局 v4_group_imf = QGridLayout() vbox1 = QGroupBox("分量值") self.radio_1 = QRadioButton("分量1") self.radio_2 = QRadioButton("分量2") self.radio_3 = QRadioButton("分量3") self.radio_4 = QRadioButton("分量4") self.radio_5 = QRadioButton("分量5") self.radio_6 = QRadioButton("分量6") self.radio_7 = QRadioButton("分量7") self.radio_8 = QRadioButton("分量8") self.radio_9 = QRadioButton("分量9") self.radio_1.setChecked(True) self.radio_val = self.radio_1.text() # 優先順序佈局 v4_group_prior = QGridLayout() vbox2 = QGroupBox("優先順序") cb1 = QCheckBox("葉片1") cb2 = QCheckBox("葉片2") cb3 = QCheckBox("葉片3") self.v4_lineEdit = QLineEdit() # 時間佈局 v4_group_time = QGridLayout() vbox3 = QGroupBox("時間選擇") self.v4_combox1 = QComboBox(minimumWidth=100) self.v4_combox1.addItem("空") # 按鍵 v4_button = QPushButton("顯示圖形") # 寫入網格格佈局 v4_group_imf.addWidget(self.radio_1,0) v4_group_imf.addWidget(self.radio_2,1) v4_group_imf.addWidget(self.radio_3,1,0) v4_group_imf.addWidget(self.radio_4,1) v4_group_imf.addWidget(self.radio_5,2,0) v4_group_imf.addWidget(self.radio_6,1) v4_group_imf.addWidget(self.radio_7,3,0) v4_group_imf.addWidget(self.radio_8,1) v4_group_imf.addWidget(self.radio_9,4,0) v4_group_prior.addWidget(cb1,0) v4_group_prior.addWidget(cb2,0) v4_group_prior.addWidget(cb3,0) v4_group_prior.addWidget(QLabel("選擇是:"),0) v4_group_prior.addWidget(self.v4_lineEdit,5,0) v4_group_time.addWidget(self.v4_combox1) # 寫入左側佈局 vbox1.setLayout(v4_group_imf) vbox2.setLayout(v4_group_prior) vbox3.setLayout(v4_group_time) v4_wlayout.addItem(QSpacerItem(50,20)) v4_wlayout.addWidget(vbox1) v4_wlayout.addItem(QSpacerItem(50,20)) v4_wlayout.addWidget(vbox2) v4_wlayout.addItem(QSpacerItem(50,20)) v4_wlayout.addWidget(vbox3) v4_wlayout.addItem(QSpacerItem(50,20)) v4_wlayout.addWidget(v4_button) v4_wlayout.addItem(QSpacerItem(50,20)) # 事件繫結 self.radio_1.toggled.connect(lambda: self._changestyle(self.radio_1)) self.radio_2.toggled.connect(lambda: self._changestyle(self.radio_2)) self.radio_3.toggled.connect(lambda: self._changestyle(self.radio_3)) self.radio_4.toggled.connect(lambda: self._changestyle(self.radio_4)) self.radio_5.toggled.connect(lambda: self._changestyle(self.radio_5)) self.radio_6.toggled.connect(lambda: self._changestyle(self.radio_6)) self.radio_7.toggled.connect(lambda: self._changestyle(self.radio_7)) self.radio_8.toggled.connect(lambda: self._changestyle(self.radio_8)) self.radio_9.toggled.connect(lambda: self._changestyle(self.radio_9)) cb1.stateChanged.connect(lambda: self._prior_func(cb1)) cb2.stateChanged.connect(lambda: self._prior_func(cb2)) cb3.stateChanged.connect(lambda: self._prior_func(cb3)) v4_button.clicked.connect(lambda: self._show_func(v5_wlayout)) def _fouth_right(self,v5_wlayout): # 載入波形圖 self.tmp_plt = plt_init() v5_wlayout.addWidget(self.tmp_plt) def _my_line(self,var=True): # var 為True時,為橫線,否則為豎線 line = QFrame(self) line_var = QFrame.HLine sp_var = Qt.Horizontal if not var: line_var = QFrame.VLine sp_var = Qt.Vertical line.setFrameShape(line_var) line.setFrameShadow(QFrame.Sunken) splitter = QSplitter(sp_var) splitter.addWidget(line) return splitter def _wind_chooice(self): tmp_list = wind_mach_chooice(self.h1_combox1.currentText()) self.h1_combox2.clear() self.h1_combox2.addItems(tmp_list) def _start_func(self): a = self.h1_combox1.currentText() b = self.h1_combox2.currentText() c = self.h1_combox3.currentText() d = self.h1_combox4.currentText() e = self.h2_date1.dateTime().toString("yy-MM-dd") f = self.h2_date2.dateTime().toString("yy-MM-dd") # 多執行緒的引用 self.start_func = RunThread(target=self._start_thread,args=(a,b,c,d,e,f)) # 多執行緒啟動 self.start_func.start() def _stop_func(self): # 執行緒停止 self.start_func.stop() print("執行結束") def _start_thread(self,a,f): print("*****執行列印*****") print(wind_mach_chooice(a)) print(a,d) print(e) print(f) print("%s" % (time.strftime('<%H:%M:%S>',time.localtime()))) self.v4_combox1.clear() self.v4_combox1.addItems(tmp_time_list) print("*****執行列印*****") def _changestyle(self,btn): # 單選項的判斷函式 if btn.isChecked(): self.radio_val = btn.text() #print("%s"%(time.strftime('<%H:%M:%S>',time.localtime()))) def _prior_func(self,cb): # 複選框內容新增 if cb.isChecked(): if cb.text()[-1] not in self.Items: self.Items.append(cb.text()[-1]) shop_cart= ",".join(self.Items) self.v4_lineEdit.setText(shop_cart) else: if cb.text()[-1] in self.Items: self.Items.remove(cb.text()[-1]) shop_cart = ",".join(self.Items) self.v4_lineEdit.setText(shop_cart) def _show_func(self,v5_wlayout): print("*****顯示列印*****") print(self.radio_val) num = self.v4_lineEdit.text() print(self.v4_combox1.currentText()) v5_wlayout.removeWidget(self.tmp_plt) self.tmp_plt = plt_show(num) v5_wlayout.addWidget(self.tmp_plt) print("*****顯示列印*****") if __name__ == '__main__': cgitb.enable(format="text") app = QApplication(sys.argv) win = Qt_Test_Frame() sys.exit(app.exec_())
處理把列表檔案轉成字典與繪圖
# pyqt5_graph.py 檔名 import pyqtgraph as pg from TmpData import _read_data,wind_mach_chooice colour = ["r","g","b"] yp_list = ["葉片1","葉片2","葉片3"] def _data_to_dict(): mydict = {} for my_vars,i in zip(_read_data(),range(len(_read_data()))): tmp_dict = {} for var,j in zip(my_vars,range(len(my_vars))): tmp_dict[var[0]] =var[1] mydict[i] = tmp_dict return mydict def plt_init(): # 繪圖初始化 pg.setConfigOption("background","w") plt = pg.PlotWidget() plt.addLegend(size=(150,80)) plt.showGrid(x=True,y=True,alpha=0.5) return plt def plt_show(num): # 傳繪製的新圖 mydict = _data_to_dict() pg.setConfigOption("background",alpha=0.5) for i in num.split(","): i = int(i)-1 plt.plot(x=list(mydict[i].keys()),y=list(mydict[i].values()),pen=colour[i],name=yp_list[i]) return plt if __name__ == '__main__': _data_to_dict() pass
模擬給其它檔案傳指定資料
# TmpData.py 檔名 import os import numpy as np file_path = os.path.join(os.getcwd(),"風機採集訊號資料\\") wind_field = ["風場1","風場2","風場3"] wind_machine = {"風場1":["大別山","天目山"],"風場2":["崑崙山","三清山"],"風場3":["五指山","火焰山"]} wind_blade = ["X-20Hz","X-1K","Y-20Hz","Y-1K"] signal_type = ["包絡","振動"] tmp_time_list = ["20190501","20190502","20190504","20190508","20190515"] def wind_mach_chooice(val): return wind_machine[val] def _read_data(): file_list = os.listdir(file_path) file_list = [var for var in file_list if var.split(".")[1] == "csv"] a = [] for var in file_list: tmp = os.path.join(file_path,var) rd_file = np.loadtxt(tmp,delimiter=",",usecols=(0,1)) a.append(rd_file) return a[0],a[1],a[2]
多執行緒管理
因為程式執行時間久,主介面就會出現假死的狀態,要引用多執行緒
# Mythreading.py 檔名 from PyQt5.QtCore import QThread,pyqtSignal class RunThread(QThread): counter_value = pyqtSignal(int) def __init__(self,target,args,name=""): QThread.__init__(self) self.target = target self.args = args self.is_running = True def run(self): #print("starting",self.name,"at:",ctime()) self.res = self.target(*self.args) def stop(self): # 負責停止執行緒 self.terminate()
關於QT異常直接退出沒有報錯的情況,查bug比較麻煩
import cgitb # 這句放在所有程式開始前,這樣就可以正常列印異常了 cgitb.enable(format="text")
這些天本人身體不舒服,但還是把做完的這個分享出來,有些細節沒有具體說明,下次身體好點,再單獨拿出來分享,累了,晚安!
以上這篇Python3.x+pyqtgraph實現資料視覺化教程就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。