gpt4 book ai didi

python - 如何在另一个线程中设置 qasync 事件循环?

转载 作者:行者123 更新时间:2023-12-05 04:48:38 25 4
gpt4 key购买 nike

我正在开发一个异步 PyQt 应用程序 - 也就是说,我需要 asyncio 和 PyQt。由于 asyncio 和 PyQt 都使用它们自己的事件循环,如果没有 qasync,这是不可能的,它是为解决这个问题而创建的。以下是您如何使用它:

app = QApplication(sys.argv)
asyncio.set_event_loop(qasync.QEventLoop(app))
exit(app.exec_())

现在,我想将我所有的逻辑移动到一个单独的“工作”线程中。为此,我创建了一个 QThread,它会自动为此线程设置一个新的 Qt 事件循环。但我也需要能够在该线程中使用 asyncio,因此我必须对工作线程执行相同的 qasync 技巧。

但是我该怎么做呢?据我了解,qasync.QEventLoop(app) 将获得应用程序的主事件循环,而不是线程的循环。你不能做 qasync.QEventLoop(thread)

这是一个代码示例。

import logging
import sys
import datetime
import asyncio
import qasync

from PyQt5.QtCore import QObject, pyqtSlot, QThread
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget, QVBoxLayout


class Logic(QObject): # Logic to run in a separate thread
enabled = False

@pyqtSlot()
def start(self):
logger.debug('Start called')
self.enabled = True
asyncio.create_task(self.process())

@pyqtSlot()
def stop(self):
logger.debug('Stop called')
self.enabled = False

async def process(self):
while self.enabled:
logger.debug(f'Processing ({datetime.datetime.now()})...')
await asyncio.sleep(0.5)


if __name__ == '__main__':
logging.basicConfig(format='%(levelname)s:%(name)s: %(message)s')
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

app = QApplication(sys.argv)
asyncio.set_event_loop(qasync.QEventLoop(app))

logic = Logic()

# Move logic to another thread
# thread = QThread()
# logic.moveToThread(thread)
# thread.start()

window = QMainWindow()
window.setCentralWidget(QWidget())
window.centralWidget().setLayout(QVBoxLayout())

window.centralWidget().layout().addWidget(QPushButton(text='Start', clicked=logic.start))
window.centralWidget().layout().addWidget(QPushButton(text='Stop', clicked=logic.stop))

window.show()

logger.debug('Launching the application...')
exit(app.exec_())

如果按原样运行它,它会在单个线程中运行。但是如果你取消注释“Move logic to another thread” block ,那么它就会失败,因为 RuntimeError: no running event loop for asyncio.create_task()

我该如何解决这个问题?我想所需的行为很明显 - 它的行为应该与在单个线程中的行为相同,除了 logic 中的所有内容都应该在单独的线程中运行。

一个子问题是,qasync 究竟做了什么?我的理解是否正确,它以某种方式允许对 asyncio 使用相同的主 Qt 事件循环?所以 qasync.QEventLoop(app) 返回一个...某种 asyncio 兼容的代理对象到现有的 Qt 事件循环?..

最佳答案

我认为问题是由于您正在设置从 QApplication(在主线程中)创建的 QEventLoop。这是我的版本,用于展示如何从单独的线程运行 asyncio:

import logging
import sys
import datetime
import asyncio
import qasync
import threading
from PyQt5.QtCore import QObject, pyqtSlot, QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget, QVBoxLayout


class Logic(QObject): # Logic to run in a separate thread
enabled = False

@pyqtSlot()
def start(self):
logger.debug('Start called')
self.enabled = True
asyncio.create_task(self.process())

@pyqtSlot()
def stop(self):
logger.debug('Stop called')
self.enabled = False

async def process(self):
while self.enabled:
# demonstrate that we can emit a signal from a thread
self.message.emit(f'Processing ({datetime.datetime.now()}), worker thread id: {threading.get_ident()}...')
await asyncio.sleep(0.5)

# signal declaration
message = pyqtSignal(str)

# subclass QThread. override the run function to create an event loop and run forever
class WorkerThread(QThread):
def run(self):
loop = qasync.QEventLoop(self)
asyncio.set_event_loop(loop)
loop.run_forever()

if __name__ == '__main__':
logging.basicConfig(format='%(levelname)s:%(name)s: %(message)s')
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

app = QApplication(sys.argv)
logger.debug(f'main thread id: {threading.get_ident()}')

# Move logic to another thread
logic = Logic()
thread = WorkerThread()
logic.moveToThread(thread)
thread.start()

window = QMainWindow()
window.setCentralWidget(QWidget())
window.centralWidget().setLayout(QVBoxLayout())

window.centralWidget().layout().addWidget(QPushButton(text='Start', clicked=logic.start))
window.centralWidget().layout().addWidget(QPushButton(text='Stop', clicked=logic.stop))
# connect logic in workerThread to lambda function in this thread
logic.message.connect(lambda msg: logger.debug(f'current thread: {threading.get_ident()}, {msg}'))

window.show()

logger.debug('Launching the application...')
exit(app.exec_())

关于python - 如何在另一个线程中设置 qasync 事件循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67979231/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com