使用便捷的async/await语法在Qt应用程序中启动线程
项目描述
qt-async-threads 允许Qt应用程序使用便捷的 async/await 语法在线程中运行计算密集型或IO操作,略微修改代码以提供更响应的UI。
该库的目的是通过使用 async/await,以简单便捷的方式提高现有Qt应用程序的UI响应速度,同时不需要大规模重构。
支持 PyQt5、PyQt6、PySide2 和 PySide6,归功于 qtpy。
示例
下面的小部件在用户点击按钮时下载猫的图片(为了简洁,省略了一些部分)
class CatsWidget(QWidget):
def __init__(self, parent: QWidget) -> None:
...
self.download_button.clicked.connect(self._on_download_button_clicked)
def _on_download_button_clicked(self, checked: bool = False) -> None:
self.progress_label.setText("Searching...")
api_url = "https://api.thecatapi.com/v1/images/search"
for i in range(10):
try:
# Search.
search_response = requests.get(api_url)
self.progress_label.setText("Found, downloading...")
# Download.
url = search_response.json()[0]["url"]
download_response = requests.get(url)
except ConnectionError as e:
QMessageBox.critical(self, "Error", f"Error: {e}")
return
self._save_image_file(download_response)
self.progress_label.setText(f"Done downloading image {i}.")
self.progress_label.setText(f"Done, {downloaded_count} cats downloaded")
这很好,但是在图片下载过程中,UI会稍微冻结,变得不响应。
使用 qt-async-threads,我们可以轻松地将代码修改为
class CatsWidget(QWidget):
def __init__(self, runner: QtAsyncRunner, parent: QWidget) -> None:
...
# QtAsyncRunner allows us to submit code to threads, and
# provide a way to connect async functions to Qt slots.
self.runner = runner
# `to_sync` returns a slot that Qt's signals can call, but will
# allow it to asynchronously run code in threads.
self.download_button.clicked.connect(
self.runner.to_sync(self._on_download_button_clicked)
)
async def _on_download_button_clicked(self, checked: bool = False) -> None:
self.progress_label.setText("Searching...")
api_url = "https://api.thecatapi.com/v1/images/search"
for i in range(10):
try:
# Search.
# `self.runner.run` calls requests.get() in a thread,
# but without blocking the main event loop.
search_response = await self.runner.run(requests.get, api_url)
self.progress_label.setText("Found, downloading...")
# Download.
url = search_response.json()[0]["url"]
download_response = await self.runner.run(requests.get, url)
except ConnectionError as e:
QMessageBox.critical(self, "Error", f"Error: {e}")
return
self._save_image_file(download_response)
self.progress_label.setText(f"Done downloading image {i}.")
self.progress_label.setText(f"Done, {downloaded_count} cats downloaded")
通过使用一个 QtAsyncRunner 实例并将槽函数改为 async 函数,runner.run 的调用将在一个线程中运行,不会阻塞 Qt 事件循环,使 UI 更加敏捷和响应。
得益于 async/await 语法,我们可以保持整个流程与之前相同,包括自然地处理异常。
我们可以使用 ThreadPoolExecutor 或 QThreads 重新编写第一个示例,但这将需要重大的重写。
文档
有关完整文档,请参阅 https://qt-async-threads.readthedocs.io/en/latest。
与其他库的差异
有一些出色的库允许使用异步框架与 Qt 结合使用
这些库完全集成了它们各自的框架,允许应用程序异步地与套接字、线程、文件系统、任务、取消系统进行通信,使用其他异步库(如 httpx),等等。
它们本身非常强大,然而,它们有一个缺点,即它们要求您的 main 入口点也必须是 async,这可能在现有应用程序中很难适应。
qt-async-threads,另一方面,只关注一个功能:允许用户利用 async/await 语法以 更自然地处理线程,而无需对现有应用程序进行重大重构。
许可证
根据 MIT 许可证发布。
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。