在項目中遇到樹莓派串口通信問題。由於本人一直從事.net 開發,希望將樹莓派系統換成Win10 IOT版。但是在測試過程中出現無法找到串口的問題。最終也沒有解決。最終按照領導要求,linux (了解不多)比較穩定。所以硬着頭皮重拾了python(之前學習過簡單的語法),剛開始做成了控制台,配置比較麻煩最終通過Qt改成了桌面版的,至於能不能在linux上運行,還沒做測試。。廢話不多少了,進入正題。。。。
1、系統界面如圖所示。

首先,對於python基礎都不太熟悉,對GUI更不用說了。通過在園子中查找查找,將Qt Designer 設計的圖像話界面轉換成.py文件。這個過程比較艱辛,可以通過其他童鞋的博客進行配置。(查找太多了也沒有將地址copy下來。非常感謝園友的博客)
1、轉換的ui.py代碼。
其中包括布局代碼已經按鈕事件處理邏輯部分
(不會使用pycharm,將轉換的文件保存,使用vscode打開)
在QT中通過clicked的connect綁定事件處理函數。如下所示。
self.pushButton.clicked.connect(self.conectSerial)#綁定click 事件
對於列表,剛開始選擇listView,主要在使用過程中不知怎么動態綁定內容,遂改成了listWidget,可以像C#語言中的List一樣,通過InsertItem或者AddItem進行添加。
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'uitest.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
import serialportHelper
import time
import threading
import treadtest
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(417, 357)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(20, 0, 61, 31))
self.label.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label.setAutoFillBackground(True)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setWordWrap(True)
self.label.setObjectName("label")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setGeometry(QtCore.QRect(80, 10, 131, 22))
self.comboBox.setObjectName("comboBox")
portList=serialportHelper.serialPortslist()
for i in range(0,len(portList)):
self.comboBox.addItem(portList[i].device)
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(20, 30, 61, 31))
self.label_2.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_2.setAutoFillBackground(True)
self.label_2.setAlignment(QtCore.Qt.AlignCenter)
self.label_2.setWordWrap(True)
self.label_2.setObjectName("label_2")
self.comboBox_2 = QtWidgets.QComboBox(self.centralwidget)
self.comboBox_2.setGeometry(QtCore.QRect(80, 40, 131, 22))
self.comboBox_2.setObjectName("comboBox_2")
self.comboBox_2.addItem("")
self.comboBox_2.addItem("")
self.comboBox_2.addItem("")
self.comboBox_2.addItem("")
self.comboBox_2.addItem("")
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(20, 60, 61, 31))
self.label_3.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_3.setAutoFillBackground(True)
self.label_3.setAlignment(QtCore.Qt.AlignCenter)
self.label_3.setWordWrap(True)
self.label_3.setObjectName("label_3")
self.comboBox_3 = QtWidgets.QComboBox(self.centralwidget)
self.comboBox_3.setGeometry(QtCore.QRect(80, 70, 131, 22))
self.comboBox_3.setObjectName("comboBox_3")
self.comboBox_3.addItem("")
self.comboBox_3.addItem("")
self.comboBox_3.addItem("")
self.label_4 = QtWidgets.QLabel(self.centralwidget)
self.label_4.setGeometry(QtCore.QRect(20, 90, 61, 31))
self.label_4.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_4.setAutoFillBackground(True)
self.label_4.setAlignment(QtCore.Qt.AlignCenter)
self.label_4.setWordWrap(True)
self.label_4.setObjectName("label_4")
self.comboBox_4 = QtWidgets.QComboBox(self.centralwidget)
self.comboBox_4.setGeometry(QtCore.QRect(80, 100, 131, 22))
self.comboBox_4.setObjectName("comboBox_4")
self.comboBox_4.addItem("")
self.comboBox_4.addItem("")
self.comboBox_4.addItem("")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(260, 80, 75, 23))
self.pushButton.setIconSize(QtCore.QSize(20, 20))
self.pushButton.setDefault(False)
self.pushButton.setObjectName("pushButton")
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setGeometry(QtCore.QRect(260, 120, 75, 23))
self.pushButton_2.setIconSize(QtCore.QSize(20, 20))
self.pushButton_2.setDefault(False)
self.pushButton_2.setObjectName("pushButton_2")
self.listWidget = QtWidgets.QListWidget(self.centralwidget)
self.listWidget.setGeometry(QtCore.QRect(10, 160, 391, 192))
self.listWidget.setObjectName("listWidget")
self.comboBox_5 = QtWidgets.QComboBox(self.centralwidget)
self.comboBox_5.setGeometry(QtCore.QRect(80, 130, 131, 22))
self.comboBox_5.setObjectName("comboBox_5")
self.comboBox_5.addItem("")
self.label_5 = QtWidgets.QLabel(self.centralwidget)
self.label_5.setGeometry(QtCore.QRect(20, 120, 61, 31))
self.label_5.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_5.setAutoFillBackground(True)
self.label_5.setAlignment(QtCore.Qt.AlignCenter)
self.label_5.setWordWrap(True)
self.label_5.setObjectName("label_5")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "串口通信"))
self.label.setText(_translate("MainWindow", "串口"))
self.label_2.setText(_translate("MainWindow", "波特率"))
self.comboBox_2.setItemText(0, _translate("MainWindow", "1200"))
self.comboBox_2.setItemText(1, _translate("MainWindow", "2400"))
self.comboBox_2.setItemText(2, _translate("MainWindow", "9600"))
self.comboBox_2.setItemText(3, _translate("MainWindow", "19200"))
self.comboBox_2.setItemText(4, _translate("MainWindow", "115200"))
self.label_3.setText(_translate("MainWindow", "校驗位"))
self.comboBox_3.setItemText(0, _translate("MainWindow", "N"))
self.comboBox_3.setItemText(1, _translate("MainWindow", "O"))
self.comboBox_3.setItemText(2, _translate("MainWindow", "E"))
self.label_4.setText(_translate("MainWindow", "數據位"))
self.comboBox_4.setItemText(0, _translate("MainWindow", "8"))
self.comboBox_4.setItemText(1, _translate("MainWindow", "7"))
self.comboBox_4.setItemText(2, _translate("MainWindow", "6"))
self.pushButton.setText(_translate("MainWindow", "打開"))
self.pushButton_2.setText(_translate("MainWindow", "關閉"))
self.comboBox_5.setItemText(0, _translate("MainWindow", "1"))
self.label_5.setText(_translate("MainWindow", "停止位"))
self.pushButton.clicked.connect(self.conectSerial)
self.pushButton_2.clicked.connect(self.closeSerial)
self.pushButton_2.setEnabled(False)
global ser
flag=False
ser =None
def conectSerial(self):
try:
self.openSerial()
except:
if self.pushButton.isEnabled==False:
self.pushButton.setEnabled(True)
self.pushButton_2.setEnabled(False)
if ser!=None and ser.is_open==True:
self.closeSerial()
pass
def closeSerial(self):
try:
if ser!=None and ser.is_open==True:
global flag
flag=False
serialportHelper.ClosePort(ser)
self.listWidget.insertItem(0,"串口已關閉 {}".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
self.pushButton.setEnabled(True)
self.pushButton_2.setEnabled(False)
self.setEnable(True)
except Exception as e:
self.listWidget.insertItem(0,e)
pass
def openSerial(self):
try:
portname=self.comboBox.currentText()
baudrate=self.comboBox_2.currentText()
parity=self.comboBox_3.currentText()
bytesize=self.comboBox_4.currentText()
stopbits=self.comboBox_5.currentText()
global ser
ser,ret=serialportHelper.OpenPort(portname,baudrate,parity,int(bytesize),int(stopbits),100)
if ret==True:
global flag
flag=True
self.listWidget.insertItem(0,'串口已打開 {}'.format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())))
self.pushButton.setEnabled(False)
self.pushButton_2.setEnabled(True)
# thread=threading.Thread(target=self.readdata)
# self.thread.
# thread.setDaemon(True)
# thread.start()
self.thread=treadtest.MyThread(ser)
self.thread._single.connect(self.chuli)
self.thread.start()
self.setEnable(False)
except Exception as e:
self.listWidget.insertItem(0,e)
def chuli(self,s):
self.listWidget.insertItem(0,s)
def setEnable(self,flag):
self.comboBox.setEnabled(flag)
self.comboBox_2.setEnabled(flag)
self.comboBox_3.setEnabled(flag)
self.comboBox_4.setEnabled(flag)
self.comboBox_5.setEnabled(flag)
2、新增一個aa.py文件,手動添加如下代碼作為窗體程序運行的入口。
import sys
from PyQt5.QtWidgets import QApplication , QMainWindow
from ui import *
class MyMainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent)
self.setupUi(self)
if __name__=="__main__":
# 每一pyqt5應用程序必須創建一個應用程序對象。sys.argv參數是一個列表,從命令行輸入參數。
app = QApplication(sys.argv)
myWin = MyMainWindow()
# 顯示在屏幕上
myWin.show()
# 系統exit()方法確保應用程序干凈的退出
# 的exec_()方法有下划線。因為執行是一個Python關鍵詞。因此,exec_()代替
sys.exit(app.exec_())
3、串口操作模塊 serialportHelper.py
通過pyserial 模塊進行處理
import msvcrt
import serial
import serial.tools.list_ports
def OpenPort(comName,bps,parity,size,stopbits,timeout):
try:
ser =serial.Serial(comName,bps,bytesize=size,parity=parity,stopbits=stopbits,timeout=timeout)
if (ser.is_open):
ret =True
except Exception as e:
print("---異常---:", e)
ser=None
ret=False
return ser,ret
def DWritePort(ser,text):
result = ser.write(text.encode("gbk")) # 寫數據
return result
def ClosePort(ser):
ser.close()
def serialPortslist():
port_list=list(serial.tools.list_ports.comports())
if len(port_list)==0:
print("沒有端口")
return None
else:
for i in range(0,len(port_list)):
print(port_list[i])
return port_list
4、通過多線程進行串口數據接收
由於在處理過程中將處理結果進行顯示在主線程UI中,進過艱難查找,需要繼承QThread線程處理類,通過信號量進行傳遞。(該過程類似於C#中的delegate委托)
from PyQt5.QtCore import *
import time
import services
class MyThread(QThread):
_single=pyqtSignal(str)
def __init__(self,ser):
super(MyThread,self).__init__()
self.ser=ser
print(ser)
def run(self):
try:
while self.ser!=None and self.ser.is_open==True:
try:
# for i in range(10):
# self._single.emit('串口接收的值:%s' % i)
# time.sleep(0.1)
count=self.ser.in_waiting
if count>0:
time.sleep(1)
str1 = self.ser.read(self.ser.in_waiting).decode("gbk")
print(str1)
self._single.emit('串口接收的值:'+str1.replace('\r\n',''))
user_url="http://172.16.0.101:8088/SAPFRCEX.asmx?wsdl"
client1=services.ClientObj(user_url)
result=client1.service.getBOM(str('J010190968'))
if result!=None:
#print("測試結果:",result)
get_list=services.GetArrayOfStringValue(result,'diffgram')
get_list1=services.GetArrayOfStringValue(get_list[0],'DocumentElement')
get_list2=services.GetArrayOfStringValue(get_list1[0],'dt')
for i in range(0,len(get_list2)):
self._single.emit('webservice返回值:%s' % get_list2[i][3][0])
else:
self._single.emit("webservice 無返回信息")
except Exception as e:
print(e)
self._single.emit(e)
pass
except:
pass
5、由於串口接收的數據需要進行上傳webservice
創建一個services.py文件。通過使用pip install 按照suds模塊,按照完成有異常,最后查找,按照suds-jurko 問題解決。
import suds
from suds.client import Client,sudsobject
from array import array
def GetArrayOfStringValue(array,info):
'''處理webservice返回的array of string,並獲取返回值列表'''
getarray = array
getdict = sudsobject.asdict(getarray)
getlist = getdict.get('%s'%info)
return getlist
def ClientObj(url):
user_url="http://xxxx:xxxx/SAPFRCEX.asmx?wsdl"
client1=Client(user_url)
return client1
6、linux 下打包
在vscode 終端下,通過輸入 pyinstaller -F aa.py 則在文件根目錄文件下生成Linux下aa的可執行文件。截圖和第一張截圖一致。
總之,通過艱難的尋找,基本完成了GUI串口通信Demo ,后續我會將Demo 上傳到Git 上。
非常感謝園友博客的支持。。再次感謝。
