1. 程式人生 > >Python 串列埠通訊 GUI 開發

Python 串列埠通訊 GUI 開發

在專案中遇到樹莓派串列埠通訊問題。由於本人一直從事.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

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執行緒處理類,通過訊號量進行傳遞。

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 按照pyserial模組

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

  

總之,通過艱難的尋找,基本完成了GUI串列埠通訊Demo ,後續我會將Demo 上傳到Git 上。

非常感謝園友部落格的支援。。再次感謝。

 

 

快下班了,後續詳細解釋再行完善。。。。