You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于qasync与PySide6的asyncio程序:何时可调度事件循环任务?

问题描述

使用如下代码启动PySide6 + qasync程序时,在MainWindow的__init__或showEvent中调度的asyncio任务,要等到主窗口关闭后才会执行;但通过按钮触发调度时任务可正常运行,希望实现任务自动触发。

启动代码:

async def main():
    logger.info('client app starting')
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    loop = QEventLoop(app)
    with loop:
        loop.run_forever()

if __name__ == '__main__':
    initLogging(fileName=os.path.join('log','client.log'))
    asyncio.run(main())

环境版本:

python 3.12
PySide 6.7.3
qasync 0.27.1

可复现问题的示例代码(hello函数输出会在窗口关闭后打印):

import sys
import asyncio
from qasync import QApplication, QEventLoop
from PySide6.QtWidgets import QMainWindow, QLabel

async def hello():
    print('hello')

class MainWindow(QMainWindow):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle('Client App')

        task = asyncio.create_task(hello())

        self.label = QLabel('Main Window')
        self.setCentralWidget(self.label)

    def showEvent(self, event):
        print('main window show event')
        task = asyncio.create_task(hello())
    
    def focusInEvent(self, event):
        print('main window focus in event')

async def main():
    print('app starting')
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    loop = QEventLoop(app)
    with loop:
        loop.run_forever()

if __name__ == '__main__':
    asyncio.run(main())
问题原因

在main()函数中,main.show()触发showEvent时,qasync的QEventLoop还未启动,此时当前活跃的asyncio事件循环是asyncio.run()创建的临时循环。后续启动的QEventLoop会接管事件循环,但之前创建的任务绑定在旧循环中,旧循环被挂起直到程序结束才会执行这些任务,导致任务延迟到窗口关闭后才运行。

解决方案

方法1:在QEventLoop启动后提交任务

在main()函数的with loop:块内、loop.run_forever()之前创建任务,此时QEventLoop已成为当前活跃的asyncio循环,任务会立即执行:

async def main():
    print('app starting')
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    
    loop = QEventLoop(app)
    with loop:
        # 此时QEventLoop已接管当前asyncio循环
        asyncio.create_task(hello())
        loop.run_forever()

方法2:通过Qt定时器延迟提交任务(适合窗口事件触发)

如果需要在窗口showEvent中触发任务,使用QTimer.singleShot(0, ...)将任务提交到Qt事件队列,确保QEventLoop启动后再执行任务创建逻辑:

import sys
import asyncio
from qasync import QApplication, QEventLoop
from PySide6.QtWidgets import QMainWindow, QLabel
from PySide6.QtCore import QTimer

async def hello():
    print('hello')

class MainWindow(QMainWindow):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle('Client App')
        self.label = QLabel('Main Window')
        self.setCentralWidget(self.label)

    def showEvent(self, event):
        print('main window show event')
        # 0延迟定时器,确保事件循环启动后再创建任务
        QTimer.singleShot(0, self._start_async_task)
        event.accept()

    def _start_async_task(self):
        asyncio.create_task(hello())

async def main():
    print('app starting')
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    
    loop = QEventLoop(app)
    with loop:
        loop.run_forever()

if __name__ == '__main__':
    asyncio.run(main())

方法3:使用qasync的异步槽装饰器

利用qasync提供的asyncSlot装饰器,直接将Qt事件转换为异步函数,确保任务在QEventLoop中执行:

from qasync import asyncSlot

class MainWindow(QMainWindow):
    # ... 其他初始化代码 ...
    
    def showEvent(self, event):
        print('main window show event')
        self._start_async_task()
        event.accept()

    @asyncSlot()
    async def _start_async_task(self):
        await hello()
关键注意点
  • 必须确保asyncio任务是在qasync的QEventLoop成为当前活跃循环后创建,否则任务会绑定到旧的临时循环,无法及时执行。
  • 不要在__init__或showEvent的同步执行阶段直接创建asyncio任务,需等待QEventLoop启动完成。

内容的提问来源于stack exchange,提问作者iamthebull

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:44:50