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_())

以前就是参照网友设计的一个串口助手小工具。

如有问题,可关注微信公众号进行咨询!

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

上一篇:DataGrip for Mac激活码


下一篇:QCustomPlot柱状图