1.Pyqt5作为图像UI设计模块,用到了QT内置的Qt Design Studio设计图像界面生成界面代码。
2.Python中多线程Thread模块,用于分离绘图,串口数据接收和界面UI刷新,以防止软件卡顿,影响用户使用。
3.Python中Matplotlib和numpy模块作为绘图工具模块,哦,对哦,还有队列Queue用来线程间通信。
4.导入的serial模块用来接受串口数据。
5.只是自己实践任务,有理解错误的,或者更好优化的欢迎大神们交流指正~~~.~~~___~~~.~~~
6.Everything is Bingo !!! 咱们开始吧(持续更新中ING)
7.分享个经验,学习Python模块最好办法,浏览他们的源代码,还有度娘,哈啊哈 !!!
ps:线程和主线程之间还有信号通信,加的画图太丑(ps:这个编辑器的代码生成图像有点儿次了)就不加了
1.Qt Design Studio是一款UI设计和开发工具,它让设计师和开发者可以迅速设计原型,并且开发复杂的可伸缩的UI。(百度的,反正挺好使,没弄到中文版,只有英文版的,具体的安装教程CSDN可以找到,我就不赘述了),贴个推荐吧:转自链接:https://www.cnblogs.com/lsdb/p/9121903.html 直接整Qt Design Studio 5.4 版本WIN10
2.Qt Designer用于像VC++的MFC一样拖放、设计控件
PyUIC用于将Qt Designer生成的.ui文件转换成.py文件
Qt Designer和PyUIC都包含在PyQt5中,所以我们只需要安装PyQt5塻块然后再指定Qt Designer和PyUIC即可
使用教程:链接https://www.cnblogs.com/lsdb/p/9122425.html
以下是本人设计的UI窗口(不是很好看哈哈,能用就行)以及用PyUIC生成的UI_Main窗口代码:
UI_main 生成的串口代码:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'shiboqi_main.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# 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(880, 461)
MainWindow.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(670, 10, 61, 20))
self.label.setObjectName("label")
self.com_list = QtWidgets.QComboBox(self.centralwidget)
self.com_list.setGeometry(QtCore.QRect(740, 10, 71, 20))
self.com_list.setObjectName("com_list")
self.com_baud = QtWidgets.QComboBox(self.centralwidget)
self.com_baud.setGeometry(QtCore.QRect(740, 40, 71, 20))
self.com_baud.setObjectName("com_baud")
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(670, 70, 51, 20))
self.label_3.setObjectName("label_3")
self.com_bytes = QtWidgets.QComboBox(self.centralwidget)
self.com_bytes.setGeometry(QtCore.QRect(740, 70, 69, 22))
self.com_bytes.setObjectName("com_bytes")
self.label_4 = QtWidgets.QLabel(self.centralwidget)
self.label_4.setGeometry(QtCore.QRect(670, 100, 54, 16))
self.label_4.setObjectName("label_4")
self.com_stopbytes = QtWidgets.QComboBox(self.centralwidget)
self.com_stopbytes.setGeometry(QtCore.QRect(740, 100, 69, 22))
self.com_stopbytes.setObjectName("com_stopbytes")
self.label_5 = QtWidgets.QLabel(self.centralwidget)
self.label_5.setGeometry(QtCore.QRect(670, 130, 54, 16))
self.label_5.setObjectName("label_5")
self.com_checkbyte = QtWidgets.QComboBox(self.centralwidget)
self.com_checkbyte.setGeometry(QtCore.QRect(740, 130, 69, 22))
self.com_checkbyte.setObjectName("com_checkbyte")
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(670, 40, 54, 16))
self.label_2.setObjectName("label_2")
self.Com_Open = QtWidgets.QPushButton(self.centralwidget)
self.Com_Open.setGeometry(QtCore.QRect(670, 160, 81, 23))
self.Com_Open.setObjectName("Com_Open")
self.com_refrsh = QtWidgets.QPushButton(self.centralwidget)
self.com_refrsh.setGeometry(QtCore.QRect(670, 190, 171, 23))
self.com_refrsh.setObjectName("com_refrsh")
self.label_6 = QtWidgets.QLabel(self.centralwidget)
self.label_6.setGeometry(QtCore.QRect(670, 220, 91, 20))
self.label_6.setObjectName("label_6")
self.Data_Test = QtWidgets.QComboBox(self.centralwidget)
self.Data_Test.setGeometry(QtCore.QRect(760, 220, 69, 22))
self.Data_Test.setObjectName("Data_Test")
self.Data_testb = QtWidgets.QPushButton(self.centralwidget)
self.Data_testb.setGeometry(QtCore.QRect(760, 250, 75, 23))
self.Data_testb.setObjectName("Data_testb")
self.comstate_tip = QtWidgets.QLabel(self.centralwidget)
self.comstate_tip.setGeometry(QtCore.QRect(810, 390, 54, 20))
self.comstate_tip.setObjectName("comstate_tip")
self.label_10 = QtWidgets.QLabel(self.centralwidget)
self.label_10.setGeometry(QtCore.QRect(720, 390, 81, 20))
self.label_10.setObjectName("label_10")
self.label_11 = QtWidgets.QLabel(self.centralwidget)
self.label_11.setGeometry(QtCore.QRect(720, 360, 81, 20))
self.label_11.setObjectName("label_11")
self.paint_tip = QtWidgets.QLabel(self.centralwidget)
self.paint_tip.setGeometry(QtCore.QRect(810, 360, 54, 20))
self.paint_tip.setObjectName("paint_tip")
self.label_13 = QtWidgets.QLabel(self.centralwidget)
self.label_13.setGeometry(QtCore.QRect(690, 310, 161, 41))
self.label_13.setLineWidth(2)
self.label_13.setObjectName("label_13")
self.label_7 = QtWidgets.QLabel(self.centralwidget)
self.label_7.setGeometry(QtCore.QRect(400, 370, 54, 16))
self.label_7.setObjectName("label_7")
self.label_8 = QtWidgets.QLabel(self.centralwidget)
self.label_8.setGeometry(QtCore.QRect(400, 390, 54, 20))
self.label_8.setObjectName("label_8")
self.paint_pause = QtWidgets.QPushButton(self.centralwidget)
self.paint_pause.setGeometry(QtCore.QRect(580, 370, 75, 23))
self.paint_pause.setObjectName("paint_pause")
self.update_AT = QtWidgets.QPushButton(self.centralwidget)
self.update_AT.setGeometry(QtCore.QRect(580, 390, 75, 23))
self.update_AT.setObjectName("update_AT")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(460, 370, 113, 20))
self.lineEdit.setObjectName("lineEdit")
self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit_2.setGeometry(QtCore.QRect(460, 390, 113, 20))
self.lineEdit_2.setObjectName("lineEdit_2")
self.com_close = QtWidgets.QPushButton(self.centralwidget)
self.com_close.setGeometry(QtCore.QRect(760, 160, 81, 23))
self.com_close.setObjectName("com_close")
self.paint_widget = QtWidgets.QWidget(self.centralwidget)
self.paint_widget.setGeometry(QtCore.QRect(10, 10, 641, 351))
self.paint_widget.setObjectName("paint_widget")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 880, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.label.setBuddy(self.com_list)
self.label_3.setBuddy(self.com_bytes)
self.label_4.setBuddy(self.com_stopbytes)
self.label_5.setBuddy(self.com_checkbyte)
self.label_2.setBuddy(self.com_baud)
self.label_6.setBuddy(self.Data_Test)
self.comstate_tip.setBuddy(self.Com_Open)
self.paint_tip.setBuddy(self.paint_pause)
self.label_7.setBuddy(self.lineEdit)
self.label_8.setBuddy(self.lineEdit_2)
self.retranslateUi(MainWindow)
self.Com_Open.clicked['bool'].connect(self.comstate_tip.update)
self.paint_pause.clicked.connect(self.paint_tip.update)
self.update_AT.clicked.connect(self.lineEdit.copy)
self.update_AT.clicked.connect(self.lineEdit_2.copy)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "串口号:"))
self.label_3.setText(_translate("MainWindow", "数据位:"))
self.label_4.setText(_translate("MainWindow", "停止位:"))
self.label_5.setText(_translate("MainWindow", "校验位:"))
self.label_2.setText(_translate("MainWindow", "波特率:"))
self.Com_Open.setText(_translate("MainWindow", "打开串口连接"))
self.com_refrsh.setText(_translate("MainWindow", "刷新串口设备"))
self.label_6.setText(_translate("MainWindow", "数据显示测试:"))
self.Data_testb.setText(_translate("MainWindow", "开始测试"))
self.comstate_tip.setText(_translate("MainWindow", "串口关"))
self.label_10.setText(_translate("MainWindow", "串口运行状态:"))
self.label_11.setText(_translate("MainWindow", "绘图运行状态:"))
self.paint_tip.setText(_translate("MainWindow", "绘图开始"))
self.label_13.setText(_translate("MainWindow", "运行状态监测:"))
self.label_7.setText(_translate("MainWindow", "幅值:"))
self.label_8.setText(_translate("MainWindow", "数据频率:"))
self.paint_pause.setText(_translate("MainWindow", "暂停绘图"))
self.update_AT.setText(_translate("MainWindow", "更新A-T"))
self.com_close.setText(_translate("MainWindow", "关闭串口连接"))
QThread类提供了一个与平台无关的管理线程的方法。一个QThread对象管理一个线程。QThread的执行从
run()函数的执行开始,在Qt自带的QThread类中,run()函数通过调用exec()函数来启动事件循环机制,并且在线程内部处理Qt的事件。在Qt中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,从而让主界面能及时响应用户的请求操作。QThread的使用方法有如下两种:
1.QObject::moveToThread()
2.继承QThread类(本处用这个)
1.自定义一个继承QThread的类MyThread,重载MyThread中的run()函数,在run()函数中写入需要执行的工作.
2.调用start()函数来启动线程。
具体实现如下(给个例子):
from PyQt5.QtCore import QThread
class Mythread(QThread):
def __init__(self,parent = None):
super(Mythread,self).__init__()
# 重写QThread的run函数
def run(self):
i = 0
while i<100:
i += 1
print(i)
t = Mythread()
t.start()
sum = 0
for k in range(100):
sum += k
print(sum)
## 运行于此大家会发现两个运算在同时进行 (其实是近似,python内的线程并非真正的线程)
这是个从属于QThread类的特殊机制,可以用于Qthread线程之间的通信的实现,根据 定义类— 连接 — 触发来整。具体咱直接看这里的例子。
代码贴下来:
from PyQt5 import QtCore
class MySingnal(QThread):
# 信号创建记得放在__init__()之外,作为类属性
Data_Signal = QtCore.pyqtSignal(list)
Tip_Singal = QtCore.pyqtSignal(str,str)
Paint_Singal = QtCore.pyqtSignal(int)
def __init__(self):
super(MySingnal,self).__init__()
def Data_Sender(self,Data):
self.Data_Signal.emit(Data)
def Tip_Sender(self,title,message):
self.Tip_Singal.emit(title,message)
def Paint_Sender(self,int_data):
self.Paint_Singal.emit(int_data)
def singalFunction(str,str2):
print(str,str2)
t = MySingnal()
t.Tip_Singal.connect(singalFunction)
t.Tip_Singal.emit("你说巧不巧","调用成功了耶")
pyserial模块封装了对串口的访问。
罗辑思维的主讲人罗胖在《这代人的学习》这期节目中曾经讲过:学习最快的途径是找着一个牛人带你学习。而作为当代的程序员而言,我们编程时,难免会有我们不曾碰到的一些模块啊,程序思路啊等等等等。所以当遇到这些问题了咋办,指定是必须得干的嘛!那我们就必须得学习这些新东西,而学习最快得途径就是找那些大牛带你学,咱不可能买着个飞机票去拜访人家跟TA唠嗑去,所以我们有啥办法咧,当然是看书,看(源码)《—————————————————— 就它 ^ _ ^
浏览serial得源码时,发现了在 路径serial //threaded//__ init __py里面的源码有串口线程通信协议和它的代码示例,这可帮了咱们不少忙!我们可以利用他的通信协议,重写线程run和他的数据处理方式,这样就可以实现我们所需的串口线程通信协议了!!!好了,开整!
1.接下来咱们就需要修改一下子协议了!按照我们下位机的传输数据格式把其分割开,读取出我们所需要的数据。至于类的继承和重写方法这些东西我就不再赘述了。代码如下
class PrintLines(FramedPacket):
def __init__(self):
super(FramedPacket, self).__init__()
self.packet = bytearray()
def connection_lost(self, exc):
"""Forget transport"""
self.transport = None
self.in_packet = False
del self.packet[:]
super(PrintLines, self).connection_lost(exc)
sys.stdout.write('port closed')
def data_received(self, data):
"""Find data enclosed in START/STOP, call handle_packet"""
handel_Data = []
for byte in serial.iterbytes(data):
if byte == self.START:
self.in_packet = True
elif byte == self.STOP:
self.in_packet = False
handel_Data.append(self.handle_packet(bytes(self.packet))) # make read-only copy
del self.packet[:]
elif self.in_packet:
self.packet.extend(byte)
else:
self.handle_out_of_packet_data(byte)
return handel_Data
def handle_packet(self,packet):
Buffer_type = ''
translated_packet = packet.decode()
for index in translated_packet:
if index == 'C':
Buffer_type = 'CMD'
elif index == 'D':
Buffer_type = 'DATA'
if Buffer_type == 'CMD':
# 预留命令行接口
return translated_packet.split('a')[1].split('b')[0]
elif Buffer_type == 'DATA':
return int(translated_packet.split('a')[1].split('b')[0])
else:
return 'error'
raise NotImplementedError('please implement functionality in handle_packet')
2.然后我们再把协议放到通信的线程中去。
class ReadThread(ReaderThread):
def __init__(self,serial_instance,protocol_factory,Myqueue):
super(ReadThread,self).__init__(serial_instance = serial_instance,protocol_factory = protocol_factory)
self.temp = None
self.queue = Myqueue
self.flag = None
self.ReaderstateSingal = MySingnal()
def data_handler(self,handledata):
# 传输状况标志
cmdsign = None
for data0 in handledata:
if data0 == 'error':
self.flag = 'False'
elif isinstance(data0,int):
try:
self.queue.put(data0,block=False)
self.flag = 'True'
except:
self.flag = 'Lost_packet'
elif isinstance(data0,str):
self.flag = 'CMD'
cmdsign = data0
self.ReaderstateSingal.Tip_Sender(cmdsign,self.flag)
def run(self):
"""Reader loop"""
if not hasattr(self.serial, 'cancel_read'):
self.serial.timeout = 1
self.protocol = self.protocol_factory()
try:
self.protocol.connection_made(self)
except Exception as e:
self.alive = False
self.protocol.connection_lost(e)
self._connection_made.set()
return
error = None
self._connection_made.set()
while self.alive and self.serial.is_open:
try:
# read all that is there or wait for one byte (blocking)
data = self.serial.read(self.serial.in_waiting or 1)
except serial.SerialException as e:
# probably some I/O problem such as disconnected USB serial
# adapters -> exit
error = e
break
else:
if data:
# make a separated try-except for called used code
try:
self.temp = self.protocol.data_received(data)
self.data_handler(self.temp)
except Exception as e:
error = e
break
self.alive = False
self.protocol.connection_lost(error)
self.protocol = None
当时整的时候苦思冥想找不到测试之法,百度之后发现了一个好东西叫VSPD!嗯,用完大家都说好啊,创建虚拟串口,调试串口程序必备软件,免除了硬件问题啊,这个都可以下载的到,需要的大家亦可以私信我!
#################################################
if __name__ == '__main__':
from multiprocessing import Process
def write1(ki,k2):
global tempdata
k0 = None
for i in range(100):
k0 = '(Da' + str(i+i*0.001) +'b)'
z0 = bytes(k0,encoding= 'utf-8')
ki.write(z0)
time.sleep(0.05)
'''
if len(tempdata) <= 20:
tempdata.append(k2.temp)
else:
print(tempdata)
del tempdata[:]
'''
myqueue = Queue()
kuia = []
# 虚拟串口调试一下协议是否好使,COM2 - COM4换成你的那个串口号 ,一个写数据,一个读数据
# 这不是一个完整的测试代码,只是一个示例 具体大家自己修改修改,不能啥都嚼好了给你吃,自己试试。
ser0 = serial.serial_for_url('COM2', baudrate=115200, timeout=1)
ser1 = serial.serial_for_url('COM4', baudrate=115200, timeout=1)
t0 = ReadThread(ser0,PrintLines,myqueue)
t1 = ReadThread(ser1,PrintLines,myqueue)
kai = DataConduit(queue0= myqueue,mythread= t1)
t0.start()
t1.start()
kai.start()
write1(t0,t1)
# for i in range(100):
# print(myqueue.get(),'\t')
kai.pause()
t0.close()
t1.close()
t1.join(5)
t0.join(5)
# print(kai.list,len(kai.list),kai.is_alive(),t0.is_alive(),t1.is_alive())
kai.stop()
print(kai.is_alive())
del kai.list[:]
print(kai.list)