Qt for Python做一個虛擬示波器軟件
Qt for Python做一個虛擬示波器軟件
摘要
示波器是一種用來將電信號轉化為可視化圖形的智能儀器,在物理學中尤為常見。20世紀70年代以來,微電子技術和微信計算機技術的快速發展,使電子儀器的整體水平發生了很大變化。自微處理器問世后,先后出現了獨立式智能儀器及自動測試系統、個人儀器系統。隨著個人計算機算力的不斷增強,虛擬化成為電子儀器的一個發展方向。本文用社區版qt creator5.0.2軟件設計制作了示波器虛擬軟件,由于時間倉促,僅實現了部分基本功能,僅供參考。
關鍵詞:示波器;虛擬儀器;虛擬軟件;
1. Qt
Qt是常用的一種ui設計軟件,包含了大量的控件類,并以直觀的方式展示出來。同時,其附帶的Qt Designer 可以從左邊的控件類庫直接拖動控件到設計面板,所見即所得,大大節省了計算坐標優化窗口界面布局的工作,而且里面的控件風格都比較現代,更有美感。
1.1 Qt Creator 5.0.2 Community
Qt是一款商業軟件,所以用于商業盈利涉及版權問題。但是對于非盈利個人也有社區開源版——Qt Creator 5.0.2 Community,本人用的是5.0.2版本。不附安裝教程,CSDN內相關資料很多。
Qt Creator 5.0.2 Community歡迎界面:

1.2 創建Qt工程
點擊"Project"邊上的"+New"創建新的工程。

原本是打算用C++的,但是考慮到要用到畫圖功能,所以改注意用Python。在新建工程中選擇"Applicaton(Qt for Python)“,然后再選擇"Qt for Python - Window(UI file)”,這是生成一個Windows桌面軟件的工程,默認自帶了一個空的ui文件。

這里輸入工程名稱、選擇工程創建路徑。

來到這一步,可以不改。但是我想創建一個主窗口,所以"Base class"我改成了"QMainWindow"。

后面都可以一路"Next"。
1.3 UI設計
可以使用項目自動生成的.ui文件進行設計,需要額外的ui時可以新建文件,選擇ui類型的文件。如下圖:

一頓操作猛如虎,就得到了如下的ui界面:

1.4 ui文件轉py文件
因為前面生成的是.ui文件,在python文件中是不能直接調用的。所以我們需要將.ui文件轉為.py文件,方便import。
這時需要啟動Terminal,然后cd命令轉到設計好的.ui文件所在的文件目錄下,輸入如下指令:
pyuic5 -o filename.py filename.ui
前面filename是.ui文件名,后一個filename是目標文件名。這樣我們就得到了ui界面的py文件。下面就是開始編寫代碼了。
2. 程序
2.1 import
from numpy import *
from matplotlib import pyplot
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib
matplotlib.use("Qt5Agg") # 聲明使用QT5
from PyQt5.QtWidgets import QApplication,QMainWindow,QGridLayout,QGraphicsScene,QSizePolicy,qApp
import win32api
import win32con
from PyQt5 import QtCore,QtGui
import sys
import MainWindow #引入ui文件生成的py文件
2.2 窗口類
class MainWin(QMainWindow,MainWindow.Ui_MainWindow):#繼承MainWindow的Ui_MainWindow類
def __init__(self): #構造函數
super(MainWin,self).__init__()
self.setupUi(self)
self.setWindowTitle("簡易示波器")
開始想到將控件在main()函數里實現功能,但后來發現,面向對象編程才是最方便的,也是邏輯最清晰的,所以將控件的功能函數作為成員函數來實現。
class MainWin(QMainWindow,MainWindow.Ui_MainWindow):
def __init__(self):
super(MainWin,self).__init__()
self.setupUi(self)
self.setWindowTitle("簡易示波器")
self.F=MyPicture(X1,dX,C,A)#創建主窗口類下的Picture對象,窗口被創建時創建一個Picture對象
self.setWindowIcon(QtGui.QIcon("./OSC.ico"))
self.gridlayout = QGridLayout(self.groupBox_2) # 繼承容器groupBox_2
self.gridlayout.addWidget(self.F,0,1)#在groupBox_2中添加Picture對象
self.dial_4.setValue(100)#時間軸旋鈕初值
self.dial_5.setValue(20)#x軸平移旋鈕初值
self.dial_6.setValue(20)
self.pushButton_3.clicked.connect(self.ResetKey)#Auto鍵
self.horizontalSlider_3.sliderMoved.connect(self.t1_slide)#t1滑條信號
self.horizontalSlider_4.sliderMoved.connect(self.t2_slide)#t2滑條信號
self.dial_6.valueChanged.connect(self.TimerMove)#時間軸旋鈕信號
self.lineEdit.setPlaceholderText("在這里輸入自定義函數")
self.dial_5.valueChanged.connect(self.AmpliMove)#幅值旋鈕信號
self.dial_7.valueChanged.connect(self.XaxisMove)#X平移旋鈕信號
self.dial_4.valueChanged.connect(self.YaxisMove)#Y平移旋鈕信號
self.actionExit.triggered.connect(qApp.quit) #菜單欄退出信號
self.actionSin_x.triggered.connect(self.actSin_x) #選擇正弦波
self.actionxsin_x.triggered.connect(self.actxsin_x) #選擇xsin(x)波
self.actionSquare_Wave.triggered.connect(self.actSquareWave) #選擇方波
self.actionTriangle_Wave.triggered.connect(self.actTriangleWave) #選擇三角波
self.actionSawtooth_Wave.triggered.connect(self.actsawtoothWave) #選擇鋸齒波
self.pushButton.clicked.connect(self.actMyselfWaveInput) #確認輸入的公式
self.radioButton_3.clicked.connect(self.CH1Pross)#信道1信號
self.radioButton_4.clicked.connect(self.CH2Pross)#信道2信號
成員函數主要有以下內容,由于作業為提交,這里就暫不放完整源代碼了。

