pyqtpyqt5qtserialport

how to let serial work well background and also foreground?


i have a serial app, The Robot class need to always receive serial message and then to do some thing, the Demo gui need to interactive Robot and get Robot.handle_readData, if no data get, need to get again and again until had data. Now i had no idea how to solve this problem, need someone give me good idea.

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtSerialPort import *

class Robot(QObject):
    def __init__(self):
        super().__init__()
        self.serial = QSerialPort()
        self.serial.setPortName('COM2')
        self.serial.setBaudRate(QSerialPort.Baud115200)
        self.serial.readyRead.connect(self.handle_readData)
        self.serial.open(QIODevice.ReadWrite)

    #background message auto process
    def handle_readData(self):
        data = self.serial.readAll()
        self.do_something(data)

        return data

    def do_something(self, data):pass

class Demo(QDialog):
    def __init__(self):
        super().__init__()
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.search_bn = QPushButton('query', clicked=self.get_query_info)
        layout.addStretch(1)
        layout.addWidget(self.search_bn)

        main_layout = QVBoxLayout()
        main_layout.addLayout(layout)
        main_layout.addWidget(QTextEdit('msg'))
        self.setLayout(main_layout)

        #robot object
        self.robot = Robot()

    #forground message process, pseudo code
    def get_query_info(self, checked):
        self.robot.serial.write('show version')
        #how to get Robot.handle_readData  data,
        data = Robot.handle_readData() # need to get this data
        #if data is None, need to get again and again until data have some meaningful msg.
        do_someting(data)


        self.robot.serial.write('show system info')
        # how to get Robot.handle_readData  data,
        data = Robot.handle_readData()  # need to get this data
        # if data is None, need to get again and again until data have some meaningful msg.
        do_someting1(data)


app = QApplication([])
demo = Demo()
demo.show()
app.exec()

Solution

  • Qt is designed to work asynchronously, and instead your possible implementation is made for synchronous logic. The solution for these cases is to transform the implementation and in this case for example with the help of flags.

    On the other hand do not process the information where you receive the information, instead it emits a signal so that it can be used elsewhere.

    from enum import Enum, auto
    
    
    from PyQt5.QtCore import pyqtSignal, pyqtSlot, QIODevice, QObject
    from PyQt5.QtWidgets import (
        QApplication,
        QDialog,
        QHBoxLayout,
        QPushButton,
        QVBoxLayout,
        QTextEdit,
    )
    from PyQt5.QtSerialPort import QSerialPort
    
    
    class Robot(QObject):
        dataChanged = pyqtSignal(bytes)
    
        def __init__(self):
            super().__init__()
            self.serial = QSerialPort()
            self.serial.setPortName("COM2")
            self.serial.setBaudRate(QSerialPort.Baud115200)
            self.serial.readyRead.connect(self.handle_readData)
            self.serial.open(QIODevice.ReadWrite)
    
            self.dataChanged.connect(self.do_something)
    
        @pyqtSlot()
        def handle_readData(self):
            data = self.serial.readAll()
            self.dataChanged.emit(data)
    
        @pyqtSlot(bytes)
        def do_something(self, data):
            pass
    
    
    class State(Enum):
        NoneState = auto()
        VersionState = auto()
        InfoState = auto()
    
    
    class Demo(QDialog):
        def __init__(self):
            super().__init__()
            layout = QHBoxLayout()
            layout.setContentsMargins(0, 0, 0, 0)
            self.search_bn = QPushButton("query", clicked=self.get_query_info)
            layout.addStretch(1)
            layout.addWidget(self.search_bn)
    
            main_layout = QVBoxLayout(self)
            main_layout.addLayout(layout)
            main_layout.addWidget(QTextEdit("msg"))
    
            self.current_state = State.NoneState
    
            self.robot = Robot()
            self.robot.dataChanged.connect(self.process_data)
    
        @pyqtSlot()
        def get_query_info(self):
            self.robot.serial.write(b"show version")
            self.current_state = State.VersionState
    
        @pyqtSlot(bytes)
        def process_data(self, data):
            if self.current_state == State.VersionState:
                do_someting(data)
                self.robot.serial.write(b"show system info")
                self.current_state = State.InfoState
            elif self.current_state == State.InfoState:
                do_someting1(data)
                self.current_state = State.NoneState
    
    
    if __name__ == "__main__":
        import sys
    
        app = QApplication(sys.argv)
        demo = Demo()
        demo.show()
        sys.exit(app.exec_())