遇到的问题

1. 程序结构

创建一个主窗口的类:在里面主要实现窗口UI的绘制,并定义一些槽函数接口

创建主框架类:该类继承自主窗口类,并实现主窗口类中的槽函数,在该类中创建串口接收线程

串口接收线程类:该类继承自QtCore.QThread类,主要进行串口接收处理

2. 多线程

在程序结构上,需要建立两个线程:主线程和串口接收线程;主线程在程序启动时就存在;串口接收线程主要负责在后台不断的读取串口接收缓存中的数据,判断是否有数据到来。多线程通过创立一个继承QtCore.QThread的类来实现;为什么没有使用threading.Thread?因为没有threading.Thread类中找到终止线程的API,所以改用QtCore.QThread。

终止线程:

self.serialThread.quit() # serialThread为我创建的线程实例对象

点击窗口的"X"关闭窗口时,需要对线程资源进行清理

当点击窗口的"X"时,我们可以通过在我们的窗口类中重写closeEvent()方法实现最后的资源清理,在该串口程序中,实现的功能如下:

# 重写关闭窗口事件

def closeEvent(self, event):

if self.serialPara['serialPt'].isOpen() == True: # 如果串口打开了,说明线程正在运行,需要终止线程

self.serialThread.quit() # 终止线程

对接收的bytes数据进行显示处理

通过串口接收到的数据是bytes类型,类似于:b'\x12\xde\x7f' 这种形式

对于0~ 0x7f之间的数据可以使用decode()进行解码,但是0x7f之后的数据使用decode()解码时,会提示不能对utf-8的字符使用decode()进行解码,因为ascii码的范围在0 ~ 0x7f之间

解决方法:在程序中,需要对发送的数据进行格式选择:hex发送或ascii码发送两种形式,

当使用ascii码形式发送数据时直接使用decode()对接收到的数据进行解码

当使用hex码形式发送数据时,我们的串口工具需要将数据一个一个的取出来,然后使用hex()转换为hex形式的数据,然后去除0x,再将它们以空格为分割符拼接在一起;代码如下:

def run(self):

print ("启动线程")

while True:

# 获得接收到的字符

count = self.Ser.inWaiting()

if count != 0:

dealStr = ""

# 读串口数据

recv = self.Ser.read(count)

recv = recv.upper()

# 在这里将接收到数据进行区分:hex 或 字符串

# hex 格式:\xYY\xYY\xYY,如果接收到的字符是这种格式,则说明是hex字符,我们需要将

# \x去除掉,取出YY,然后组成字符串返回

# 如果接收到的是字符串,则使用decode进行解码

print ("接收到的数据 %s \n类型为: %s\n" % (recv, type(recv)))

# 尝试使用decode解码,如果失败,则表示接收到的数据为hex发送过来的数据

try:

dealStr = recv.decode()

except (TypeError, UnicodeDecodeError):

for i in range(len(recv)):

print (hex(recv[i])[2:])

dealStr += hex(recv[i])[2:]

dealStr +=' '

dealStr.rstrip(' ')

print ("处理后的数据 %s \n类型为: %s\n" % (dealStr, type(dealStr)))

# 显示接收到的数据

self.dispContent(dealStr)

# 清空接收缓冲区

self.Ser.flushInput()

time.sleep(0.1)

if self.Ser.isOpen() == False:

print ("关闭线程")

self.quit()

return

程序代码如下:

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

from PyQt5 import QtGui

from PyQt5 import QtCore

from PyQt5 import QtWidgets

import sys

import serial

import win32api

import win32com

import binascii

import struct

import time

import threading

import codecs

class MainDialog(QtWidgets.QDialog):

# ---定义属性

# 发送buf

def __init__(self, parent = None):

super(MainDialog, self).__init__()

self.setWindowTitle(self.tr("串口助手"))

self.serialNo = ("COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9")

self.serialBaud = (1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200)

self.serialChk = {"None": serial.PARITY_NONE,

"Odd": serial.PARITY_EVEN,

"Even": serial.PARITY_ODD}

self.serialChkCode = ("None", "Odd", "Even")

self.serialStopBitCode = (serial.STOPBITS_ONE, serial.STOPBITS_ONE_POINT_FIVE, serial.STOPBITS_TWO)

