目录
1、UI设计
2、将UI文件转换为Py文件
3、逻辑功能实现
3.1、初始化程序
3.2、串口检测程序
3.3、 设置及打开串口程序
3.4、定时发送数据程序
3.5、发送数据程序
3.6、接收数据程序
3.7、保存日志程序
3.8、加载日志程序
3.9、打开博客、公众号程序
3.10、清除发送和接收数据显示程序
3.11、关闭串口程序
Python Qt GUI设计系列博文终于到了实战篇,本篇博文将贯穿之前的基础知识点实现一款串口调试助手。
关注【公众号】 美男子玩编程,回复关键字:串口调试助手,获取项目源码~
资源下载:PythonQt串口调试助手-嵌入式文档类资源-CSDN下载
1、UI设计
UI设计使用Qt Creator实现,组件布局如下所示:
2、将UI文件转换为Py文件
这里使用Python脚本的方式将UI文件转换为Python文件,代码如下所示:
import os
import os.pathdir ='./' #文件所在的路径#找出路径下所有的.ui文件
def listUiFile():list = []files = os.listdir(dir)for filename in files:#print(filename)if os.path.splitext(filename)[1] == '.ui':list.append(filename)return list#把扩展名未.ui的转换成.py的文件
def transPyFile(filename):return os.path.splitext(filename)[0] + '.py'#通过命令把.ui文件转换成.py文件
def runMain():list = listUiFile()for uifile in list:pyfile = transPyFile(uifile)cmd = 'pyuic5 -o {pyfile} {uifile}'.format(pyfile=pyfile, uifile=uifile)os.system(cmd)if __name__ =="__main__":runMain()
3、逻辑功能实现
3.1、初始化程序
首先初始化一些组件和标志位的状态,设置信号与槽的关系,实现代码如下所示:
# 初始化程序def __init__(self):super(Pyqt5_Serial, self).__init__()self.setupUi(self)self.init()self.ser = serial.Serial()self.port_check()# 设置Logo和标题self.setWindowIcon(QIcon('Com.png'))self.setWindowTitle("串口调试助手 【公众号】美男子玩编程")# 设置禁止拉伸窗口大小self.setFixedSize(self.width(), self.height())# 发送数据和接收数据数目置零self.data_num_sended = 0self.Lineedit2.setText(str(self.data_num_sended))self.data_num_received = 0self.Lineedit3.setText(str(self.data_num_received))# 串口关闭按钮使能关闭self.Pushbuttom3.setEnabled(False)# 发送框、文本框清除self.Text1.setText("")self.Text2.setText("")# 建立控件信号与槽关系def init(self):# 串口检测按钮self.Pushbuttom2.clicked.connect(self.port_check)# 串口打开按钮self.Pushbuttom1.clicked.connect(self.port_open)# 串口关闭按钮self.Pushbuttom3.clicked.connect(self.port_close)# 定时发送数据self.timer_send = QTimer()self.timer_send.timeout.connect(self.data_send)self.Checkbox7.stateChanged.connect(self.data_send_timer)# 发送数据按钮self.Pushbuttom6.clicked.connect(self.data_send)# 加载日志self.Pushbuttom4.clicked.connect(self.savefiles)# 加载日志self.Pushbuttom5.clicked.connect(self.openfiles)# 跳转链接self.commandLinkButton1.clicked.connect(self.link)# 清除发送按钮self.Pushbuttom7.clicked.connect(self.send_data_clear)# 清除接收按钮self.Pushbuttom8.clicked.connect(self.receive_data_clear)
3.2、串口检测程序
检测电脑上所有串口,实现代码如下所示:
# 串口检测def port_check(self):# 检测所有存在的串口,将信息存储在字典中self.Com_Dict = {}port_list = list(serial.tools.list_ports.comports())self.Combobox1.clear()for port in port_list:self.Com_Dict["%s" % port[0]] = "%s" % port[1]self.Combobox1.addItem(port[0])# 无串口判断if len(self.Com_Dict) == 0:self.Combobox1.addItem("无串口")
3.3、 设置及打开串口程序
检测到串口后进行配置,打开串口,并且启动定时器一直接收用户输入,实现代码如下所示:
# 打开串口def port_open(self):self.ser.port = self.Combobox1.currentText() # 串口号self.ser.baudrate = int(self.Combobox2.currentText()) # 波特率flag_data = int(self.Combobox3.currentText()) # 数据位if flag_data == 5:self.ser.bytesize = serial.FIVEBITSelif flag_data == 6:self.ser.bytesize = serial.SIXBITSelif flag_data == 7:self.ser.bytesize = serial.SEVENBITSelse:self.ser.bytesize = serial.EIGHTBITSflag_data = self.Combobox4.currentText() # 校验位if flag_data == "None":self.ser.parity = serial.PARITY_NONEelif flag_data == "Odd":self.ser.parity = serial.PARITY_ODDelse:self.ser.parity = serial.PARITY_EVENflag_data = int(self.Combobox5.currentText()) # 停止位if flag_data == 1:self.ser.stopbits = serial.STOPBITS_ONEelse:self.ser.stopbits = serial.STOPBITS_TWOflag_data = self.Combobox6.currentText() # 流控if flag_data == "No Ctrl Flow":self.ser.xonxoff = False #软件流控self.ser.dsrdtr = False #硬件流控 DTRself.ser.rtscts = False #硬件流控 RTSelif flag_data == "SW Ctrl Flow":self.ser.xonxoff = True #软件流控else: if self.Checkbox3.isChecked():self.ser.dsrdtr = True #硬件流控 DTRif self.Checkbox4.isChecked():self.ser.rtscts = True #硬件流控 RTStry:time.sleep(0.1)self.ser.open()except:QMessageBox.critical(self, "串口异常", "此串口不能被打开!")return None# 串口打开后,切换开关串口按钮使能状态,防止失误操作 if self.ser.isOpen():self.Pushbuttom1.setEnabled(False)self.Pushbuttom3.setEnabled(True)self.formGroupBox1.setTitle("串口状态(开启)")# 定时器接收数据self.timer = QTimer()self.timer.timeout.connect(self.data_receive)# 打开串口接收定时器,周期为1msself.timer.start(1)
3.4、定时发送数据程序
通过定时器,可支持1ms至30s之间数据定时,实现代码如下所示:
# 定时发送数据def data_send_timer(self):try:if 1<= int(self.Lineedit1.text()) <= 30000: # 定时时间1ms~30s内if self.Checkbox7.isChecked():self.timer_send.start(int(self.Lineedit1.text()))self.Lineedit1.setEnabled(False)else:self.timer_send.stop()self.Lineedit1.setEnabled(True)else:QMessageBox.critical(self, '定时发送数据异常', '定时发送数据周期仅可设置在30秒内!')except:QMessageBox.critical(self, '定时发送数据异常', '请设置正确的数值类型!')
3.5、发送数据程序
可以发送ASCII字符和十六进制类型数据,并且可以在数据前显示发送的时间,在数据后进行换行,发送一个字节,TX标志会自动累加,实现代码如下所示:
# 发送数据def data_send(self):if self.ser.isOpen():input_s = self.Text2.toPlainText()# 判断是否为非空字符串if input_s != "":# 时间显示if self.Checkbox5.isChecked():self.Text1.insertPlainText((time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ")# HEX发送if self.Checkbox1.isChecked(): input_s = input_s.strip()send_list = []while input_s != '':try:num = int(input_s[0:2], 16)except ValueError:QMessageBox.critical(self, '串口异常', '请输入规范十六进制数据,以空格分开!')return Noneinput_s = input_s[2:].strip()send_list.append(num)input_s = bytes(send_list)# ASCII发送else: input_s = (input_s).encode('utf-8')# HEX接收显示if self.Checkbox2.isChecked(): out_s = ''for i in range(0, len(input_s)):out_s = out_s + '{:02X}'.format(input_s[i]) + ' 'self.Text1.insertPlainText(out_s)# ASCII接收显示else: self.Text1.insertPlainText(input_s.decode('utf-8')) # 接收换行 if self.Checkbox6.isChecked():self.Text1.insertPlainText('\r\n')# 获取到Text光标textCursor = self.Text1.textCursor()# 滚动到底部textCursor.movePosition(textCursor.End)# 设置光标到Text中去self.Text1.setTextCursor(textCursor)# 统计发送字符数量num = self.ser.write(input_s)self.data_num_sended += numself.Lineedit2.setText(str(self.data_num_sended))else:pass
3.6、接收数据程序
可以接收ASCII字符和十六进制类型数据,并且可以在数据前显示发送的时间,在数据后进行换行,接收一个字节,RX标志会自动累加,实现代码如下所示:
# 接收数据def data_receive(self):try:num = self.ser.inWaiting()if num > 0:time.sleep(0.1)num = self.ser.inWaiting() #延时,再读一次数据,确保数据完整性except:QMessageBox.critical(self, '串口异常', '串口接收数据异常,请重新连接设备!')self.port_close()return Noneif num > 0:data = self.ser.read(num)num = len(data)# 时间显示if self.Checkbox5.isChecked():self.Text1.insertPlainText((time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ")# HEX显示数据if self.Checkbox2.checkState():out_s = ''for i in range(0, len(data)):out_s = out_s + '{:02X}'.format(data[i]) + ' 'self.Text1.insertPlainText(out_s)# ASCII显示数据else:self.Text1.insertPlainText(data.decode('utf-8'))# 接收换行 if self.Checkbox6.isChecked():self.Text1.insertPlainText('\r\n')# 获取到text光标textCursor = self.Text1.textCursor()# 滚动到底部textCursor.movePosition(textCursor.End)# 设置光标到text中去self.Text1.setTextCursor(textCursor)# 统计接收字符的数量self.data_num_received += numself.Lineedit3.setText(str(self.data_num_received))else:pass
3.7、保存日志程序
将接收框中收发的数据保存到TXT文本中,实现代码如下所示:
# 保存日志def savefiles(self):dlg = QFileDialog()filenames = dlg.getSaveFileName(None, "保存日志文件", None, "Txt files(*.txt)")try:with open(file = filenames[0], mode='w', encoding='utf-8') as file:file.write(self.Text1.toPlainText())except:QMessageBox.critical(self, '日志异常', '保存日志文件失败!')
3.8、加载日志程序
加载保存到TXT文本中的数据信息到发送框中,实现代码如下所示:
# 加载日志def openfiles(self):dlg = QFileDialog()filenames = dlg.getOpenFileName(None, "加载日志文件", None, "Txt files(*.txt)")try:with open(file = filenames[0], mode='r', encoding='utf-8') as file:self.Text2.setPlainText(file.read())except:QMessageBox.critical(self, '日志异常', '加载日志文件失败!')
3.9、打开博客、公众号程序
点击按钮,打开我的公众号二维码和博客主页,实现代码如下所示:
# 打开博客链接和公众号二维码def link(self):dialog = QDialog()label_img = QLabel()label_img.setAlignment(Qt.AlignCenter) label_img.setPixmap(QPixmap("./img.jpg"))vbox = QVBoxLayout()vbox.addWidget(label_img)dialog.setLayout(vbox)dialog.setWindowTitle("快扫码关注公众号吧~")dialog.setWindowModality(Qt.ApplicationModal)dialog.exec_()webbrowser.open('https://blog.csdn.net/m0_38106923')
3.10、清除发送和接收数据显示程序
清除发送数据框和接收数据框的内容和计数次数,实现代码如下所示:
# 清除发送数据显示def send_data_clear(self):self.Text2.setText("")self.data_num_sended = 0self.Lineedit2.setText(str(self.data_num_sended))# 清除接收数据显示def receive_data_clear(self):self.Text1.setText("")self.data_num_received = 0self.Lineedit3.setText(str(self.data_num_received))
3.11、关闭串口程序
关闭串口,停止定时器,重置组件和标志状态,实现代码如下所示:
# 关闭串口def port_close(self):try:self.timer.stop()self.timer_send.stop()self.ser.close()except:QMessageBox.critical(self, '串口异常', '关闭串口失败,请重启程序!')return None# 切换开关串口按钮使能状态和定时发送使能状态self.Pushbuttom1.setEnabled(True)self.Pushbuttom3.setEnabled(False)self.Lineedit1.setEnabled(True)# 发送数据和接收数据数目置零self.data_num_sended = 0self.Lineedit2.setText(str(self.data_num_sended))self.data_num_received = 0self.Lineedit3.setText(str(self.data_num_received))self.formGroupBox1.setTitle("串口状态(关闭)")
资源下载:PythonQt串口调试助手-嵌入式文档类资源-CSDN下载