写点什么

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

  • 2022 年 1 月 28 日
  • 本文字数:6838 字

    阅读完需:约 22 分钟

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

Python Qt GUI 设计系列博文终于到了实战篇,本篇博文将贯穿之前的基础知识点实现一款串口调试助手。

编辑切换为居中

添加图片注释,不超过 140 字(可选)

关注【公众号】 美男子玩编程,回复关键字:串口调试助手,获取项目源码~

1、UI 设计

UI 设计使用 Qt Creator 实现,组件布局如下所示:

编辑切换为居中

添加图片注释,不超过 140 字(可选)

2、将 UI 文件转换为 Py 文件

这里使用 Python 脚本的方式将 UI 文件转换为 Python 文件,代码如下所示:

import osimport 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("串口状态(关闭)")
复制代码


发布于: 刚刚阅读数: 2
用户头像

【研究方向】物联网、嵌入式、AI、Python 2018.02.09 加入

【公众号】美男子玩编程,关注获取海量资源~

评论

发布
暂无评论
Python Qt GUI设计:做一款串口调试助手(实战篇—1)