从 PyQt GUI 连接到串口

Posted

技术标签:

【中文标题】从 PyQt GUI 连接到串口【英文标题】:Connect to serial from a PyQt GUI 【发布时间】:2019-03-08 20:21:51 【问题描述】:

我编写了一个程序来从串口发送和接收数据,但是我有一个问题,我想创建一个函数“connect()”或一个类,当我按下按钮时,该函数被执行,但是如果我在“MainWindow”类中创建这个函数,“TestThread”类中的变量“ser”变得未初始化,你能帮帮我吗?

import sys
import serial


from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.uic import loadUi


ser = serial.Serial('/dev/tty.usbmodem14201', 9600, timeout=1)

class TestThread(QThread):
    serialUpdate = pyqtSignal(str)
    def run(self):
        while ser.is_open:
            QThread.sleep(1)
            value = ser.readline().decode('ascii')
            self.serialUpdate.emit(value)
            ser.flush()

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        loadUi('/Users/bogdanvesa/P2A_GUI/mainwindow.ui', self)
        self.thread = TestThread(self)
        self.thread.serialUpdate.connect(self.handleSerialUpdate)

        self.connect_btn.clicked.connect(self.connectSer)
        self.lcd_EBtn.clicked.connect(self.startThread)

    def startThread(self):
        self.thread.start()

    def handleSerialUpdate(self, value):
        print(value)
        self.lcd_lineEdit.setText(value)


def main():

    app = QApplication(sys.argv)
    form = MainWindow()
    form.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

【问题讨论】:

@eyllanesc 好的,当我按下一个按钮时,我希望建立与 Arduino 的连接(打开串行),当我按下另一个按钮时关闭连接(关闭串行)但我不知道如何制作这个 【参考方案1】:

与其使用 pySerial + 线程,不如使用与 Qt 事件循环一起使用的 QSerialPort

from PyQt5 import QtCore, QtWidgets, QtSerialPort

class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.message_le = QtWidgets.QLineEdit()
        self.send_btn = QtWidgets.QPushButton(
            text="Send",
            clicked=self.send
        )
        self.output_te = QtWidgets.QTextEdit(readOnly=True)
        self.button = QtWidgets.QPushButton(
            text="Connect", 
            checkable=True,
            toggled=self.on_toggled
        )
        lay = QtWidgets.QVBoxLayout(self)
        hlay = QtWidgets.QHBoxLayout()
        hlay.addWidget(self.message_le)
        hlay.addWidget(self.send_btn)
        lay.addLayout(hlay)
        lay.addWidget(self.output_te)
        lay.addWidget(self.button)

        self.serial = QtSerialPort.QSerialPort(
            '/dev/tty.usbmodem14201',
            baudRate=QtSerialPort.QSerialPort.Baud9600,
            readyRead=self.receive
        )

    @QtCore.pyqtSlot()
    def receive(self):
        while self.serial.canReadLine():
            text = self.serial.readLine().data().decode()
            text = text.rstrip('\r\n')
            self.output_te.append(text)

    @QtCore.pyqtSlot()
    def send(self):
        self.serial.write(self.message_le.text().encode())

    @QtCore.pyqtSlot(bool)
    def on_toggled(self, checked):
        self.button.setText("Disconnect" if checked else "Connect")
        if checked:
            if not self.serial.isOpen():
                if not self.serial.open(QtCore.QIODevice.ReadWrite):
                    self.button.setChecked(False)
        else:
            self.serial.close()

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

【讨论】:

非常感谢,我将使用您的代码,但是为了我的安宁,可以从我的“主窗口”类中的按钮连接到串行吗?同时我的“TestThread”类运行良好..你能给我举个例子吗?【参考方案2】:

我使用了重新设计的上述代码,所以它有一个mainwindow、一个menubar和一个statusbar。我还添加了 QSerialPortInfo 类。此版本将找到活动端口并将其显示在状态栏上。

我只在 rpi 4 和 Windows 10 上对此进行了测试。

import sys

from PyQt5 import QtCore, QtWidgets, QtSerialPort 
from PyQt5.QtWidgets import QApplication, QMainWindow ,QWidget ,QToolBar ,QHBoxLayout, QAction ,QStatusBar ,QLineEdit ,QPushButton ,QTextEdit , QVBoxLayout 
from PyQt5.QtCore import Qt , pyqtSignal
from PyQt5.QtSerialPort import QSerialPortInfo

