分享自己使用python+pyserial+pyQT5写的串口调试助手

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fhqlongteng/article/details/78535393

      1、先介绍一下软件的运行环境,python3.6.1(32位) ,pyinstaller 3.3版本, pyserial 3.4版本, pyqt5 5.8.2版本,这些是主要用到的软件包。  在windosw的 cmd命令行下输入pip list可以查看

C:\Users\xxxxn>pip list
DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning.
altgraph (0.14)
colorama (0.3.7)
configparser (3.5.0)
freeze (1.0.10)
future (0.16.0)
futures (3.0.5)
gevent (1.2.1)
greenlet (0.4.12)
iso8601 (0.1.12)
macholib (1.8)
ntplib (0.3.3)
pefile (2017.9.3)
pigar (0.6.10)
pip (9.0.1)
py2exe (0.9.2.2)
PyInstaller (3.3)
pypiwin32 (220)
PyQt5 (5.8.2)
pyserial (3.4)
pywinusb (0.4.2)
PyYAML (3.12)
setuptools (28.8.0)
sip (4.19.2)
six (1.10.0)
wheel (0.29.0)

C:\Users\zhaoshimin>python
Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 17:54:52) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.

      2、pycom串口助手实现的功能,十六进制数据发送,接收,字符发送,接收,定时发送,增加一个刷新串口设备按钮,可以不用关闭软件,刷新一下新安装的串口。串口助手运行过程中USB转串口的设备断开软件不会死机,相比一些其他工具有提高。

3、程序主要由pycom.py和mainwindow.py组成,窗口代码使用QtCreat自动生成。pycom.py实现了串口的操作和菜单逻辑

pycom.py的源代码如下:

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QTimer
from mainwindow import Ui_MainWindow
import sys
#from datetime import datetime
import serial
import serial.tools.list_ports
from pyico import *

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
   
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)