self.serialPara = {

"comNo": 0,

"baud": 9600,

"stopBit": 1,

"dataBit": 8,

"chk": "odd",

"serialPt": "串口1"}

# 串口状态,打开/关闭

self.isSerialOpen = False

self.sendBuf = []

self.initUI()

def initUI(self):

layout = QtWidgets.QVBoxLayout(self)

# 内容显示区

self.contentDispText = QtWidgets.QTextEdit()

self.contentDispText.setReadOnly(True)

# 设置区

line1_inputLabel = QtWidgets.QLabel(self.tr("输入框:"))

self.line1_inputLineEdit = QtWidgets.QLineEdit()

self.line1_sendButton = QtWidgets.QPushButton(self.tr("发送"))

self.line1_clearButton = QtWidgets.QPushButton(self.tr("清除"))

self.line1_hexdispCheckBox = QtWidgets.QCheckBox(self.tr("HEX显示"))

# 第二行

line2_serialNoLabel = QtWidgets.QLabel(self.tr("串口号:"))

self.line2_serialComboBox = QtWidgets.QComboBox()

for uart in self.serialNo:

self.line2_serialComboBox.insertItem(self.serialNo.index(uart), self.tr(uart))

line2_serialBaudLabel = QtWidgets.QLabel(self.tr("波特率:"))

self.line2_serialBaudComboBox = QtWidgets.QComboBox()

for baud in self.serialBaud:

self.line2_serialBaudComboBox.insertItem(self.serialBaud.index(baud), self.tr(str(baud)))

self.line2_serialBaudComboBox.setCurrentIndex(3)

self.line2_OpenSerialButton = QtWidgets.QPushButton(self.tr("打开串口"))

# 此处还要添加一个指示灯,考虑是否可以使用图片表示工作状态

self.line2_hexSendCheckBox = QtWidgets.QCheckBox(self.tr("HEX发送"))

self.line2_hexSendCheckBox.setChecked(True)

# 第三行

line3_dataBitLabel = QtWidgets.QLabel(self.tr("数据位:"))

self.line3_dataBitComboBox = QtWidgets.QComboBox()

i = 0

for bit in range(5, 9):

self.line3_dataBitComboBox.insertItem(i, self.tr(str(bit)))

i += 1

self.line3_dataBitComboBox.setCurrentIndex(3)

line3_checkLabel = QtWidgets.QLabel(self.tr("校验位:"))

self.line3_checkComboBox = QtWidgets.QComboBox()

for chk in self.serialChkCode:

self.line3_checkComboBox.insertItem(self.serialChkCode.index(chk), self.tr(chk))

line3_stopBitLabel = QtWidgets.QLabel(self.tr("停止位:"))

self.line3_stopBitComboBox = QtWidgets.QComboBox()

self.line3_stopBitComboBox.insertItem(0, self.tr('1'))

self.line3_stopBitComboBox.insertItem(1, self.tr('1.5'))

self.line3_stopBitComboBox.insertItem(2, self.tr('2'))

self.line3_stopBitComboBox.setCurrentIndex(0)

# 此处还需要添加一个扩展功能

setHbox = QtWidgets.QHBoxLayout()

setGridLayout = QtWidgets.QGridLayout()

setGridLayout.setContentsMargins(0, 0, 0, 0)

setGridLayout.setSpacing(10)

setGridLayout.addWidget(line1_inputLabel, 0, 0)

setGridLayout.addWidget(self.line1_inputLineEdit, 0, 1, 1, 3)

setGridLayout.addWidget(self.line1_sendButton, 0, 4)

setGridLayout.addWidget(self.line1_clearButton, 0, 5)

setGridLayout.addWidget(self.line1_hexdispCheckBox, 0, 6)

setGridLayout.addWidget(line2_serialNoLabel, 1, 0)

setGridLayout.addWidget(self.line2_serialComboBox, 1, 1)

setGridLayout.addWidget(line2_serialBaudLabel, 1, 2)

setGridLayout.addWidget(self.line2_serialBaudComboBox, 1, 3)

setGridLayout.addWidget(self.line2_OpenSerialButton, 1, 4)

setGridLayout.addWidget(self.line2_hexSendCheckBox, 1, 6)

