Как отправлять данные через серийный порт не из главного потока приложения PySide6

При попытке отправить что угодно из класса MainWindow на серийный порт ошибки не возникает, однако, когда пытаешься отправить данные из второго потока, который должен работать в бесконечном цикле, программа ругается:

QObject::startTimer: Timers cannot be started from another thread

Попытался как-то вызвать функцию из главного класса во втором потоке, но что то не вышло Мне нужно, чтобы данные отправлялись и при этом программа не фризилась перед ожиданием следующей отправки данных

main.py:

# This Python file uses the following encoding: utf-8
import os
import sys
import wmi
import time
import json
from pathlib import Path

from arduino import serialSendDict, serialSendInt
from arduino import onOpen, getPorts, makeSelectedInt

from PySide6.QtGui import QGuiApplication
from PySide6.QtCore import QObject, Slot, Signal, QRunnable, QThreadPool
from PySide6.QtQml import QQmlApplicationEngine

...

class Worker(QRunnable):
    def __init__(self):
        super(Worker, self).__init__()

    @Slot()
    def run(self):
        global infinity
        global freq

        infinity = 'not the limit'

        while infinity == 'not the limit':
            time.sleep(freq)
            data = getData()
            if data is not None:
                serialSendDict(data)
                serialSendInt(['print'])


class MainWindow(QObject):
    def __init__(self):
        QObject.__init__(self)

        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(1)

    portsModelList = Signal(list)

    @Slot()
    def applyButton(self):
        if count > 0:
            serialSendInt(makeSelectedInt(selected, disk_list))
            worker = Worker()
            self.threadpool.start(worker)

    @Slot()
    def stopButton(self):
        global infinity

        infinity = 'limit'
        serialSendInt(['bye', '97'])

   ...


if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    main = MainWindow()

    engine.rootContext().setContextProperty('backend', main)
    engine.load(os.fspath(Path(__file__).resolve().parent / "qml/main.qml"))

    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec())

arduino.py

import time
from PySide6.QtSerialPort import QSerialPort, QSerialPortInfo
from PySide6.QtCore import QIODevice

serial = QSerialPort()
serial.setBaudRate(115200)
disk_list = []
selected = []


# Configuring the Arduino Port
def getPorts():
    portlist = []
    ports = QSerialPortInfo().availablePorts()
    for port in ports:
        portlist.append(port.portName())
    if len(portlist) == 0:
        portlist.append('None')
    return portlist


# Sending data to Arduino
def serialSendInt(data):
    if data[0] == 'info':
        data.pop(0)
        txs = '99,' + ','.join(map(str, data)) + ';'
        serial.write(txs.encode())
    elif data[0] == 'print':
        data.pop(0)
        txs = '98,' + 'print' + ';'
        serial.write(txs.encode())
    elif data[0] == 'bye':
        data.pop(0)
        txs = '97,' + 'bye' + ';'
        serial.write(txs.encode())
    elif data[0] == 'hello':
        data.pop(0)
        txs = '95,' + ','.join(map(str, data)) + ';'
        serial.write(txs.encode())


# Sending data to Arduino
def serialSendDict(data):
    ints = makeSelectedInt(selected, disk_list)
    ints.pop(0)
    for int in ints:
        val = takeWhatDoYouNeed(int, data)
        print(val)
        txs = int + ',' + ','.join(map(str, val)) + ';'
        print(txs + ' has been sended')
        serial.write(txs.encode())


...


if __name__ == "__main__":
    pass

На PyQt5 была функция qWait, который позволял не фризить интерфейс и ожидать время, однако на PySide я подобной функции не нашел


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

Автор решения: lovko

Спасибо eri за идею.

Было решено изменить класс с бесконечным циклом на следующий:

    class Thread(QThread):

    sendData = Signal(dict)
    sendPrint = Signal(list)

    def __init__(self):
        super(Thread, self).__init__()

    def __del__(self):
        self.wait()

    def run(self):
        global infinity
        global freq

        infinity = 'not the limit'

        while infinity == 'not the limit':
            time.sleep(freq)
            data = getData()
            if data is not None:
                self.sendData.emit(data)
                self.sendPrint.emit(['print'])

Теперь безо всяких проблем по нажатию кнопки Apply запускается поток, отправляет сигнал в главный поток, а он в свою очередь отправляет данные в последовательный порт:

    class MainWindow(QObject):
    def __init__(self):
        QObject.__init__(self)


    def sendDataToArduino(self, data):
        if type(data) == dict:
            serialSendDict(data)
        elif type(data) == list:
            serialSendInt(data)

    @Slot()
    def applyButton(self):
        if count > 0:
            serialSendInt(makeSelectedInt(selected, disk_list))
            self.thread = Thread()
            self.thread.sendData.connect(self.sendDataToArduino)
            self.thread.sendPrint.connect(self.sendDataToArduino)
            self.thread.start()
→ Ссылка