33.python串口助手工具案例詳解


邏輯文件

import sys
import serial
import serial.tools.list_ports
from Ui_day13_test01 import Ui_mainWindow
from PyQt5.QtWidgets import QMainWindow,QApplication,QMessageBox
from PyQt5.QtCore import QTimer

# AttributeError: 'Pyqt5_Serial' object has no attribute 'setCentralWidget'的解決方法
class PyqtSerial(QMainWindow,Ui_mainWindow):
    def __init__(self):
        super().__init__()  # 用super繼承父類中的__init__()方法
        # 因為前面Ui程序中mainWindow = QtWidgets.QMainWindow(),而在邏輯文件中self是
        # 指myshow對象,相當於就是用用myshow來設置界面
        self.setupUi(self)
        self.start()
        self.setWindowTitle('我的串口小助手')  # 更改軟件標題名稱
        self.ser=serial.Serial()    # 調用串口模塊

        # 接收數據和發送數據數目置零
        self.data_num_received = 0
        self.recv_byte.setText(str(self.data_num_received))
        self.data_num_sended = 0
        self.send_byte.setText(str(self.data_num_sended))

        # 函數執行條件變量
        self.com_check=False    # 判斷串口是否檢查

    # 設置界面啟動函數
    def start(self):
        self.state_label.setText('請點擊【檢測串口】按鈕!')
        # 串口檢測按鈕
        self.ser_test.clicked.connect(self.portCheck)

        # 串口信息顯示
        self.com_port.currentTextChanged.connect(self.portImf)

        # 打開串口按鈕
        self.open_ser.clicked.connect(self.portOpen)

        # 關閉串口按鈕
        self.close_ser.clicked.connect(self.portClose)

        # 發送數據按鈕
        self.send_botton.clicked.connect(self.dataSend)

        # 發送定時器發送數據
        self.timer_send=QTimer()    # 括號內可以加self,也可以不加
        self.timer_send.timeout.connect(self.dataSend)  # 每次計時結束,觸發括號內的函數
        self.time_send.stateChanged.connect(self.dataTimerSend)   # QCheckBox選擇框改變后連接括號內的函數

        # 接收定時器接收數據
        # 程序先運行start函數,然后運行到這個定時器時,就不操作了,只要界面上有操作,
        # 並且該操作對應的函數里有self.timer.start()開始計時的代碼,則當計時結束后,
        # 就會觸發此定時器后面的函數
        self.timer=QTimer() # 創建一個定時器
        self.timer.timeout.connect(self.dataReceive)    # 每次計時結束,觸發括號內的函數

        # 清除發送窗口
        self.send_clear.clicked.connect(self.sendDataClear)

        # 清除接收窗口
        self.recv_clear.clicked.connect(self.receiveDataClear)

    # 清除發送窗口
    def sendDataClear(self):
        self.send_data.setText('')

    # 清除接收窗口
    def receiveDataClear(self):
        self.recv_data.setText('')

    # 定時發送數據
    def dataTimerSend(self):
        if self.time_send.isChecked():  # 檢查time_send的QCheckBox是否勾選上
            self.timer_send.start(int(self.time_num.text()))    # 計時器按time_num值開始計時
            self.time_num.setEnabled(False) # time_num的QLineEdit不選中
        else:
            self.timer_send.stop()  # 發送計時器停止
            self.time_num.setEnabled(True)  # # time_num的QLineEdit選中

    # 發送數據
    def dataSend(self):
        if self.ser.isOpen():   # 判斷串口是否打開
            input_s=self.send_data.toPlainText()    # 將發送文本框中的內容賦值給變量
            if input_s !='':    # 判斷發送文本框中有內容
                # 非空字符串
                if self.send_hex.isChecked():   # 判斷hex的QCheckBox勾選上了
                    # hex發送
                    input_s=input_s.strip()     # 去掉發送文本框內容前后的空格、回車等
                    send_list=[]    # 創建一個發送列表
                    while input_s !='': # 發送內容變量不為空
                        try:
            # >>> int('12',base=16) # 如果是帶參數base的話,12要以字符串的形式進行輸入,12 為 16進制
            # 18
                            num=int(input_s[0:2],16)    # 將發送內容的前兩位轉換成16進制
                        except ValueError:
                            QMessageBox.critical(self,'wrong data','請輸入十六進制數據,以空格分開!')
                            return
                        input_s=input_s[2:].strip() # 切割input_s中前兩位,並去掉前后空格,最后賦值到input_s
                        send_list.append(num)   # 將已經轉換的前兩位添加到發送列表中
                    input_s=bytes(send_list)    # 將發送列表轉換成bytes
                else:
                    # ascii發送
                    input_s=(input_s+'\r\n').encode('gbk')  # 將發送內容按gbk格式編碼

                num=self.ser.write(input_s) # 發送數據
                self.data_num_sended+=num   # 已發送內容計數
                self.send_byte.setText(str(self.data_num_sended))   # 改變已發送計數框中的值
        else:
            pass

    # 關閉串口
    def portClose(self):
        self.timer.stop()   # 接收定時器關閉
        self.timer_send.stop()  # 發送定時器關閉
        try:
            self.ser.close()    # 關閉串口
        except:
            pass
        self.open_ser.setEnabled(True)  # 設置按鈕QPushButton狀態為True
        self.close_ser.setEnabled(False)    # 設置按鈕QPushButton狀態為False
        self.time_num.setEnabled(True)  # QLineEdit恢復默認值

        # 接收數據和發送數據數目置零
        self.data_num_received=0
        self.recv_byte.setText(str(self.data_num_received))
        self.data_num_sended=0
        self.send_byte.setText(str(self.data_num_sended))
        self.groupBox_4.setTitle('串口狀態(已關閉)')   # 改變串口狀態框的名稱

    # 接收數據
    def dataReceive(self):
        # self.timer.start(2000)  # 此處設置的時間為2000毫秒,表示每間隔2000毫秒運行下面的print代碼
        # print('數據接收中')
        try:
            num=self.ser.inWaiting()    # 返回接收緩沖區中的字符數
        except:
            self.portClose()    # 關閉串口
            return None
        if num>0:   # 接收緩沖區中的字符數大於零時
            data=self.ser.read(num) # 讀取全部數據
            num=len(data)   # 提取數據的字符長度
            # hex顯示
            if self.recv_hex.checkState():  # 判斷hex接收的QCheckBox是否選中
                out_s=''
                for i in range(0,len(data)):    # 循環遍歷接收到的數據
                    # X:輸出整數的大寫十六進制方式;
                    out_s=out_s+'{:X}'.format(data[i])+' '  # 將字符轉換成整數的大寫十六進制方式
                self.recv_data.insertPlainText(out_s)   # 將轉換好的數據添加到接收數據文本框中
            else:
                # 串口接收到的字符串為b'123',要轉化成unicode字符串才能輸出到窗口中去
                self.recv_data.insertPlainText(data.decode('gbk'))

            # 統計接收字符的數量
            self.data_num_received+=num # 將接收到的數據的長度數賦值給計數變量
            self.recv_byte.setText(str(self.data_num_received)) # 將接收到的數據長度數顯示在計數文本框中

            # 獲取到text光標
            textCursor=self.recv_data.textCursor()

            # 滾動到底部
            textCursor.movePosition(textCursor.End)

            # 設置光標到text中去
            self.recv_data.setTextCursor(textCursor)
        else:
            pass

    # 打開串口
    def portOpen(self):
        # try:
        #     # 設置串口參數(此為網友代碼),運行出錯,
        #     self.ser.port=self.com_port.currentText()
        #     self.ser.baudrate=int(self.baudrate.currentText())
        #     self.ser.bytesize=int(self.data_bit.currentText())
        #     self.ser.stopbits=int(self.stop_bit.currentText())
        #     self.ser.parity=self.check_bit.currentText()  # 出錯原因在這里,這個是不用的
        # except Exception as e:
        #     print('請先打開其它設置!',e)

        # 設置串口參數
        self.ser.port = self.com_port.currentText()
        self.ser.baudrate = int(self.baudrate.currentText())
        self.ser.bytesize = int(self.data_bit.currentText())
        self.ser.stopbits = int(self.stop_bit.currentText())

        try:
            self.ser.open()
        except:
            QMessageBox.critical(self,'Port Error','此串口不能被打開!')
            return None     # return代表結束該函數執行,此處可以寫成return不加None

        # 打開串口接收定時器,周期為2ms
        self.timer.start(2) # 此處的意思是當2ms的時間到了之后,觸發括號內的dataReceive函數

        if self.ser.isOpen():
            self.open_ser.setEnabled(False) # 設置按鈕QPushButton狀態為False
            self.close_ser.setEnabled(True) # 設置按鈕QPushButton狀態為True
            self.groupBox_4.setTitle('串口狀態(已開啟)')

    # 串口信息
    def portImf(self):
        if self.com_check:
            # 顯示選定的串口的詳細信息
            imf_s=self.com_port.currentText()   # 將串口選擇的當前內容賦值給變量
            if imf_s !='':
                self.state_label.setText('串口{}選擇完成!'.format(self.com_port.currentText()))
        else:
            self.state_label.setText('請先點擊檢測串口按鈕!')

    # 串口檢查
    def portCheck(self):
        # 檢測所有存在的串口,將信息存儲在字典中
        self.com_dict={}    # 創建一個空字典
        port_list=list(serial.tools.list_ports.comports())  # 查找串口數據,並保存在列表中
        self.com_port.clear()   # 清空默認串口菜單數據
        # 遍列串口數列表,並將com名稱重新添加到串口菜單中
        for port in port_list:
            self.com_dict['%s'%port[0]]='%s'%port[1]
            self.com_port.addItem(port[0])
        # 判斷串口是否找到
        if len(self.com_dict)==0:
            self.state_label.setText('無串口!')
        else:
            self.state_label.setText('共有{}個串口,請選擇串口!'.format(len(self.com_dict)))
        self.com_check=True