class AddComport(QMainWindow):
    porttnavn = pyqtSignal(str)

    def __init__(self, parent , menu):
        super().__init__(parent)

  
        menuComporte = menu.addMenu("Comporte")
    
        info_list = QSerialPortInfo()
        serial_list = info_list.availablePorts()
        serial_ports = [port.portName() for port in serial_list]
        if(len(serial_ports)> 0):
            antalporte = len(serial_ports)
            antal = 0
            while antal < antalporte:
                button_action = QAction(serial_ports[antal], self)
                txt = serial_ports[antal]
                portinfo = QSerialPortInfo(txt)
                buttoninfotxt = " Ingen informationer"
                if portinfo.hasProductIdentifier():
                    buttoninfotxt = ("Produkt specifikation = " + str(portinfo.vendorIdentifier()))
                if portinfo.hasVendorIdentifier():
                    buttoninfotxt =  buttoninfotxt + (" Fremstillers id = "+ str(portinfo.productIdentifier()))
                button_action = QAction( txt , self)
                button_action.setStatusTip( buttoninfotxt)
                button_action.triggered.connect(lambda checked, txt = txt: self.valgAfComportClick(txt))
                menuComporte.addAction(button_action)
                antal = antal +1
        else:
            print("Ingen com porte fundet")

    def valgAfComportClick(self , port):
        self.porttnavn.emit(port)
   
    def closeEvent(self, event):
        self.close()


class MainWindow(QMainWindow):  
    def __init__(self):
        super(MainWindow, self).__init__()

        portname = "None"
    
        self.setStatusBar(QStatusBar(self))
   
        menu = self.menuBar()
        comfinder = AddComport(self , menu)
        comfinder.porttnavn.connect(self.valgAfComport)

        self.setWindowTitle("Serial port display / send")
    
        self.message_le = QLineEdit()
        self.send_btn = QPushButton(
            text="Send",
            clicked=self.send
        )
    
        self.output_te = QTextEdit(readOnly=True)
        self.button = QPushButton(
            text="Connect", 
            checkable=True,
            toggled=self.on_toggled
        )
        lay = QVBoxLayout(self)
        hlay = QHBoxLayout()
        hlay.addWidget(self.message_le)
        hlay.addWidget(self.send_btn)
        lay.addLayout(hlay)
        lay.addWidget(self.output_te)
        lay.addWidget(self.button)
    
        widget = QWidget()
        widget.setLayout(lay)
        self.setCentralWidget(widget)

        self.serial = QtSerialPort.QSerialPort(
            portname,
            baudRate=QtSerialPort.QSerialPort.Baud9600,
            readyRead=self.receive)
   
           
    @QtCore.pyqtSlot()
    def receive(self):
        while self.serial.canReadLine():
            text = self.serial.readLine().data().decode()
            text = text.rstrip('\r\n')
            self.output_te.append(text)

    @QtCore.pyqtSlot()
    def send(self):
        self.serial.write(self.message_le.text().encode())

    @QtCore.pyqtSlot(bool)
    def on_toggled(self, checked):
        self.button.setText("Disconnect" if checked else "Connect")
        if checked:
            if not self.serial.isOpen():
                self.serial.open(QtCore.QIODevice.ReadWrite)
                if not self.serial.isOpen():
                    self.button.setChecked(False)
            else:
                self.button.setChecked(False)
        else:
            self.serial.close()
  
    def valgAfComport(self , nyport):
        seropen = False
        if self.serial.isOpen():
            seropen = True
            self.serial.close()   
        self.serial.setPortName(nyport)
        if seropen:
            self.serial.open(QtCore.QIODevice.ReadWrite)
            if not self.serial.isOpen():
                self.button.setChecked(False)
        
        print(nyport)
    
    def closeEvent(self, event):
        self.serial.close()
        print("Comport lukket")
        # print(comporttxt)
    
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())

【讨论】:

以上是关于从 PyQt GUI 连接到串口的主要内容,如果未能解决你的问题,请参考以下文章

pyQt5从dict创建按钮连接到具有附加值的函数

如何在 Python 中将函数连接到 PyQT5 GUI

PyQt5 无法连接到显示器

如何在从串口python pyqt接收的textbrowser pyqt5上附加串行数据[重复]

将点击事件连接到 pyqt 的功能

串口服务器的连接方式有哪几种?