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

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

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)
      
以下是使用Python3、PyQT5Pyserial实现串口工具的方法: 1. 安装PyserialPyQT5库 在终端或命令行中输入以下命令来安装PyserialPyQT5: ``` pip install pyserial pip install pyqt5 ``` 2. 创建GUI界面 使用PyQT5创建一个简单的GUI界面。在这个界面中,你需要添加一个下拉菜单来选择串口、一个文本框来显示串口数据和一些按钮来打开/关闭串口和发送数据。 下面是一个简单的PyQT5串口工具界面: ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QTextEdit, QPushButton, QHBoxLayout, QVBoxLayout, QWidget class SerialTool(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): # 设置窗口大小 self.setGeometry(100, 100, 400, 300) # 设置窗口标题 self.setWindowTitle('Serial Tool') # 添加下拉菜单 self.portComboBox = QComboBox(self) self.portComboBox.move(10, 10) self.portComboBox.resize(180, 30) # 添加文本框 self.textEdit = QTextEdit(self) self.textEdit.move(10, 50) self.textEdit.resize(380, 180) # 添加按钮 self.openButton = QPushButton('Open', self) self.openButton.move(10, 240) self.openButton.resize(80, 30) self.closeButton = QPushButton('Close', self) self.closeButton.move(100, 240) self.closeButton.resize(80, 30) self.sendButton = QPushButton('Send', self) self.sendButton.move(190, 240) self.sendButton.resize(80, 30) self.clearButton = QPushButton('Clear', self) self.clearButton.move(280, 240) self.clearButton.resize(80, 30) # 添加布局管理器 hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.openButton) hbox.addWidget(self.closeButton) hbox.addWidget(self.sendButton) hbox.addWidget(self.clearButton) vbox = QVBoxLayout() vbox.addWidget(self.portComboBox) vbox.addLayout(hbox) widget = QWidget() widget.setLayout(vbox) self.setCentralWidget(widget) ``` 3. 实现串口操作 使用Pyserial库来创建串口连接。在打开串口之前,你需要获取可用的串口列表并将它们添加到下拉菜单中。在发送数据之前,你需要从文本框中获取数据。 下面是一个简单的Pyserial串口操作代码: ```python import serial class SerialTool(QMainWindow): def __init__(self): super().__init__() self.initUI() self.initSerial() def initSerial(self): # 获取可用串口列表 ports = serial.tools.list_ports.comports() # 将可用串口添加到下拉菜单中 for port in ports: self.portComboBox.addItem(port.device) # 创建串口连接 self.ser = serial.Serial() def openSerial(self): # 设置串口参数 port = self.portComboBox.currentText() baudrate = 9600 bytesize = serial.EIGHTBITS parity = serial.PARITY_NONE stopbits = serial.STOPBITS_ONE self.ser.port = port self.ser.baudrate = baudrate self.ser.bytesize = bytesize self.ser.parity = parity self.ser.stopbits = stopbits # 打开串口连接 try: self.ser.open() self.ser.flushInput() self.ser.flushOutput() self.textEdit.append('Serial port is open.') except Exception as e: self.textEdit.append(str(e)) def closeSerial(self): # 关闭串口连接 try: self.ser.close() self.textEdit.append('Serial port is closed.') except Exception as e: self.textEdit.append(str(e)) def sendSerial(self): # 发送数据 data = self.textEdit.toPlainText() try: self.ser.write(data.encode()) self.textEdit.clear() self.textEdit.append('Data is sent: ' + data) except Exception as e: self.textEdit.append(str(e)) ``` 4. 运行程序 将GUI界面和串口操作代码组合起来,并在主函数中运行程序。 下面是一个完整的PyQT5串口工具代码: ```python import sys import serial from PyQt5.QtWidgets import QApplication, QMainWindow, QComboBox, QTextEdit, QPushButton, QHBoxLayout, QVBoxLayout, QWidget class SerialTool(QMainWindow): def __init__(self): super().__init__() self.initUI() self.initSerial() def initUI(self): # 设置窗口大小 self.setGeometry(100, 100, 400, 300) # 设置窗口标题 self.setWindowTitle('Serial Tool') # 添加下拉菜单 self.portComboBox = QComboBox(self) self.portComboBox.move(10, 10) self.portComboBox.resize(180, 30) # 添加文本框 self.textEdit = QTextEdit(self) self.textEdit.move(10, 50) self.textEdit.resize(380, 180) # 添加按钮 self.openButton = QPushButton('Open', self) self.openButton.move(10, 240) self.openButton.resize(80, 30) self.closeButton = QPushButton('Close', self) self.closeButton.move(100, 240) self.closeButton.resize(80, 30) self.sendButton = QPushButton('Send', self) self.sendButton.move(190, 240) self.sendButton.resize(80, 30) self.clearButton = QPushButton('Clear', self) self.clearButton.move(280, 240) self.clearButton.resize(80, 30) # 添加布局管理器 hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(self.openButton) hbox.addWidget(self.closeButton) hbox.addWidget(self.sendButton) hbox.addWidget(self.clearButton) vbox = QVBoxLayout() vbox.addWidget(self.portComboBox) vbox.addLayout(hbox) widget = QWidget() widget.setLayout(vbox) self.setCentralWidget(widget) # 绑定按钮事件 self.openButton.clicked.connect(self.openSerial) self.closeButton.clicked.connect(self.closeSerial) self.sendButton.clicked.connect(self.sendSerial) self.clearButton.clicked.connect(self.textEdit.clear) def initSerial(self): # 获取可用串口列表 ports = serial.tools.list_ports.comports() # 将可用串口添加到下拉菜单中 for port in ports: self.portComboBox.addItem(port.device) # 创建串口连接 self.ser = serial.Serial() def openSerial(self): # 设置串口参数 port = self.portComboBox.currentText() baudrate = 9600 bytesize = serial.EIGHTBITS parity = serial.PARITY_NONE stopbits = serial.STOPBITS_ONE self.ser.port = port self.ser.baudrate = baudrate self.ser.bytesize = bytesize self.ser.parity = parity self.ser.stopbits = stopbits # 打开串口连接 try: self.ser.open() self.ser.flushInput() self.ser.flushOutput() self.textEdit.append('Serial port is open.') except Exception as e: self.textEdit.append(str(e)) def closeSerial(self): # 关闭串口连接 try: self.ser.close() self.textEdit.append('Serial port is closed.') except Exception as e: self.textEdit.append(str(e)) def sendSerial(self): # 发送数据 data = self.textEdit.toPlainText() try: self.ser.write(data.encode()) self.textEdit.clear() self.textEdit.append('Data is sent: ' + data) except Exception as e: self.textEdit.append(str(e)) if __name__ == '__main__': app = QApplication(sys.argv) serialTool = SerialTool() serialTool.show() sys.exit(app.exec_()) ``` 运行程序后,你应该可以打开串口并发送数据了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值