Не могу в потоке QThread передать или считать данные по COM порту на PYQT 5

Необходимо считать и записать данные по COM порту в потоке. Не принимает данные по COM порту в потоке. Не понимаю как передать сигнал метода потока правильно. Программирую недавно, буду рад любому совету.

# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QIODevice, QByteArray
from PyQt5.QtSerialPort import QSerialPort, QSerialPortInfo

# Класс потока где обьявляется счетчик и приниматются или передаются данные по COM порту
class MyThread(QtCore.QThread):
    #mysignal = QtCore.pyqtSignal(str)
    mysignal = QtCore.pyqtSignal(QByteArray, name="My_Signal")
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.serport = QSerialPort()
        self.serport.setBaudRate(9600)
        self.serport.setPortName("COM3")
        self.serport.setDataBits(8)
        self.serport.setParity(0)
        self.serport.setStopBits(1)
        print(self.serport.open(QIODevice.ReadWrite))
        print(self.serport.isReadable())
        self.running = False
        self.count = 0

# функция с телом потока
    def run(self):
        self.running = True
        while self.running:
            self.count += 1
            #self.mysignal.emit("count = %s" % self.count)
            buf = self.serport.readLine(14)
            if buf:
                self.mysignal.emit(buf)
            print(buf)
            self.sleep(1)


# Класс главного окна со счетчиком и привзкой потока через сигнал класса MyThread
class MyWindow(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        self.label = QtWidgets.QLabel("Нажми кнопку для запуска потока")
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.btnStart = QtWidgets.QPushButton("Запустить поток")
        self.btnStop = QtWidgets.QPushButton("Остановить поток")
        self.vbox = QtWidgets.QVBoxLayout()
        self.vbox.addWidget(self.label)
        self.vbox.addWidget(self.btnStart)
        self.vbox.addWidget(self.btnStop)
        self.setLayout(self.vbox)
        self.mythread = MyThread()
        self.btnStart.clicked.connect(self.on_start)
        self.btnStop.clicked.connect(self.on_stop)
        self.mythread.mysignal.connect(self.on_change, QtCore.Qt.QueuedConnection)

# Старт потока
    def on_start(self):
        if not self.mythread.isRunning():
            self.mythread.start()

# Останавливаем поток
    def on_stop(self):
        self.mythread.running = False

# Метод который получает данные через сигнал потока класса MyThread
    def on_change(self, s):
        self.label.setText(str(s))

# Метод который останавливает поток класса MyThread
    def closeEvent(self, event):
        self.hide()
        self.mythread.running = False
        self.mythread.wait(5000)
        event.accept()


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle("Запуск и остановка потока")
    window.resize(300,100)
    window.show()
    sys.exit(app.exec_())

Ответы (2 шт):

Автор решения: Alexander Chernin

Создавайте объект серийного порта в методе run, чтобы он работал в рабочем окружении (адресном пространстве) потока.

Общая схема такая:

class MyThread(QThread):  

    # булевский флаг активности/завершения работы потока
    _quit = False

    def run(self):
        serial = QSerialPort()
        # необходимые настройки идут либо тут, либо в цикле, ниже,
        # если вы будете  их менять в процессе работы потока и серийного порта.
        # В таком случае их надо постоянно проверять и пересоздавать порт, при необходимости

        # Проверяем флаг выхода/завершения работы потока
        while not self._quit:            
            if not serial.open(QIODevice.ReadWrite):
                print('Не могу открыть порт. Ошибка - 'serial.error())
                return

            # Передача данных
            if <есть данные на передачу>:
                # Следующие пара функций вызываются вместе последовательно
                # передаем данные в com-порт
                serial.write(requestData)
                # ожидаем пока все данные будут переданы
                serial.waitForBytesWritten(<Время для передачи данных>)

            # Получение данных
            # currentWaitTimeout - время ожидания, в течении 
            # которого данных должны прийти, иначе вернется False и мы пойдем дальше
            if serial.waitForReadyRead(currentWaitTimeout):
                # Считываем то, что пришло
                QByteArray responseData = serial.readAll()

    # Останавливаем поток
    def stop(self):
        self._quit = True

→ Ссылка
Автор решения: eri

В Qt уже есть основной цикл и слот что в порту появились данные.

class MyThreadedPort(QtCore.QObject):
    #mysignal = QtCore.pyqtSignal(str)
    mysignal = QtCore.pyqtSignal(QByteArray, name="My_Signal")
    def __init__(self, port="COM3", parent=None):
        QtCore.QObject.__init__(self) # отдельный тред делается без parent
        self.thread = QtCore.QThread()

        self.moveToThread(self.thread) # уводим слоты в тред

        self.serport = QSerialPort()
        self.serport.setBaudRate(9600)
        self.serport.setPortName(port)
        self.serport.setDataBits(8)
        self.serport.setParity(0)
        self.serport.setStopBits(1)

        self.thread.moveToThread(self.thread) # уводим порт в свой поток
        
        print(self.serport.open(QIODevice.ReadWrite))
        print(self.serport.isReadable())
        self.serport.readyRead.connect(self.readyread)
        self.running = False
        self.count = 0

    @QtCore.pyqtSlot()
    def stop(self):
        self.running = False # флаг ставится в треде - мутекс не нужен (QueuedConnection)
        self.serport.close() # достаточно закрыть порт
        
    @QtCore.pyqtSlot()
    def readyread(self):
        while self.running and self.serial.bytesAvailable() >= 14:
            self.count += 1
            buf = self.serport.readLine(14)
            self.mysignal.emit(buf)
            
    @QtCore.pyqtSlot('QByteArray')
    def write(self, data):
        self.serport.write(data)
        self.serport.flush()
    
→ Ссылка