扫描二维码关注公众号,回复: 4297587 查看本文章

       
        # 设置应用程序的窗口图标
        self.setWindowIcon(QIcon(":/com.png"))

       
           
        #串口无效
        self.ser = None
        self.send_num = 0
        self.receive_num = 0
       

        #显示发送与接收的字符数量
        dis = '发送:'+ '{:d}'.format(self.send_num) + '  接收:' + '{:d}'.format(self.receive_num)
        self.statusBar.showMessage(dis)

        #刷新一下串口的列表
        self.refresh()
       
        #波特率控件
        self.comboBox_2.addItem('115200')
        self.comboBox_2.addItem('57600')
        self.comboBox_2.addItem('56000')
        self.comboBox_2.addItem('38400')
        self.comboBox_2.addItem('19200')
        self.comboBox_2.addItem('14400')
        self.comboBox_2.addItem('9600')
        self.comboBox_2.addItem('4800')
        self.comboBox_2.addItem('2400')
        self.comboBox_2.addItem('1200')

        #数据位控件
        self.comboBox_3.addItem('8')
        self.comboBox_3.addItem('7')
        self.comboBox_3.addItem('6')
        self.comboBox_3.addItem('5')

        #停止位控件
        self.comboBox_4.addItem('1')
        self.comboBox_4.addItem('1.5')
        self.comboBox_4.addItem('2')

        #校验位控件
        self.comboBox_5.addItem('NONE')
        self.comboBox_5.addItem('ODD')
        self.comboBox_5.addItem('EVEN')

        #对testEdit进行事件过滤
        self.textEdit.installEventFilter(self)

        #实例化一个定时器
        self.timer = QTimer(self)

        self.timer_send= QTimer(self)
        #定时器调用读取串口接收数据
        self.timer.timeout.connect(self.recv)

        #定时发送
        self.timer_send.timeout.connect(self.send)
       
        #发送数据按钮
        self.pushButton.clicked.connect(self.send)
        #打开关闭串口按钮
        self.pushButton_2.clicked.connect(self.open_close)

        #刷新串口外设按钮
        self.pushButton_4.clicked.connect(self.refresh)

        #清除窗口
        self.pushButton_3.clicked.connect(self.clear)

        #定时发送
        self.checkBox_4.clicked.connect(self.send_timer_box)

       

    #刷新一下串口
    def refresh(self):
        #查询可用的串口
        plist=list(serial.tools.list_ports.comports())

        if len(plist) <= 0:
            print("No used com!");
            self.statusBar.showMessage('没有可用的串口')
           

        else:
            #把所有的可用的串口输出到comboBox中去
            self.comboBox.clear()
           
            for i in range(0, len(plist)):
                plist_0 = list(plist[i])
                self.comboBox.addItem(str(plist_0[0]))

           
    #事件过滤
    def eventFilter(self, obj, event):
        #处理textEdit的键盘按下事件
        if event.type() == event.KeyPress:
           
            if self.ser != None:
                #获取按键对应的字符
                char = event.text()
               
                num = self.ser.write(char.encode('utf-8'))
                self.send_num = self.send_num + num
                dis = '发送:'+ '{:d}'.format(self.send_num) + '  接收:' + '{:d}'.format(self.receive_num)
                self.statusBar.showMessage(dis)
            else:
                pass
            return True
        else:
           
            return False
       
       
    #重载窗口关闭事件
    def closeEvent(self,e):

        #关闭定时器,停止读取接收数据
        self.timer_send.stop()
        self.timer.stop()

        #关闭串口
        if self.ser != None:
            self.ser.close()

    #定时发送数据
    def send_timer_box(self):
        if self.checkBox_4.checkState():
            time = self.lineEdit_2.text()

            try:
                time_val = int(time, 10)
            except ValueError:
                QMessageBox.critical(self, 'pycom','请输入有效的定时时间!')
                return None

            if time_val == 0:
                QMessageBox.critical(self, 'pycom','定时时间必须大于零!')
                return None
            #定时间隔发送
            self.timer_send.start(time_val)
           
        else:
            self.timer_send.stop()
           

    #清除窗口操作
    def clear(self):
        self.textEdit.clear()
        self.send_num = 0
        self.receive_num = 0
        dis = '发送:'+ '{:d}'.format(self.send_num) + '  接收:' + '{:d}'.format(self.receive_num)
        self.statusBar.showMessage(dis)
       

    #串口接收数据处理
    def recv(self):
       
        try:
            num = self.ser.inWaiting()
        except:

            self.timer_send.stop()
            self.timer.stop()
            #串口拔出错误,关闭定时器
            self.ser.close()
            self.ser = None

           
            #设置为打开按钮状态
            self.pushButton_2.setChecked(False)
            self.pushButton_2.setText("打开串口")
            print('serial error!')
            return None
        if(num > 0):
            #有时间会出现少读到一个字符的情况,还得进行读取第二次,所以多读一个
            data = self.ser.read(num)
           
            #调试打印输出数据
            #print(data)
            num = len(data)
            #十六进制显示
            if self.checkBox_3.checkState():
                out_s=''
                for i in range(0, len(data)):
                    out_s = out_s + '{:02X}'.format(data[i]) + ' '
               
                self.textEdit.insertPlainText(out_s)
                 
            else:     
                #串口接收到的字符串为b'123',要转化成unicode字符串才能输出到窗口中去
                self.textEdit.insertPlainText(data.decode('iso-8859-1'))
               
               
           
            #统计接收字符的数量
            self.receive_num = self.receive_num + num
            dis = '发送:'+ '{:d}'.format(self.send_num) + '  接收:' + '{:d}'.format(self.receive_num)
            self.statusBar.showMessage(dis)
           
            #获取到text光标
            textCursor = self.textEdit.textCursor()
            #滚动到底部
            textCursor.movePosition(textCursor.End)
            #设置光标到text中去
            self.textEdit.setTextCursor(textCursor)
        else:
            pass
           
           
              
       
    #串口发送数据处理
    def send(self):
        if self.ser != None:
            input_s = self.lineEdit.text()
            if input_s != "":

                #发送字符
                if (self.checkBox.checkState() == False):
                    if self.checkBox_2.checkState():
                        #发送新行
                        input_s = input_s + '\r\n'
                    input_s = input_s.encode('utf-8')   
                   
                else:
                    #发送十六进制数据
                    input_s = input_s.strip() #删除前后的空格
                    send_list=[]
                    while input_s != '':
                        try:
                            num = int(input_s[0:2], 16)
                           
                        except ValueError:
                            print('input hex data!')
                            QMessageBox.critical(self, 'pycom','请输入十六进制数据,以空格分开!')
                            return None
                       
                        input_s = input_s[2:]
                        input_s = input_s.strip()
                       
                        #添加到发送列表中
                        send_list.append(num)
                    input_s = bytes(send_list)
                print(input_s)
                #发送数据   
                try:
                    num = self.ser.write(input_s)
                except:

                    self.timer_send.stop()
                    self.timer.stop()
                    #串口拔出错误,关闭定时器
                    self.ser.close()
                    self.ser = None

                   
                    #设置为打开按钮状态
                    self.pushButton_2.setChecked(False)
                    self.pushButton_2.setText("打开串口")
                    print('serial error send!')
                    return None
                   
               
               
                self.send_num = self.send_num + num
                dis = '发送:'+ '{:d}'.format(self.send_num) + '  接收:' + '{:d}'.format(self.receive_num)
                self.statusBar.showMessage(dis)
                #print('send!')
            else:
                print('none data input!')
           
        else:
            #停止发送定时器
            self.timer_send.stop()
            QMessageBox.critical(self, 'pycom','请打开串口')
           
    #打开关闭串口       
    def open_close(self, btn_sta):
       
        if btn_sta == True:
            try:
                #输入参数'COM13',115200
                print(int(self.comboBox_2.currentText()))
                self.ser = serial.Serial(self.comboBox.currentText(), int(self.comboBox_2.currentText()), timeout=0.1)
            except:
                QMessageBox.critical(self, 'pycom','没有可用的串口或当前串口被占用')
                return None
            #字符间隔超时时间设置
            self.ser.interCharTimeout = 0.001   
            #1ms的测试周期
            self.timer.start(2)
            self.pushButton_2.setText("关闭串口")
            print('open!')
        else:
            #关闭定时器,停止读取接收数据
            self.timer_send.stop()
            self.timer.stop()
           
            try:
                #关闭串口
                self.ser.close()
            except:
                QMessageBox.critical(self, 'pycom','关闭串口失败')
                return None
               
            self.ser = None
           
            self.pushButton_2.setText("打开串口")
            print('close!')

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.show()
    sys.exit(app.exec_())