2.3 信號類VirtualSignal
class VirtualSignal():
def __init__(self):#略
def changesig(self): #更改信號,略
def changesigA(self): #更改幅值,略
def changesigC(self): #更改偏移,略
此類在MainWin類中實現,用于描述信道信號,便于操作信號。
2.4 畫布類
#定義畫布類,繼承FigureCanvasQTAgg
class MyFigure(FigureCanvas):
def __init__(self,parent=None):
fig=Figure(figsize=(77,50),dpi=100) #定義畫布
super(MyFigure,self).__init__(fig) #繼承父類
self.axes=fig.add_subplot(111) #定義圖像
self.compute_initial_figure()
self.setParent(parent)
FigureCanvas.setSizePolicy(self,QSizePolicy.Expanding,QSizePolicy.Expanding)#圖像大小
FigureCanvas.updateGeometry(self) #更新畫布
def compute_initial_figure(self):
pass
2.5 圖像類
#圖像子類,繼承畫布類
class MyPicture(MyFigure):
def __init__(self,x1,dx,c,a): #構造函數
super(MyPicture,self).__init__()
self.x1=x1 #初始化坐標
self.dx=dx #初始化坐標軸大小
self.x2=self.x1+self.dx
CH1=VirtualSignal(a,c) #信號類對象,信道1
CH2=VirtualSignal(a,c) #信號類對象,信道2
self.CH=[CH1,CH2]
self.xindao=0 #信道序號
self.t1=T1 #t1滑條初始位置
self.t2=T2 #t2滑條初始位置
self.update_figure() #更新畫布
def changex(self): #橫向滑動坐標軸,略
def changeTimer(self): #放縮坐標軸,略
def changeC(self): #更改偏移量,略
def changeA(self): #更改幅值,略
def t1SlideChange(self): #操作時間滑條t1,略
def t2SlideChange(self): #操作時間滑條t2,略
def changesignal(self): #更換信道信號,略
def update_figure(self): #更新畫布,略
這里就體現了類的優點,每個對象的函數都定義在了類里,這樣就不會使main函數過于臃腫,而且當需要修改類的成員時,也不需要整個文件修改,只需要在類里修改,條理清晰,更為整潔。
2.6 全局變量
X1=0.0 #坐標軸起始位置
dX=20.0 #坐標軸寬度
C=0.0 #圖像上下平移的刻度
A=1.0 #幅值
T1=0 #t1滑條的值
T2=100 #t2滑條的值
SineWave="sin(x)" #正弦波表達式
xcosWave="cos(x)*x" #復合函數1
xsinWave="sin(x)*x" #復合函數2
SquareWave="where(x%4.0<2,-1.0,1.0)" #方波,where函數是numpy庫里的函數
TriangleWave="where(x%8<=4,x%8,8-x%8)" #三角波
SawToothWave="where(x%6<6,x%4,0)" #鋸齒波
2.7 主函數
mian函數主要作用是將窗口類實現,其余相關操作都以成員函數的方式定義在了相應的類里,這樣,main函數就簡單了不少。
if __name__ == '__main__':
app = QApplication(sys.argv)
ui=MainWin() #窗口類的實現對象
ui.show() #顯示窗口
sys.exit(app.exec_()) #退出
最終,整個程序的結構如下:

3. 結果

一開始的想法是制作動態的圖像,但是由于讓圖像保持動態無法避免地導致計算機重復刷新而使其他操作的相應卡頓,所以最終選擇了靜態。也可以嘗試動態,只需要修改圖像類和畫布類即可。參照這篇文章可以實現動態效果。
由于這是作業尚未提交,暫不附源代碼,有問題歡迎溝通。
浙公網安備 33010602011771號