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. 结果
一开始的想法是制作动态的图像,但是由于让图像保持动态无法避免地导致计算机重复刷新而使其他操作的相应卡顿,所以最终选择了静态。也可以尝试动态,只需要修改图像类和画布类即可。参照这篇文章可以实现动态效果。
由于这是作业尚未提交,暂不附源代码,有问题欢迎沟通。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通