4、界面程序的源代码如下:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'mainwindow.ui'
#
# Created by: PyQt5 UI code generator 5.8.2
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(494, 418)
        self.centralWidget = QtWidgets.QWidget(MainWindow)
        self.centralWidget.setAutoFillBackground(False)
        self.centralWidget.setObjectName("centralWidget")
        self.textEdit = QtWidgets.QTextEdit(self.centralWidget)
        self.textEdit.setGeometry(QtCore.QRect(10, 0, 471, 201))
        self.textEdit.setObjectName("textEdit")
        self.label = QtWidgets.QLabel(self.centralWidget)
        self.label.setGeometry(QtCore.QRect(10, 210, 51, 21))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralWidget)
        self.label_2.setGeometry(QtCore.QRect(10, 240, 51, 21))
        self.label_2.setObjectName("label_2")
        self.comboBox = QtWidgets.QComboBox(self.centralWidget)
        self.comboBox.setGeometry(QtCore.QRect(50, 210, 69, 22))
        self.comboBox.setObjectName("comboBox")
        self.comboBox_2 = QtWidgets.QComboBox(self.centralWidget)
        self.comboBox_2.setGeometry(QtCore.QRect(50, 240, 69, 22))
        self.comboBox_2.setObjectName("comboBox_2")
        self.lineEdit = QtWidgets.QLineEdit(self.centralWidget)
        self.lineEdit.setGeometry(QtCore.QRect(130, 280, 341, 20))
        self.lineEdit.setObjectName("lineEdit")
        self.label_3 = QtWidgets.QLabel(self.centralWidget)
        self.label_3.setGeometry(QtCore.QRect(130, 250, 91, 31))
        self.label_3.setObjectName("label_3")
        self.pushButton = QtWidgets.QPushButton(self.centralWidget)
        self.pushButton.setGeometry(QtCore.QRect(390, 250, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralWidget)
        self.pushButton_2.setGeometry(QtCore.QRect(140, 210, 75, 23))
        self.pushButton_2.setCheckable(True)
        self.pushButton_2.setObjectName("pushButton_2")
        self.comboBox_3 = QtWidgets.QComboBox(self.centralWidget)
        self.comboBox_3.setGeometry(QtCore.QRect(50, 270, 69, 22))
        self.comboBox_3.setObjectName("comboBox_3")
        self.comboBox_4 = QtWidgets.QComboBox(self.centralWidget)
        self.comboBox_4.setGeometry(QtCore.QRect(50, 300, 69, 22))
        self.comboBox_4.setObjectName("comboBox_4")
        self.label_4 = QtWidgets.QLabel(self.centralWidget)
        self.label_4.setGeometry(QtCore.QRect(10, 270, 51, 21))
        self.label_4.setObjectName("label_4")
        self.label_5 = QtWidgets.QLabel(self.centralWidget)
        self.label_5.setGeometry(QtCore.QRect(10, 300, 51, 21))
        self.label_5.setObjectName("label_5")
        self.comboBox_5 = QtWidgets.QComboBox(self.centralWidget)
        self.comboBox_5.setGeometry(QtCore.QRect(50, 330, 69, 22))
        self.comboBox_5.setObjectName("comboBox_5")
        self.label_6 = QtWidgets.QLabel(self.centralWidget)
        self.label_6.setGeometry(QtCore.QRect(10, 330, 51, 21))
        self.label_6.setObjectName("label_6")
        self.pushButton_3 = QtWidgets.QPushButton(self.centralWidget)
        self.pushButton_3.setGeometry(QtCore.QRect(390, 210, 75, 23))
        self.pushButton_3.setObjectName("pushButton_3")
        self.checkBox = QtWidgets.QCheckBox(self.centralWidget)
        self.checkBox.setGeometry(QtCore.QRect(130, 310, 71, 16))
        self.checkBox.setObjectName("checkBox")
        self.checkBox_2 = QtWidgets.QCheckBox(self.centralWidget)
        self.checkBox_2.setGeometry(QtCore.QRect(210, 310, 71, 16))
        self.checkBox_2.setObjectName("checkBox_2")
        self.pushButton_4 = QtWidgets.QPushButton(self.centralWidget)
        self.pushButton_4.setGeometry(QtCore.QRect(220, 210, 81, 23))
        self.pushButton_4.setObjectName("pushButton_4")
        self.checkBox_3 = QtWidgets.QCheckBox(self.centralWidget)
        self.checkBox_3.setGeometry(QtCore.QRect(310, 210, 71, 21))
        self.checkBox_3.setObjectName("checkBox_3")
        self.checkBox_4 = QtWidgets.QCheckBox(self.centralWidget)
        self.checkBox_4.setGeometry(QtCore.QRect(140, 240, 71, 16))
        self.checkBox_4.setObjectName("checkBox_4")
        self.lineEdit_2 = QtWidgets.QLineEdit(self.centralWidget)
        self.lineEdit_2.setGeometry(QtCore.QRect(220, 240, 51, 16))
        self.lineEdit_2.setObjectName("lineEdit_2")
        self.label_7 = QtWidgets.QLabel(self.centralWidget)
        self.label_7.setGeometry(QtCore.QRect(280, 240, 54, 12))
        self.label_7.setObjectName("label_7")
        self.gridLayoutWidget = QtWidgets.QWidget(self.centralWidget)
        self.gridLayoutWidget.setGeometry(QtCore.QRect(0, 0, 2, 2))
        self.gridLayoutWidget.setObjectName("gridLayoutWidget")
        self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
        self.gridLayout.setContentsMargins(11, 11, 11, 11)
        self.gridLayout.setSpacing(6)
        self.gridLayout.setObjectName("gridLayout")
        MainWindow.setCentralWidget(self.centralWidget)
        self.menuBar = QtWidgets.QMenuBar(MainWindow)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 494, 23))
        self.menuBar.setObjectName("menuBar")
        MainWindow.setMenuBar(self.menuBar)
        self.mainToolBar = QtWidgets.QToolBar(MainWindow)
        self.mainToolBar.setObjectName("mainToolBar")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar)
        self.statusBar = QtWidgets.QStatusBar(MainWindow)
        self.statusBar.setObjectName("statusBar")
        MainWindow.setStatusBar(self.statusBar)

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "pycom串口助手(作者:龙腾 [email protected])"))
        self.label.setText(_translate("MainWindow", "串口号"))
        self.label_2.setText(_translate("MainWindow", "波特率"))
        self.lineEdit.setText(_translate("MainWindow", "123"))
        self.label_3.setText(_translate("MainWindow", "字符串输入框:"))
        self.pushButton.setText(_translate("MainWindow", "发送"))
        self.pushButton_2.setText(_translate("MainWindow", "打开串口"))
        self.label_4.setText(_translate("MainWindow", "数据位"))
        self.label_5.setText(_translate("MainWindow", "停止位"))
        self.label_6.setText(_translate("MainWindow", "校验位"))
        self.pushButton_3.setText(_translate("MainWindow", "清除窗口"))
        self.checkBox.setText(_translate("MainWindow", "HEX发送"))
        self.checkBox_2.setText(_translate("MainWindow", "发送新行"))
        self.pushButton_4.setText(_translate("MainWindow", "刷新串口设备"))
        self.checkBox_3.setText(_translate("MainWindow", "HEX显示"))
        self.checkBox_4.setText(_translate("MainWindow", "定时发送"))
        self.lineEdit_2.setText(_translate("MainWindow", "1000"))
        self.label_7.setText(_translate("MainWindow", "ms/次"))

       本人python是边学边用,做出来的软件还有一些小问题,还请大家指点,共同进步。

5、软件图标的程序文件,软件上显示的LOGO要生成pyico.py文件供窗口应用程序调用来显示出来,pyico文件是从图片资源文件自动生成的。

      最后提供一个完整工程的下载链接:https://pan.baidu.com/s/1uNHH_nAa5deGizRCuN2cTQ

      如果你觉得这篇开源教程对你有帮助,请你小***额***打***赏***博***主以示鼓励,写出更好的技术文章来。

       微信支付

  

猜你喜欢

转载自blog.csdn.net/fhqlongteng/article/details/78535393