setGridLayout.addWidget(line3_dataBitLabel, 2, 0)

setGridLayout.addWidget(self.line3_dataBitComboBox, 2, 1)

setGridLayout.addWidget(line3_checkLabel, 2, 2)

setGridLayout.addWidget(self.line3_checkComboBox, 2, 3)

setGridLayout.addWidget(line3_stopBitLabel, 2, 4)

setGridLayout.addWidget(self.line3_stopBitComboBox, 2, 5)

setHbox.addLayout(setGridLayout)

setHbox.setSizeConstraint(QtWidgets.QLayout.SetFixedSize)

layout.addWidget(self.contentDispText)

layout.addLayout(setHbox)

# -----对控件进行初始化----

self.line1_sendButton.setEnabled(False) # 在没打开串口时,设置串口为无法操作的状态

# -----为按钮添加事件-----

# 串口开关操作

#self.line2_OpenSerialButton.clicked.connect(self.serialState)

# 这里省略了receiver,使用的是connect中的一个重载函数,receiver默认为this

# 在PYQT4.5之后,这是一种新的信号和槽的 API,老的例子API: button.clicked.connect(self.onClicked)

self.line2_OpenSerialButton.clicked.connect(self.serialState)

#self.connect(self.line2_OpenSerialButton, QtCore.SIGNAL("clicked()"), self.serialState)

# 发送数据操作

self.line1_sendButton.clicked.connect(self.writeStr)

#self.connect(self.line1_sendButton, QtCore.SIGNAL("clicked()"), self.writeStr)

# 清除按钮

self.line1_clearButton.clicked.connect(self.contentDispText.clear)

#self.connect(self.line1_clearButton, QtCore.SIGNAL("clicked()"), self.contentDispText, QtCore.SLOT("clear()"))

# 窗口的关闭按钮

#self.connect(self, QtCore.SIGNAL(""))

def setSerialState(self, state):

pass

# 显示收发数据

def dispContent(self, argvStr):

pass

#self.contentDispText.append(r'\n')

# 处理输入的数据

def dealInputData(self, argv):

pass

def writeStr(self):

pass

def serialState(self):

pass

class ReceiveThread(QtCore.QThread):

def __init__(self, Ser, dispContent):

super(ReceiveThread, self).__init__()

self.Ser = Ser

self.dispContent = dispContent

print ("创建线程")

def run(self):

print ("启动线程")

while True:

# 获得接收到的字符

count = self.Ser.inWaiting()

if count != 0:

dealStr = ""

# 读串口数据

recv = self.Ser.read(count)

# 在这里将接收到数据进行区分:hex 或 字符串

# hex 格式:\xYY\xYY\xYY,如果接收到的字符是这种格式,则说明是hex字符,我们需要将

# \x去除掉,取出YY,然后组成字符串返回

# 如果接收到的是字符串,则使用decode进行解码

print ("接收到的数据 %s \n类型为: %s\n" % (recv, type(recv)))

try:

dealStr = recv.decode()

except (TypeError, UnicodeDecodeError):

for i in range(len(recv)):

print ("不可以吗")

print (hex(recv[i])[2:])

dealStr += hex(recv[i])[2:]

dealStr +=' '

print ("处理后的数据 %s \n类型为: %s\n" % (dealStr, type(dealStr)))

# 显示接收到的数据

self.dispContent(dealStr)

# 清空接收缓冲区

self.Ser.flushInput()

time.sleep(0.1)

if self.Ser.isOpen() == False:

print ("关闭线程")

self.quit()

return

class SerialFrame(MainDialog):

def __init__(self, parent = None):

super(SerialFrame, self).__init__(parent)

# 重写关闭窗口事件

def closeEvent(self, event):

if self.serialPara['serialPt'].isOpen() == True:

self.serialThread.quit()

def getSerialPt(self):

return self.serialPara['serialPt']

def setSerialState(self, state):

self.isSerialOpen = state

if self.isSerialOpen == True:

self.line2_OpenSerialButton.setText(self.tr("关闭串口"))

else:

self.line2_OpenSerialButton.setText(self.tr("打开串口"))

# 设置串口其他参数的控件为不可设置状态

self.line2_serialComboBox.setDisabled(state)

