PyQt5+asyncqt问题:asyncSlot()仍导致事件循环冻结
解决PyQt5+asyncqt异步槽函数中窗口无响应的问题
你的问题出在异步槽函数里仍然执行了同步阻塞的下载操作。asyncSlot只是让槽函数支持异步语法,但pytube的download()方法是同步阻塞的,它会占用Qt事件循环的主线程,导致窗口无法处理用户操作,直到下载完成。
要解决这个问题,需要把同步的下载任务放到单独的线程中执行,避免阻塞主线程。可以用asyncio.to_thread()(Python 3.9及以上)或者loop.run_in_executor()来实现。
修改后的完整代码如下:
import sys import asyncio from asyncqt import QEventLoop, asyncSlot from pytube import YouTube from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QLineEdit, QTextEdit, QPushButton, QVBoxLayout) class MainWindow(QWidget): def __init__(self): super().__init__() self.setLayout(QVBoxLayout()) self.lblStatus = QLabel('Idle', self) self.layout().addWidget(self.lblStatus) self.editUrl = QLineEdit('Hi!', self) self.layout().addWidget(self.editUrl) self.editResponse = QTextEdit('', self) self.layout().addWidget(self.editResponse) self.btnFetch = QPushButton('Fetch', self) self.btnFetch.clicked.connect(self.on_btnFetch_clicked) self.layout().addWidget(self.btnFetch) @asyncSlot() async def on_btnFetch_clicked(self): self.lblStatus.setText('downloading...') # 将同步下载任务放到线程中执行 await asyncio.to_thread(self.download_video) self.lblStatus.setText('downloaded!') def download_video(self): # 这里是原有的同步下载逻辑,现在在单独线程执行 yt = YouTube('https://www.youtube.com/watch?v=zG4uYN3n8zs') yt.streams.filter(resolution='720p').first().download('C:/Users/Andrew/Desktop') if __name__ == '__main__': app = QApplication(sys.argv) mainWindow = MainWindow() mainWindow.show() loop = QEventLoop(app) asyncio.set_event_loop(loop) with loop: sys.exit(loop.run_forever())
关键说明:
asyncio.to_thread()会把传入的同步函数放到线程池的线程中执行,主线程的事件循环可以继续处理窗口的用户操作,不会被阻塞。- 如果你的Python版本低于3.9,可以用
loop.run_in_executor()替代:# 先获取事件循环 loop = asyncio.get_running_loop() await loop.run_in_executor(None, self.download_video) - 把下载逻辑单独抽成一个同步函数,放到线程中执行,这样异步槽函数只负责调度和状态更新,不会阻塞主线程。
内容的提问来源于stack exchange,提问作者Andrew
相关产品推荐
相关产品推荐

