[pyqt5+pyserial]python实现modbus串口通信

本文通过PyQt5构建GUI界面,并结合pyserial库,详细介绍了如何使用Python实现Modbus串口通信。同时,利用QThread确保UI线程不被阻塞,保证了用户体验。Func.py文件中包含了具体的Modbus交互指令和CRC校验功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Test.py实现GUI界面和大部分逻辑 

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

from PyQt5.QtCore import QDateTime, Qt, QTimer, pyqtSignal, QThread
from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QDateTimeEdit,
        QDial, QDialog, QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit,
        QProgressBar, QPushButton, QRadioButton, QScrollBar, QSizePolicy,
        QSlider, QSpinBox, QStyleFactory, QTableWidget, QTabWidget, QTextEdit,
        QVBoxLayout, QWidget, QMessageBox)
from PyQt5.QtGui import (QIntValidator)
import serial
import serial.tools.list_ports
import crcmod.predefined
import Func
import time
import threading

class WidgetGallery(QDialog):

    # global dict1
    # dict1 = {'SUM': '0000'}
    sig = pyqtSignal(str, str, float, str, str, str)

    def __init__(self, parent=None):
        super(WidgetGallery, self).__init__(parent)
        self.originalPalette = QApplication.palette()

        self.comComboBox = QComboBox()
        # print(self.getPort())


        # self.comComboBox.currentIndexChanged.connect(self.selectionchange)
        # print(self.comComboBox.currentText())
        self.pasg = '1'  # 默认通道为1,不然初始化bug
        self.serFlag = False
        comLabel = QLabel("串口:")
        comLabel.setBuddy(self.comComboBox)


        self.bpsComboBox = QComboBox()
        self.bpsComboBox.addItems(self.getBps())

        bpsLabel = QLabel("波特率:")
        bpsLabel.setBuddy(self.bpsComboBox)

        self.crcComboBox = QComboBox()
        self.crcComboBox.addItems(self.getCrc())

        crcLabel = QLabel("校验:")
        crcLabel.setBuddy(self.crcComboBox)

        self.openButton = QPushButton("打开串口")
        self.openButton.setDefault(False)
        self.openButton.clicked.connect(self.openSer)
        self.openButton.setCheckable(True)
        # self.openButton.setAutoExclusive(True)
        # self.openButton.setEnabled(False)
        # print(self.getPort())
        self.comComboBox.addItems(self.getPort())


        # self.open1Button = QPushButton("go03")
        # self.open1Button.setDefault(False)
        # self.staNum = '01'
        # self.funCode = '03'
        # self.keyName = 'SUM'
        # self.open1Button.clicked.connect(self.sendFunction03(staNum, funCode, keyName))
        # self.open1Button.clicked.connect(self.Print)
        # self.open1Button.clicked.connect(self.sendFunction03)

        self.setWindowFlags(Qt.Widget)
        self.setWindowTitle("ST05D校准软件")
        self.resize(600, 400)
        self.setFixedSize(600, 400)
        self.changeStyle('windowsvista')

        topLayout = QHBoxLayout()
        topLayout.addWidget(comLabel)
        topLayout.addWidget(self.comComboBox)
        topLayout.addWidget(bpsLabel)
        topLayout.addWidget(self.bpsComboBox)
        topLayout.addWidget(crcLabel)
        topLayout.addWidget(self.crcComboBox)
        topLayout.addSpacing(50)
        topLayout.addWidget(self.openButton)
        # topLayout.addWidget(self.open1Button)
        topLayout.addStretch(1)

        self.createTopLeftGroupBox()
        self.createTopRightGroupBox()
        self.createMiddleLeftGroupBox()
        self.createMiddleRightGroupBox()
        self.createBottomLeftGroupBox()
        self.createBottomRightGroupBox()

        mainLayout = QGridLayout()
        mainLayout.addLayout(topLayout, 0, 0, 1, 2)
        mainLayout.addWidget(self.topLeftGroupBox, 1, 0)
        mainLayout.addWidget(self.topRightGroupBox, 1, 1)
        mainLayout.addWidget(self.middleLeftGroupBox, 2, 0)
        mainLayout.addWidget(self.middleRightGroupBox, 2, 1)
        mainLayout.addWidget(self.bottomLeftGroupBox, 3, 0)
        mainLayout.addWidget(self.bottomRightGroupBox, 3, 1)
        mainLayout.setColumnStretch(0, 1)
        mainLayout.setColumnStretch(1, 1)

        self.setLayout(mainLayout)


    # def Print(self):
    #     print('print')


    def closeEvent(self, event):
        # self.c.terminate()
        print('thread=sta', self.readthread._running)
        self.readthread.terminate()
        print('close')

    def openSer(self):
        print('open')
        print(self.openButton.isChecked())
        if self.openButton.isChecked():
            self.ser = serial.Serial(self.comComboBox.currentText(), self.bpsComboBox.currentText(), timeout=0)
            self.comComboBox.setEnabled(False)
            self.bpsComboBox.setEnabled(False)
            self.crcComboBox.setEnabled(False)
            self.openButton.setText('关闭串口')
        else:
            print('no')
            self.ser.close()
            self.openButton.setText('打开串口')
            self.comComboBox.setEnabled(True)
            self.bpsComboBox.setEnabled(True)
            self.crcComboBox.setEnabled(True)
        # if not self.serFlag:
        #     self.ser = serial.Serial(self.comComboBox.currentText(), self.bpsComboBox.currentText(), timeout=0)
        #     self.serFlag = True
        #     print(self.comComboBox.currentText(), self.bpsComboBox.currentText())
        # else:
        #     self.ser.close()
        #     self.serFlag = False
        #     self.openSer()

    # def sendFunction03(self, staNum, funCode, keyName):
    def send03(self, keyName):
        try:
            staNum = self.staNum
            # funCode = self.funCode
            # keyName = self.keyName
        except Exception:
            staNum = '00'
        # keyName = self.keyName
        funCode = '03'
        print('033')
        if self.openButton.isChecked():
            list1 = Func.sendFunction03(staNum, funCode, keyName)
            # list1 = []
            # dict1 = Func.addressDict03
            # list1.append(int('0x' + staNum, 16))
            # list1.append(int('0x' + funCode, 16))
            # list1.append(int('0x' + dict1[keyName][0:2], 16))
            # list1.append(int('0x' + dict1[keyName][2:4], 16))
            # list1.append(int('0x00', 16))
            # list1.append(int('0x02', 16))
            # crc16_func = crcmod.predefined.mkCrcFun('modbus')
            # # ba = bytearray([0o01, 0o03, 0o00, 0o00, 0o00, 0o02])
            # a = bytes().fromhex(staNum + funCode + dict1[keyName] + '0002')
            # # print(a)
            # reslist = hex(crc16_func(a))[2:].zfill(4)
            # # print(type(reslist[2:].upper()), reslist[:2].upper())
            # list1.append(int('0x' + reslist[2:], 16))
            # list1.append(int('0x' + reslist[:2], 16))
            print(list1)
            try:
                self.ser.write(list1)
            except Exception:
                print('not ok')
                reply = QMessageBox.warning(self,
                                            "串口设置错误",
                                            "请检查串口号及波特率设置",
                                            QMessageBox.Yes | QMessageBox.No)
                # self.echo(reply)
            else:
                print(type(self.ser.read()))
                result16 = str(self.ser.read(), encoding="utf8")[6:14]  # 去掉从站号功能码crc校验码等,只保留读取值
                print(result16)
                if len(result16) == 0:
                    reply = QMessageBox.warning(self,
                                                "地址读取空",
                                                "请检查串口设置",
                                                QMessageBox.Yes | QMessageBox.No)
                elif keyName == 'DN':  # 获取从站地址
                    # result10 = int('0x' + result16[2:], 16)  # 先实现只读后两位
                    result10 = Func.hex8ToInt(result16)
                    self.AdrlineEdit.setText(result10)
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值