self.line2_serialBaudComboBox.setDisabled(state)

self.line3_stopBitComboBox.setDisabled(state)

self.line3_checkComboBox.setDisabled(state)

self.line3_dataBitComboBox.setDisabled(state)

self.line1_sendButton.setEnabled(state) # 在没打开串口时,设置串口为无法操作的状态

# 显示收发数据

def dispContent(self, argvStr):

isHexDisp = self.line1_hexdispCheckBox.isChecked()

argvStr = str(argvStr)

if isHexDisp == False:

self.contentDispText.append(argvStr)

else:

s = ""

for i in range(len(argvStr)):

hval = ord(argvStr[i])

hhex = "%02x" % hval

s += hhex + ' '

self.contentDispText.append(s)

# self.contentDispText.append(r'\n')

# 处理输入的数据

def dealInputData(self, argv):

# 将QString转换为string,因为要使用python内置的函数,必须进行转换

strList = str(argv)

if len(strList) == 0:

QtWidgets.QMessageBox.information(self, "警告", "请输入字符后,再发送!", QtWidgets.QMessageBox.Ok)

return "InPuT eRRoR"

else:

# 1. 判断是HEX发送还是字符串发送

isHex = self.line2_hexSendCheckBox.isChecked()

if isHex == True: # HEX发送

strList = strList.strip() # 去除两边的空格

strList = strList.upper() # 转换为大写字母

list = []

list = strList.split() # 以空格为分隔符分隔字符串,并存入list列表中

getStr = ""

# 假如输入的字符是:ff 55 aa cc 01

for i in range(len(list)):

if len(list[i]) < 2:

list[i] = '0' + list[i]

getStr += list[i]

# 到这一步时字符已经被处理为:ff55aacc01

# 通过decode("hex")进行处理后的数据就是两个字符为一组的十进制数据

# 进行异常处理,当进行数据格式转换时,因为可能有很多种情况导致转换失败,所以此处使用异常处理

try:

return codecs.decode(getStr, "hex_codec")

except ValueError:

print ("abdc")

QtWidgets.QMessageBox.information(self, "警告", "请输入十六进制字符!", QtWidgets.QMessageBox.Ok)

return "InPuT eRRoR"

else:

# 字符串发送,转换为utf-8格式

return strList.encode("utf-8")

def writeStr(self):

# 读取数据并返回

inputStr = self.line1_inputLineEdit.text()

# 处理数据

list = self.dealInputData(inputStr)

if list == "InPuT eRRoR":

return

else:

# 发送数据

self.serialPara["serialPt"].write(list)

# 将发送的数据显示在文本框中

self.dispContent(inputStr)

def serialState(self):

if self.isSerialOpen == False:

try:

# 获取选择的串口编号

self.serialPara["comNo"] = self.line2_serialComboBox.currentIndex()

# 获取串口参数信息:波特率、数据位、校验位、停止位

self.serialPara["baud"] = self.serialBaud[self.line2_serialBaudComboBox.currentIndex()]

self.serialPara["dataBit"] = int(self.line3_dataBitComboBox.currentText())

self.serialPara["chk"] = self.serialChk[self.serialChkCode[self.line3_checkComboBox.currentIndex()]]

self.serialPara["stopBit"] = self.serialStopBitCode[self.line3_stopBitComboBox.currentIndex()]

# 打开串口

self.serialPara["serialPt"] = serial.Serial()

self.serialPara["serialPt"].baudrate = self.serialPara["baud"]

self.serialPara["serialPt"].parity = self.serialPara["chk"]

self.serialPara["serialPt"].stopbits = self.serialPara["stopBit"]

self.serialPara["serialPt"].bytesize = self.serialPara["dataBit"]

self.serialPara["serialPt"].port = self.serialPara["comNo"]

self.serialPara["serialPt"].open()

# 启动线程

self.serialThread = ReceiveThread(self.serialPara["serialPt"], self.dispContent)

self.serialThread.start()

except Exception:

QtWidgets.QMessageBox.information(self, "警告", "串口打开失败", QtWidgets.QMessageBox.Ok)

return

# print "端口号: %d" % self.serialPara["comNo"]

else:

self.serialPara["serialPt"].close()

self.setSerialState(not self.isSerialOpen)

