Python Qt GUI设计:做一款串口调试助手(实战篇—1)

目录

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.path
 
dir ='./' #文件所在的路径
 
#找出路径下所有的.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 = 0
        self.Lineedit2.setText(str(self.data_num_sended))
        self.data_num_received = 0
        self.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.FIVEBITS
        elif flag_data == 6:
            self.ser.bytesize = serial.SIXBITS
        elif flag_data == 7:
            self.ser.bytesize = serial.SEVENBITS
        else:
            self.ser.bytesize = serial.EIGHTBITS

        flag_data = self.Combobox4.currentText()  # 校验位
        if flag_data == "None":
            self.ser.parity = serial.PARITY_NONE
        elif flag_data == "Odd":
            self.ser.parity = serial.PARITY_ODD
        else:
            self.ser.parity = serial.PARITY_EVEN

        flag_data = int(self.Combobox5.currentText()) # 停止位
        if flag_data == 1:
            self.ser.stopbits = serial.STOPBITS_ONE
        else:
            self.ser.stopbits = serial.STOPBITS_TWO

        flag_data = self.Combobox6.currentText()  # 流控
        if flag_data == "No Ctrl Flow":
            self.ser.xonxoff = False  #软件流控
            self.ser.dsrdtr  = False  #硬件流控 DTR
            self.ser.rtscts  = False  #硬件流控 RTS
        elif flag_data == "SW Ctrl Flow":
            self.ser.xonxoff = True  #软件流控
        else:         
            if self.Checkbox3.isChecked():
                self.ser.dsrdtr = True  #硬件流控 DTR
            if self.Checkbox4.isChecked():
                self.ser.rtscts = True  #硬件流控 RTS
        try:
            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)
        # 打开串口接收定时器,周期为1ms
        self.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 None
                        
                        input_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 += num
                self.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 None
        
        if 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 += num
            self.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 = 0
        self.Lineedit2.setText(str(self.data_num_sended))

    # 清除接收数据显示
    def receive_data_clear(self):
        self.Text1.setText("")

        self.data_num_received = 0
        self.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 = 0
        self.Lineedit2.setText(str(self.data_num_sended))
        self.data_num_received = 0
        self.Lineedit3.setText(str(self.data_num_received))
        
        self.formGroupBox1.setTitle("串口状态(关闭)")

资源下载:PythonQt串口调试助手-嵌入式文档类资源-CSDN下载 

  • 28
    点赞
  • 258
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论
### 回答1: Python Qt 串口调试助手是一个基于PythonQt框架开发的串口调试工具,可以通过串口与外部设备进行通信和调试。它提供了一个清晰简单的界面,用户可以通过该界面进行串口的打开、关闭、设置波特率等操作,并能实时监控并显示串口接收到的数据。 这个工具的源码主要包含以下几个部分: 1. 界面设计:使用Qt Designer工具进行界面的设计,将设计好的界面保存为 .ui文件。然后使用 PyQt或者PySide库中的工具将 .ui文件转换成 Python可执行的代码。 2. 串口操作:利用 Python 的串口库,如 pySerial,实现串口的打开、关闭、设置波特率等操作。该部分代码负责与外部设备进行串口通信,并将接收到的数据传递给界面显示。 3. 事件处理:利用 Qt 框架提供的信号与槽机制,将界面上的各种操作(如按钮点击、串口参数设置等)与对应的函数进行绑定。当用户进行操作时,相应的事件函数会被触发,从而执行对应的操作。 4. 数据显示:通过 Qt 框架提供的文本框、列表框等控件,将串口接收到的数据实时显示在界面上。可以根据需求,对接收到的数据进行解析和处理,如格式化显示、存储到文件等。 开发这个工具需要有一定的Python编程和Qt框架的基础。可以通过学习相关的教程和文档来了解更多关于Python Qt 串口调试助手的开发和使用。通过阅读源码,可以深入理解工具的实现原理,并根据需要进行二次开发和定制。 ### 回答2: Python Qt串口调试助手是一个用于调试串口通信的工具。它使用Python开发,并使用Qt图形界面库构建用户界面。以下是该工具的源码解析: 1. 导入所需的库: ``` import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QPlainTextEdit from PyQt5.QtSerialPort import QSerialPort, QSerialPortInfo from PyQt5.QtCore import QIODevice ``` 2. 创建一个主窗口类,并继承QMainWindow: ``` class SerialAssistant(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): # 设置窗口标题和大小 self.setWindowTitle("Python Qt串口调试助手") self.resize(800, 600) ``` 3. 在initUI方法中创建用户界面元素: ``` # 创建一个文本框用于显示串口信息和接收的数据 self.dataTextEdit = QPlainTextEdit(self) self.dataTextEdit.setReadOnly(True) # 创建一个串口实例 self.serial = QSerialPort(self) # 创建一个下拉菜单用于选择串口 self.serialPortComboBox = QComboBox(self) self.serialPortComboBox.currentIndexChanged.connect(self.openSerialPort) # 创建一个按钮用于打开和关闭串口 self.openCloseButton = QPushButton("打开串口", self) self.openCloseButton.clicked.connect(self.handleOpenCloseButton) # 布局管理 layout = QVBoxLayout() layout.addWidget(self.dataTextEdit) layout.addWidget(self.serialPortComboBox) layout.addWidget(self.openCloseButton) centralWidget = QWidget() centralWidget.setLayout(layout) self.setCentralWidget(centralWidget) ``` 4. 实现打开和关闭串口的方法: ``` def openSerialPort(self, index): # 关闭已经打开的串口 if self.serial.isOpen(): self.serial.close() # 获取选择的串口信息 portName = self.serialPortComboBox.itemData(index) # 打开选择的串口 self.serial.setPortName(portName) self.serial.setBaudRate(QSerialPort.Baud9600) self.serial.setDataBits(QSerialPort.Data8) self.serial.setParity(QSerialPort.NoParity) self.serial.setStopBits(QSerialPort.OneStop) self.serial.setFlowControl(QSerialPort.NoFlowControl) if self.serial.open(QIODevice.ReadWrite): self.openCloseButton.setText("关闭串口") else: self.openCloseButton.setText("打开串口") ``` 5. 实现处理打开和关闭串口按钮点击事件的方法: ``` def handleOpenCloseButton(self): if self.serial.isOpen(): self.serial.close() self.openCloseButton.setText("打开串口") else: self.openSerialPort(self.serialPortComboBox.currentIndex()) ``` 6. 实现串口数据接收的方法: ``` def handleReadyRead(self): while self.serial.bytesAvailable(): data = self.serial.readAll() self.dataTextEdit.appendPlainText(data) ``` 7. 编写主函数来创建应用程序实例,并运行: ``` if __name__ == "__main__": app = QApplication(sys.argv) serialAssistant = SerialAssistant() serialAssistant.show() sys.exit(app.exec_()) ``` 这就是Python Qt串口调试助手的源码,它可以帮助用户通过串口进行通信,并在图形界面上显示接收到的数据。用户可以选择串口,并通过打开和关闭按钮控制串口的状态。同时,它还提供了一个文本框用于显示串口信息和接收的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不脱发的程序猿

亲,赏包辣条吧~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值