美文网首页python
Python Qt GUI设计:做一款串口调试助手(实战篇—1)

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

作者: 不脱发的程序猿 | 来源:发表于2022-01-28 22:08 被阅读0次

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

    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("串口状态(关闭)")

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/ghztkrtx.html