if __name__ == "__main__":

app = QtWidgets.QApplication(sys.argv)

m = SerialFrame()

#event = SerialOperate(m)

m.show()

app.exec_()

python 串口助手 简书_[pyqt5-pyserial实现一个串口调试工具记录]相关推荐

  1. python 串口助手 简书_python用pyserial读取串口问题解决

    object is not callable Error: 'bool' object is not callable 没有返回值 正确代码: ser = serial.Serial('COM7', ...

  2. python 串口助手 简书_python 制作串口工具(二)

    如需转载,请标明出处! Beautiful is better than ugly. 前言 继续上一篇使用 python 制作串口工具(一),完成要实现的串口工具代码逻辑! 实现 最终效果: imag ...

  3. python工厂模式 简书_工厂

    思考题 public void printMenu() { PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); ArrayList ...

  4. python工厂模式 简书_工厂模式

    什么是工厂设计模式? 定义一个用于创建对象的接口,让子类决定将哪一个类实例化,专门用来生产对象.在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如 ...

  5. 小甲鱼python课后题简书_【Python爬虫】-笨办法学 Python 习题01-10

    一.作业内容: 01.将下面的内容写到一个文件中,取名为ex1.py.这个命名方式很重要,Python文件最好以.py结尾. 1 print "Hello World!" 2 pr ...

  6. python飞机大战简书_飞机大战(pygame)开发实录一

    飞机大战,算是面向对象的程序语言入门必修程序,但大多教程只是点到为止,跳过了很多实际开发会遇到的问题,自然也错过了很多不错的经验.该系列文章针对python的pygame包,详细讲解了飞机大战的开发. ...

  7. python进行数据分析 简书_《利用python进行数据分析》读书笔记1

    读取json内容: import json path='路径\文件名.txt' records=[json.loads(line) for line in open(path)] #records为由 ...

  8. python工厂模式 简书_[Python设计模式] 01 - 简单工厂模式

    设计模式的目的是让代码易维护.易扩展,不能为了模式而模式,因此一个简单的工具脚本是不需要用到任何模式的. 简单工厂模式又叫静态工厂方法模式,工厂模式家族中最简单的一种模式.这个模式的基本工作方式: 通 ...

  9. [译] 12步轻松搞定python装饰器 - 简书

    [译] 12步轻松搞定python装饰器 - 简书 呵呵!作为一名教python的老师,我发现学生们基本上一开始很难搞定python的装饰器,也许因为装饰器确实很难懂.搞定装饰器需要你了解一些函数式编 ...

最新文章

  1. 关键数据保险箱,AD RMS服务器部署指南
  2. 办公室影响同事关系的九种行为
  3. Windows 11成上班摸鱼利器,微软CEO喊话苹果:欢迎引入iMessage
  4. java正则表示判断。是否以某个关键字结尾的
  5. linux访问文档根目录之外的网页_开发文档加载不再卡顿,亿点点提升
  6. 无监督学习:无监督降维
  7. Qt Creator优化移动设备的应用程序
  8. 剑指offer之 旋转数组的最小数字
  9. java在线找错_平台配置及测试错误提示及解决方案
  10. R树空间索引及其变种
  11. 取得数组最大值与最小值
  12. linux下i2c驱动笔记
  13. DAG镶嵌模型+原始路径打印
  14. 计算机应用基础搜题答案,大一计算机应用基础试题及答案
  15. maven命令打jar包
  16. word文档通配符换行_Word效率指南(二)
  17. bmp怎样转成jpg?
  18. 修复cdn服务器连接异,cdn服务器连接异常怎么处理
  19. Android 画闹钟
  20. 音视频dsp中对音频的处理

热门文章

  1. 什么是前端?前端技术包含哪些?什么是后端?
  2. CC2530中adc的使用记录
  3. 基于asp.net的篮球俱乐部管理系统
  4. 更改Delph窗体的默认字体
  5. java血饮狂刀_血饮狂刀PC版
  6. vue调用微信扫描条形码
  7. 基于Ubuntu的ORB-SLAM2项目环境搭建过程
  8. 最大程度地降低乘员地伤害程度
  9. [论文分享]去中心化金融(DeFi)安全与防护现状
  10. JavaScript-函数的简单运用小案例