赞
踩
超级好用的Python QT GUI串口调试助手
目录
1.PyQt5_Serial_Debug_Assistant_V1.0源码
Python串口调试助手支持常用的50bps - 10Mbps波特率,能设置校验、数据位和停止位,能以ASCII码或十六进制接收或发送任何数据或字符,可以任意设定自动发送周期,并能将接收数据实时保存成文本文件,能发送任意大小的数据或字符。
备注: V1.0为简单Demo,适合初级用户使用,V1.1可用于工程应用
高级版: 【博客9】缤果PyQt5串口调试助手V2.0(高级篇)_pyqt 串口助手_Bingo缤果的博客-CSDN博客
英文名:PyQt5_Serial_Debug_Assistant_V1.1
支持:常用的50bps ~ 10Mbps波特率
类型:串口调试助手
软件大小:18M
软件版本:V1.1
软件下载&更新:百度网盘链接_提取码 6666
一款强大而稳定的PyQt串口调试助手,支持常用的110-921600bps波特率及自定义波特率,波特率最高可支持8000000(串口硬件有关),可适应于非标准波特率。支持动态修改串口和波特率,保存当前日志,打开当前日志以及实时保存日志(默认按每小时分包保存日志,100M自动清空接收窗口,防止UI阻塞)。
PyQt5_Serial_Debug_Assistant_V1.1
PyQt5_Serial_Debug_Assistant_V1.1_串口UI布局
PyQt5_Serial_Debug_Assistant_V1.0
代码如下(示例):
- import sys
- import serial # 导入模块 #安装: pip3 install pyserial
- import serial.tools.list_ports
- import webbrowser
- import time
- import datetime
-
- # 导入Ui设计
- from PyQt5.QtWidgets import QApplication, QMainWindow # 串口Ui文件
- from PyQt5.QtWidgets import QMessageBox, QLabel, QFileDialog
- from PyQt5.QtCore import QTimer
- from PyQt5.QtGui import QIcon
-
- import Serial_Ui_Designer # 串口UI文件
-
- # PyQt5程序打包
- # 1.使用PyInstaller来打包:
- # 安装:pip3 install PyInstaller
- # 打包:pyinstaller -F -w -i=Com.ico main.py #单文件打包-优缺点:只生成exe文件,但文件大,打开软件时加载时间长
- # 打包:pyinstaller -D -w -i=Com.ico main.py #多文件打包-优缺点:生成exe关联包,可删除无效库,文件小,运行顺畅
-
- # 主窗口
- class PyQt5_Serial(QMainWindow, Serial_Ui_Designer.Ui_MainWindow):
- # 初始化程序
- def __init__(self):
- super(PyQt5_Serial, self).__init__()
- self.setupUi(self)
- self.Init()
- self.Qt5_Ui_Init()
-
- # 初始化
- def Init(self):
- self.ser = serial.Serial()
- self.port_check()
- self.baudrateBox.setCurrentIndex(5) # 921600-20 9600-5
- self.dataBitsBox.setCurrentIndex(3) # 8
-
- # 设置Logo和标题
- self.setWindowIcon(QIcon('Com.ico'))
- self.setWindowTitle("PyQt5_串口调试助手_V1.0")
- #self.setWindowTitle("PyQt5_Serial_Debug_Assistant_V1.0")
-
- # 发送数据和接收数据数目置零
- self.data_num_sended = 0
- self.label_Tx.setText(str(self.data_num_sended))
- self.data_num_received = 0
- self.label_Rx.setText(str(self.data_num_received))
-
- # 串口关闭按钮使能关闭
- self.sendButton.setEnabled(0)
- self.checkBox_autoSend.setEnabled(0)
-
- # 发送框、文本框清除
- self.sendTextEdit.setText("")
- self.recvTextEdit.setText("")
-
- # 建立控件信号与槽关系
- def Qt5_Ui_Init(self):
- # 串口检测按钮
- self.pushButton_Refresh.clicked.connect(self.port_check)
- # 串口打开按钮
- self.pushButton_Open_Close.clicked.connect(self.port_open_close)
- # 定时发送数据
- self.timer_send = QTimer()
- self.timer_send.timeout.connect(self.data_send)
- self.checkBox_autoSend.stateChanged.connect(self.data_send_timer)
- # 发送数据按钮
- self.sendButton.clicked.connect(self.data_send)
- # 保存日志
- self.pushButton_saveLog.clicked.connect(self.savefiles)
- # 加载文件
- self.pushButton_openLog.clicked.connect(self.openfiles)
- # 跳转链接
- self.commandLinkButton.clicked.connect(self.link)
- # 清除发送按钮
- self.pushButton_ClearSend.clicked.connect(self.send_data_clear)
- # 清除接收按钮
- self.pushButton_ClearRecive.clicked.connect(self.receive_data_clear)
- # RTS
- self.checkBox_RTS.clicked.connect(self.rts_handle)
- # DTR
- self.checkBox_DTR.clicked.connect(self.dtr_handle)
-
- # 串口检测
- def port_check(self):
- # 检测所有存在的串口,将信息存储在字典中
- self.Com_Dict = {}
- port_list = list(serial.tools.list_ports.comports())
-
- self.portNameBox.clear()
- for port in port_list:
- self.Com_Dict["%s" % port[0]] = "%s" % port[1]
- self.portNameBox.addItem(port[0])
-
- # 无串口判断
- if len(self.Com_Dict) == 0:
- self.portNameBox.addItem("无串口")
-
- # 打开/关闭串口
- def port_open_close(self):
- if self.pushButton_Open_Close.text() == "打开串口":
- self.port_open()
- else:
- self.port_close()
-
- # 打开串口
- def port_open(self):
- port = self.portNameBox.currentText()
- # print("port:", port)
-
- baudrate = int(self.baudrateBox.currentText())
- # print("baudrate:", baudrate)
-
- bytesize = int(self.dataBitsBox.currentText()) # 数据位
- # print("bytesize:", bytesize)
-
- parity = self.ParityBox.currentText() # 校验位
- # print("parity:", parity)
-
- stopbits = self.stopBitsBox.currentText() # 停止位
- # print("stopbits:", stopbits)
-
- flowctrl = self.flowControlBox.currentText() # 流控
-
- self.ser.port = port
- self.ser.baudrate = baudrate
-
- # print("bytesize:", bytesize)
- if bytesize == 5:
- self.ser.bytesize = serial.FIVEBITS
- elif bytesize == 6:
- self.ser.bytesize = serial.SIXBITS
- elif bytesize == 7:
- self.ser.bytesize = serial.SEVENBITS
- elif bytesize == 8:
- self.ser.bytesize = serial.EIGHTBITS
- else:
- self.ser.bytesize = serial.EIGHTBITS
-
- # print("parity:", parity)
- if parity == "None":
- self.ser.parity = serial.PARITY_NONE
- elif parity == "Even":
- self.ser.parity = serial.PARITY_EVEN
- elif parity == "Odd":
- self.ser.parity = serial.PARITY_ODD
- elif parity == "Space":
- self.ser.parity = serial.PARITY_SPACE
- elif parity == "Mark":
- self.ser.parity = serial.PARITY_MARK
- else:
- self.ser.parity = serial.PARITY_NONE
-
- # print("stopbits:", stopbits)
- if stopbits == "1":
- self.ser.stopbits = serial.STOPBITS_ONE
- elif stopbits == "1.5":
- self.ser.parity = serial.STOPBITS_ONE_POINT_FIVE
- elif stopbits == "2":
- self.ser.parity = serial.STOPBITS_TWO
- else:
- self.ser.stopbits = serial.STOPBITS_ONE
-
- self.ser.xonxoff = False # 软件流控
- self.ser.rtscts = False # 硬件流控 RTS
- self.ser.dsrdtr = False # 硬件流控 DTR
- # print("flowctrl:", flowctrl)
- if flowctrl == "OFF":
- self.ser.xonxoff = False # 软件流控
- self.ser.rtscts = False # 硬件流控 RTS
- self.ser.dsrdtr = False # 硬件流控 DTR
- elif flowctrl == "Hardware":
- if self.checkBox_DTR.isChecked():
- self.ser.dsrdtr = True #硬件流控 DTR
- if self.checkBox_RTS.isChecked():
- self.ser.rtscts = True #硬件流控 RTS
- elif flowctrl == "Software":
- self.ser.xonxoff = True # 软件流控
- self.ser.rtscts = False # 硬件流控 RTS
- self.ser.dsrdtr = False # 硬件流控 DTR
-
- # print(self.ser)
- # Serial < id = 0x4883040, open = False > (port='COM1', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=None, xonxoff=False, rtscts=False, dsrdtr=False)
-
- try:
- self.ser.open()
- except:
- QMessageBox.critical(self, "串口异常", "串口打开失败! 错误: 拒绝访问(被占用).")
- return None
-
- # 串口打开后,切换开关串口按钮使能状态,防止失误操作
- if self.ser.isOpen():
- self.pushButton_Open_Close.setText("关闭串口")
-
- self.portNameBox.setEnabled(0)
- self.baudrateBox.setEnabled(0)
- self.dataBitsBox.setEnabled(0)
- self.ParityBox.setEnabled(0)
- self.stopBitsBox.setEnabled(0)
- self.flowControlBox.setEnabled(0)
-
- self.pushButton_Refresh.setEnabled(0)
- self.sendButton.setEnabled(1)
- self.checkBox_autoSend.setEnabled(1)
-
- # 定时器接收数据
- self.timer = QTimer()
- self.timer.timeout.connect(self.data_receive)
- # 打开串口接收定时器,周期为1ms
- self.timer.start(1)
-
- # 关闭串口
- def port_close(self):
- try:
- self.timer.stop()
- self.timer_send.stop()
- self.ser.close()
- self.pushButton_Open_Close.setText("打开串口")
-
- self.portNameBox.setEnabled(1)
- self.baudrateBox.setEnabled(1)
- self.dataBitsBox.setEnabled(1)
- self.ParityBox.setEnabled(1)
- self.stopBitsBox.setEnabled(1)
- self.flowControlBox.setEnabled(1)
-
- self.pushButton_Refresh.setEnabled(1)
- self.sendButton.setEnabled(0)
- self.checkBox_autoSend.setEnabled(0)
- except:
- QMessageBox.critical(self, '串口异常', '关闭串口失败,请重启程序!')
- return None
-
- # 定时发送数据
- def data_send_timer(self):
- try:
- if 1<= int(self.spinBox_timeDly.text()) <= 300000: # 定时时间1ms~30s内
- if self.checkBox_autoSend.isChecked():
- self.timer_send.start(int(self.spinBox_timeDly.text()))
- self.spinBox_timeDly.setEnabled(False)
- else:
- self.timer_send.stop()
- self.spinBox_timeDly.setEnabled(True)
- else:
- QMessageBox.critical(self, '定时发送数据异常', '定时发送数据周期仅可设置在300秒内!')
- except:
- QMessageBox.critical(self, '定时发送数据异常', '请设置正确的数值类型!')
-
- # 发送数据
- def data_send(self):
- if self.ser.isOpen():
- input_s = self.sendTextEdit.toPlainText()
-
- # 判断是否为非空字符串
- if input_s != "":
- # 时间显示
- if self.checkBox_displayTime.isChecked():
- if self.checkBox_displaySend.isChecked():
- self.recvTextEdit.insertPlainText(self.get_datetime())
-
- # HEX发送
- if self.checkBox_hexSend.isChecked():
- #input_s = input_s.strip() #strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。
- input_s = input_s.replace(" ", "")
- send_list = []
- while input_s != '':
- try:
- num = int(input_s[0:2], 16) # 没有步长的简单切片
- #print( hex(num) )
- except ValueError:
- QMessageBox.critical(self, '数据异常', '请输入规范的十六进制数据!')
- return None
-
- input_s = input_s[2:]
- send_list.append(num)
-
- if self.checkBox_CR_LF.isChecked():
- send_list.append(0x0D)
- send_list.append(0x0A)
-
- input_s = bytes(send_list)
- # ASCII发送
- else:
- if self.checkBox_CR_LF.isChecked():
- input_s += '\r\n'
-
- input_s = (input_s).encode('utf-8')
-
- # HEX接收显示
- if self.checkBox_hexReceive.isChecked():
- out_s = ''
- for i in range(0, len(input_s)):
- out_s = out_s + '{:02X}'.format(input_s[i]) + ' '
-
- if self.checkBox_displaySend.isChecked():
- self.recvTextEdit.insertPlainText(out_s)
- # ASCII接收显示
- else:
- if self.checkBox_displaySend.isChecked():
- self.recvTextEdit.insertPlainText(input_s.decode('utf-8'))
-
- # 接收换行
- if self.checkBox_AutoLineBreak.isChecked():
- if self.checkBox_displaySend.isChecked():
- self.recvTextEdit.insertPlainText('\r\n')
-
- # 获取到Text光标
- textCursor = self.recvTextEdit.textCursor()
- # 滚动到底部
- textCursor.movePosition(textCursor.End)
- # 设置光标到Text中去
- self.recvTextEdit.setTextCursor(textCursor)
-
- # 统计发送字符数量
- num = self.ser.write(input_s)
- self.data_num_sended += num
- self.label_Tx.setText(str(self.data_num_sended))
- else:
- pass
-
- # 接收数据
- def data_receive(self):
- try:
- num = self.ser.inWaiting()
- # if num > 0:
- # time.sleep(0.1) #100ms
- # num = self.ser.inWaiting() # 延时,再读一次数据,确保数据完整性
- except:
- # QMessageBox.critical(self, '串口异常', '串口接收数据异常,请重新连接设备!')
- # self.port_close()
- return None
-
- if num > 0:
- data = self.ser.read(num)
- num = len(data)
-
- # HEX显示数据
- if self.checkBox_hexReceive.checkState():
- # 时间显示
- if self.checkBox_displayTime.isChecked():
- self.recvTextEdit.insertPlainText(self.get_datetime())
- out_s = ''
- for i in range(0, len(data)):
- out_s = out_s + '{:02X}'.format(data[i]) + ' '
-
- self.recvTextEdit.insertPlainText(out_s)
- # 接收换行
- if self.checkBox_AutoLineBreak.isChecked():
- self.recvTextEdit.insertPlainText('\r\n')
- # ASCII显示数据
- else:
- try:
- if self.checkBox_displayTime.isChecked():
- displayStr = self.get_datetime()
- displayStr += data.decode('utf-8',"ignore")
- displayStr = displayStr.replace("\n", "\n" + self.get_datetime())
- # 接收换行
- if self.checkBox_AutoLineBreak.isChecked():
- displayStr += "\r\n" # 接收换行
- self.recvTextEdit.insertPlainText(displayStr)
- else:
- self.recvTextEdit.insertPlainText(data.decode('utf-8',"ignore"))
- # 接收换行
- if self.checkBox_AutoLineBreak.isChecked():
- self.recvTextEdit.insertPlainText('\r\n')
- except Exception as e:
- print("接收数据异常,波特率错误,请重新配置!\n", e)
-
- # 获取到text光标
- textCursor = self.recvTextEdit.textCursor()
- # 滚动到底部
- textCursor.movePosition(textCursor.End)
- # 设置光标到text中去
- self.recvTextEdit.setTextCursor(textCursor)
-
- # 统计接收字符的数量
- self.data_num_received += num
- self.label_Rx.setText(str(self.data_num_received))
- else:
- pass
-
- # 保存日志
- def savefiles(self):
- dlg = QFileDialog()
- filename = self.portNameBox.currentText() + time.strftime("_%Y-%m-%d_%H_%M_%S", time.localtime())
- filenames = dlg.getSaveFileName(None, "保存日志文件", filename, "Txt files(*.txt)")
-
- try:
- with open(file = filenames[0], mode='w', encoding='utf-8') as file:
- file.write(self.recvTextEdit.toPlainText())
- except:
- #QMessageBox.critical(self, '日志异常', '保存日志文件失败!')
- pass
-
- # 加载日志
- 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.sendTextEdit.setPlainText(file.read())
- except:
- # QMessageBox.critical(self, '日志异常', '加载日志文件失败!')
- pass
-
- # 打开博客链接
- def link(self):
- webbrowser.open('https://blog.csdn.net/santu5234?type=blog')
-
- # 清除发送数据显示
- def send_data_clear(self):
- self.sendTextEdit.setText("")
-
- self.data_num_sended = 0
- self.label_Tx.setText(str(self.data_num_sended))
-
- # 清除接收数据显示
- def receive_data_clear(self):
- self.recvTextEdit.setText("")
-
- self.data_num_received = 0
- self.label_Rx.setText(str(self.data_num_received))
-
- self.data_num_sended = 0
- self.label_Tx.setText(str(self.data_num_sended))
-
- # 时间格式
- def get_datetime(self):
- time_now = datetime.datetime.now()
- # print(str(time_now)[:-3])
- time_now = "[" + str(time_now)[:-3] + "] " # 转为字符串后切片
- return time_now
-
- # RTS
- def rts_handle(self):
- if self.ser.isOpen():
- if self.checkBox_RTS.isChecked():
- self.ser.setRTS(1)
- else:
- self.ser.setRTS(0)
- # DTR
- def dtr_handle(self):
- if self.ser.isOpen():
- if self.checkBox_DTR.isChecked():
- self.ser.setDTR(1)
- else:
- self.ser.setDTR(0)
-
- # 主函数
- def main():
- print("Hello, I'm PyQt5_Serial_Debug_Assistant_V1.0")
- # 1、创建QApplication类的实例对象
- app = QApplication(sys.argv)
- # 2、创建一个 PyQt5_Serial 实例对象
- myMainWindow = PyQt5_Serial()
- # 3、显示主窗口
- myMainWindow.show()
- # 4、进入程序的主循环、并通过exit函数确保主循环安全结束
- sys.exit(app.exec_())
-
- if __name__ == '__main__':
- main()
PyQt5_Serial_Debug_Assistant_V1.0&V1.1
欢迎下载&更新使用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。