我正在开发一个异步 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/