if __name__ == '__main__':
    app=QApplication(sys.argv)  # 由於前面導入的是PyQt5.QtWidgets,而QApplication是該模塊里的一個類
    myshow=PyqtSerial()   # 根據類來創建一個對象
    myshow.show()   # 對象顯示
    sys.exit(app.exec_())

界面文件

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_mainWindow(object):
    def setupUi(self, mainWindow):
        mainWindow.setObjectName("mainWindow")
        mainWindow.resize(721, 490)
        mainWindow.setMinimumSize(QtCore.QSize(721, 490))
        mainWindow.setMaximumSize(QtCore.QSize(721, 490))
        self.centralWidget = QtWidgets.QWidget(mainWindow)
        self.centralWidget.setObjectName("centralWidget")
        self.widget = QtWidgets.QWidget(self.centralWidget)
        self.widget.setGeometry(QtCore.QRect(20, 20, 681, 451))
        self.widget.setObjectName("widget")
        self.groupBox = QtWidgets.QGroupBox(self.widget)
        self.groupBox.setGeometry(QtCore.QRect(0, 0, 221, 311))
        self.groupBox.setObjectName("groupBox")
        self.layoutWidget = QtWidgets.QWidget(self.groupBox)
        self.layoutWidget.setGeometry(QtCore.QRect(10, 20, 201, 281))
        self.layoutWidget.setObjectName("layoutWidget")
        self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setObjectName("gridLayout")
        self.label_3 = QtWidgets.QLabel(self.layoutWidget)
        self.label_3.setObjectName("label_3")
        self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1)
        self.label_4 = QtWidgets.QLabel(self.layoutWidget)
        self.label_4.setObjectName("label_4")
        self.gridLayout.addWidget(self.label_4, 2, 0, 1, 3)
        self.label_5 = QtWidgets.QLabel(self.layoutWidget)
        self.label_5.setObjectName("label_5")
        self.gridLayout.addWidget(self.label_5, 3, 0, 1, 1)
        self.label_6 = QtWidgets.QLabel(self.layoutWidget)
        self.label_6.setObjectName("label_6")
        self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1)
        self.label_7 = QtWidgets.QLabel(self.layoutWidget)
        self.label_7.setObjectName("label_7")
        self.gridLayout.addWidget(self.label_7, 5, 0, 1, 1)
        self.label_8 = QtWidgets.QLabel(self.layoutWidget)
        self.label_8.setObjectName("label_8")
        self.gridLayout.addWidget(self.label_8, 6, 0, 1, 1)
        self.stop_bit = QtWidgets.QComboBox(self.layoutWidget)
        self.stop_bit.setObjectName("stop_bit")
        self.stop_bit.addItem("")
        self.stop_bit.addItem("")
        self.gridLayout.addWidget(self.stop_bit, 6, 1, 1, 2)
        self.open_ser = QtWidgets.QPushButton(self.layoutWidget)
        self.open_ser.setObjectName("open_ser")
        self.gridLayout.addWidget(self.open_ser, 7, 0, 1, 3)
        self.close_ser = QtWidgets.QPushButton(self.layoutWidget)
        self.close_ser.setObjectName("close_ser")
        self.gridLayout.addWidget(self.close_ser, 8, 0, 1, 3)
        self.com_port = QtWidgets.QComboBox(self.layoutWidget)
        self.com_port.setFocusPolicy(QtCore.Qt.WheelFocus)
        self.com_port.setObjectName("com_port")
        self.com_port.addItem("")
        self.com_port.addItem("")
        self.com_port.addItem("")
        self.com_port.addItem("")
        self.gridLayout.addWidget(self.com_port, 1, 1, 1, 2)
        self.label_2 = QtWidgets.QLabel(self.layoutWidget)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1)
        self.baudrate = QtWidgets.QComboBox(self.layoutWidget)
        self.baudrate.setObjectName("baudrate")
        self.baudrate.addItem("")
        self.baudrate.addItem("")
        self.gridLayout.addWidget(self.baudrate, 3, 1, 1, 2)
        self.data_bit = QtWidgets.QComboBox(self.layoutWidget)
        self.data_bit.setObjectName("data_bit")
        self.data_bit.addItem("")
        self.data_bit.addItem("")
        self.data_bit.addItem("")
        self.data_bit.addItem("")
        self.gridLayout.addWidget(self.data_bit, 4, 1, 1, 2)
        self.check_bit = QtWidgets.QComboBox(self.layoutWidget)
        self.check_bit.setObjectName("check_bit")
        self.check_bit.addItem("")
        self.check_bit.addItem("")
        self.gridLayout.addWidget(self.check_bit, 5, 1, 1, 2)
        self.ser_test = QtWidgets.QPushButton(self.layoutWidget)
        self.ser_test.setObjectName("ser_test")
        self.gridLayout.addWidget(self.ser_test, 0, 1, 1, 2)
        self.groupBox_2 = QtWidgets.QGroupBox(self.widget)
        self.groupBox_2.setGeometry(QtCore.QRect(250, 0, 321, 181))
        self.groupBox_2.setObjectName("groupBox_2")
        self.recv_data = QtWidgets.QTextEdit(self.groupBox_2)
        self.recv_data.setGeometry(QtCore.QRect(10, 20, 301, 151))
        self.recv_data.setObjectName("recv_data")
        self.groupBox_3 = QtWidgets.QGroupBox(self.widget)
        self.groupBox_3.setGeometry(QtCore.QRect(250, 200, 321, 181))
        self.groupBox_3.setObjectName("groupBox_3")
        self.send_data = QtWidgets.QTextEdit(self.groupBox_3)
        self.send_data.setGeometry(QtCore.QRect(10, 20, 301, 151))
        self.send_data.setObjectName("send_data")
        self.groupBox_4 = QtWidgets.QGroupBox(self.widget)
        self.groupBox_4.setGeometry(QtCore.QRect(0, 330, 221, 111))
        self.groupBox_4.setObjectName("groupBox_4")
        self.layoutWidget1 = QtWidgets.QWidget(self.groupBox_4)
        self.layoutWidget1.setGeometry(QtCore.QRect(30, 20, 161, 51))
        self.layoutWidget1.setObjectName("layoutWidget1")
        self.gridLayout_2 = QtWidgets.QGridLayout(self.layoutWidget1)
        self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.label_9 = QtWidgets.QLabel(self.layoutWidget1)
        self.label_9.setObjectName("label_9")
        self.gridLayout_2.addWidget(self.label_9, 0, 0, 1, 1)
        self.label_11 = QtWidgets.QLabel(self.layoutWidget1)
        self.label_11.setObjectName("label_11")
        self.gridLayout_2.addWidget(self.label_11, 1, 0, 1, 1)
        self.send_byte = QtWidgets.QLineEdit(self.layoutWidget1)
        self.send_byte.setObjectName("send_byte")
        self.gridLayout_2.addWidget(self.send_byte, 1, 2, 1, 1)
        self.recv_byte = QtWidgets.QLineEdit(self.layoutWidget1)
        self.recv_byte.setObjectName("recv_byte")
        self.gridLayout_2.addWidget(self.recv_byte, 0, 2, 1, 1)
        spacerItem = QtWidgets.QSpacerItem(80, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.gridLayout_2.addItem(spacerItem, 0, 1, 1, 1)
        self.state_label = QtWidgets.QLabel(self.groupBox_4)
        self.state_label.setGeometry(QtCore.QRect(20, 80, 181, 21))
        self.state_label.setText("")
        self.state_label.setObjectName("state_label")
        self.recv_hex = QtWidgets.QCheckBox(self.widget)
        self.recv_hex.setGeometry(QtCore.QRect(590, 20, 91, 19))
        self.recv_hex.setObjectName("recv_hex")
        self.send_hex = QtWidgets.QCheckBox(self.widget)
        self.send_hex.setGeometry(QtCore.QRect(590, 200, 91, 19))
        self.send_hex.setObjectName("send_hex")
        self.recv_clear = QtWidgets.QPushButton(self.widget)
        self.recv_clear.setGeometry(QtCore.QRect(590, 50, 70, 30))
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.recv_clear.sizePolicy().hasHeightForWidth())
        self.recv_clear.setSizePolicy(sizePolicy)
        self.recv_clear.setStyleSheet("background-color: rgb(174, 255, 235);")
        self.recv_clear.setObjectName("recv_clear")
        self.send_botton = QtWidgets.QPushButton(self.widget)
        self.send_botton.setGeometry(QtCore.QRect(590, 230, 70, 30))
        self.send_botton.setStyleSheet("background-color: rgb(174, 255, 235);")
        self.send_botton.setObjectName("send_botton")
        self.send_clear = QtWidgets.QPushButton(self.widget)
        self.send_clear.setGeometry(QtCore.QRect(590, 280, 70, 30))
        self.send_clear.setStyleSheet("background-color: rgb(174, 255, 235);")
        self.send_clear.setObjectName("send_clear")
        self.layoutWidget2 = QtWidgets.QWidget(self.widget)
        self.layoutWidget2.setGeometry(QtCore.QRect(260, 400, 211, 23))
        self.layoutWidget2.setObjectName("layoutWidget2")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.layoutWidget2)
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.time_send = QtWidgets.QCheckBox(self.layoutWidget2)
        self.time_send.setObjectName("time_send")
        self.horizontalLayout.addWidget(self.time_send)
        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.time_num = QtWidgets.QLineEdit(self.layoutWidget2)
        self.time_num.setMinimumSize(QtCore.QSize(60, 0))
        self.time_num.setMaximumSize(QtCore.QSize(60, 16777215))
        self.time_num.setObjectName("time_num")
        self.horizontalLayout.addWidget(self.time_num)
        self.label = QtWidgets.QLabel(self.layoutWidget2)
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        mainWindow.setCentralWidget(self.centralWidget)

        self.retranslateUi(mainWindow)
        QtCore.QMetaObject.connectSlotsByName(mainWindow)

    def retranslateUi(self, mainWindow):
        _translate = QtCore.QCoreApplication.translate
        mainWindow.setWindowTitle(_translate("mainWindow", "串口小助手"))
        self.groupBox.setTitle(_translate("mainWindow", "串口設置"))
        self.label_3.setText(_translate("mainWindow", "串口選擇:"))
        self.label_4.setText(_translate("mainWindow", "Serial Port (COM1->COM2)"))
        self.label_5.setText(_translate("mainWindow", "波特率:"))
        self.label_6.setText(_translate("mainWindow", "數據位:"))
        self.label_7.setText(_translate("mainWindow", "校驗位:"))
        self.label_8.setText(_translate("mainWindow", "停止位:"))
        self.stop_bit.setItemText(0, _translate("mainWindow", "1"))
        self.stop_bit.setItemText(1, _translate("mainWindow", "2"))
        self.open_ser.setText(_translate("mainWindow", "打開串口"))
        self.close_ser.setText(_translate("mainWindow", "關閉串口"))
        self.com_port.setItemText(0, _translate("mainWindow", "COM1"))
        self.com_port.setItemText(1, _translate("mainWindow", "COM2"))
        self.com_port.setItemText(2, _translate("mainWindow", "COM3"))
        self.com_port.setItemText(3, _translate("mainWindow", "COM4"))
        self.label_2.setText(_translate("mainWindow", "串口檢測:"))
        self.baudrate.setItemText(0, _translate("mainWindow", "9600"))
        self.baudrate.setItemText(1, _translate("mainWindow", "115200"))
        self.data_bit.setItemText(0, _translate("mainWindow", "8"))
        self.data_bit.setItemText(1, _translate("mainWindow", "7"))
        self.data_bit.setItemText(2, _translate("mainWindow", "6"))
        self.data_bit.setItemText(3, _translate("mainWindow", "5"))
        self.check_bit.setItemText(0, _translate("mainWindow", "NONE"))
        self.check_bit.setItemText(1, _translate("mainWindow", "ODD"))
        self.ser_test.setText(_translate("mainWindow", "檢測串口"))
        self.groupBox_2.setTitle(_translate("mainWindow", "接收區"))
        self.groupBox_3.setTitle(_translate("mainWindow", "發送區"))
        self.groupBox_4.setTitle(_translate("mainWindow", "串口狀態"))
        self.label_9.setText(_translate("mainWindow", "已接收:"))
        self.label_11.setText(_translate("mainWindow", "已發送:"))
        self.send_byte.setText(_translate("mainWindow", "0"))
        self.recv_byte.setText(_translate("mainWindow", "0"))
        self.recv_hex.setText(_translate("mainWindow", "Hex接收"))
        self.send_hex.setText(_translate("mainWindow", "Hex發送"))
        self.recv_clear.setText(_translate("mainWindow", "清除"))
        self.send_botton.setText(_translate("mainWindow", "發送"))
        self.send_clear.setText(_translate("mainWindow", "清除"))
        self.time_send.setText(_translate("mainWindow", "定時發送"))
        self.time_num.setText(_translate("mainWindow", "1000"))
        self.label.setText(_translate("mainWindow", "ms/次"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = QtWidgets.QMainWindow()
    ui = Ui_mainWindow()
    ui.setupUi(mainWindow)
    mainWindow.show()
    sys.exit(app.exec_())

以前就是參照網友設計的一個串口助手小工具。

如有問題,可關注微信公眾號進行咨詢!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM