【问题标题】:Running an asyncio loop in a separate thread, Signals from, and to loop在单独的线程中运行异步循环,信号来自和循环
【发布时间】:2021-03-16 15:00:35
【问题描述】:

我正在尝试制作一个在后台与多个 BLE 设备进行通信的 UI。 为此,我实现了一个运行 asyncio.loop 的单独线程。这是必要的,因为我使用 bleak 0.9.1 连接到设备。

使用信号和槽将数据从 UI 线程获取到工作线程可以正常工作。但是,它在另一个方向上不起作用。据我所知,这是因为线程忙于运行循环并且永远不会停止这样做。因此,它无法处理来自 UI 线程的输入。

下面有一个显示问题的示例代码。

有什么方法可以在运行 asyncio 循环的同时处理线程中的输入槽?

import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QVBoxLayout
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
import asyncio

class Test_Thread(QObject):
    signal_back = pyqtSignal(int)

    def __init__(self,
                 loop: asyncio.AbstractEventLoop,
                 parent=None):
        super(Test_Thread, self).__init__(parent)
        self.text = "Task1 not configured"
        self.loop = loop
        self.counter = 0

    @pyqtSlot(str)
    def set_text_slot(self, txt):
        self.text = txt

    async def do_stuff1(self):
        while True:
            print(self.text)
            await asyncio.sleep(2.0)

    async def do_stuff2(self):
        while True:
            self.counter += 1
            self.signal_back.emit(self.counter)
            await asyncio.sleep(1.0)

    def work(self):
        #run the event loop
        try:
            asyncio.ensure_future(self.do_stuff1(), loop=self.loop)
            asyncio.ensure_future(self.do_stuff2(), loop=self.loop)
            self.loop.run_forever()
        finally:
            print("Disconnect...")


class Window(QWidget):

    set_text_signal = pyqtSignal(str)

    def __init__(self, parent=None):
        super(Window, self).__init__()
        self.initUi()
        self.startThread()

    def initUi(self):
        layout = QVBoxLayout()
        self.button = QPushButton('User input')
        self.button.clicked.connect(self.sendtotask)
        layout.addWidget(self.button)
        self.setLayout(layout)
        self.show()

    def startThread(self):
        loop = asyncio.get_event_loop()
        self.asyciothread = Test_Thread(loop)
        self.thread = QThread()
        self.asyciothread.moveToThread(self.thread)

        self.set_text_signal.connect(self.asyciothread.set_text_slot)
        self.asyciothread.signal_back.connect(self.receivefromthread)
        self.thread.started.connect(self.asyciothread.work)

        self.thread.start()

    @pyqtSlot(int)
    def receivefromthread(self, number):
        print(str(number))

    def sendtotask(self):
        self.set_text_signal.emit("Task: Configured")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    ui = Window()
    ui.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python multithreading pyqt pyqt5 python-asyncio


    【解决方案1】:

    没有必要使用线程来使用 Qt 的异步,因为有像 asyncqtqasync 这样的库可以启用它:

    import asyncio
    import sys
    
    from PyQt5.QtWidgets import QWidget, QPushButton, QApplication, QVBoxLayout
    from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
    
    from asyncqt import QEventLoop
    # from qasync import QEventLoop
    
    
    class Worker(QObject):
        signal_back = pyqtSignal(int)
    
        def __init__(self, loop: asyncio.AbstractEventLoop, parent=None):
            super(Worker, self).__init__(parent)
            self.text = "Task1 not configured"
            self.loop = loop
            self.counter = 0
    
        @pyqtSlot(str)
        def set_text_slot(self, txt):
            self.text = txt
    
        async def do_stuff1(self):
            while True:
                print(self.text)
                await asyncio.sleep(2.0)
    
        async def do_stuff2(self):
            while True:
                self.counter += 1
                self.signal_back.emit(self.counter)
                await asyncio.sleep(1.0)
    
        def work(self):
            asyncio.ensure_future(self.do_stuff1(), loop=self.loop)
            asyncio.ensure_future(self.do_stuff2(), loop=self.loop)
    
    
    class Window(QWidget):
        set_text_signal = pyqtSignal(str)
    
        def __init__(self, parent=None):
            super(Window, self).__init__()
            self.initUi()
            self.start_task()
    
        def initUi(self):
            layout = QVBoxLayout(self)
            self.button = QPushButton("User input")
            self.button.clicked.connect(self.sendtotask)
            layout.addWidget(self.button)
    
        def start_task(self):
            loop = asyncio.get_event_loop()
            self.worker = Worker(loop)
            self.set_text_signal.connect(self.worker.set_text_slot)
            self.worker.signal_back.connect(self.receive_from_worker)
            self.worker.work()
    
        @pyqtSlot(int)
        def receive_from_worker(self, number):
            print(str(number))
    
        def sendtotask(self):
            self.set_text_signal.emit("Task: Configured")
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        loop = QEventLoop(app)
        asyncio.set_event_loop(loop)
        ui = Window()
        ui.show()
        with loop:
            loop.run_forever()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-18
      • 2018-10-17
      • 1970-01-01
      • 1970-01-01
      • 2021-12-12
      相关资源
      最近更新 更多