跳转到主要内容

使用便捷的async/await语法在Qt应用程序中启动线程

项目描述

https://img.shields.io/pypi/v/qt-async-threads.svg https://img.shields.io/conda/vn/conda-forge/qt-async-threads.svg https://img.shields.io/pypi/pyversions/qt-async-threads.svg https://github.com/nicoddemus/qt-async-threads/workflows/test/badge.svg pre-commit.ci status https://img.shields.io/badge/code%20style-black-000000.svg https://readthedocs.org/projects/qt-async-threads/badge/?version=latest

qt-async-threads 允许Qt应用程序使用便捷的 async/await 语法在线程中运行计算密集型或IO操作,略微修改代码以提供更响应的UI。

该库的目的是通过使用 async/await,以简单便捷的方式提高现有Qt应用程序的UI响应速度,同时不需要大规模重构。

支持 PyQt5PyQt6PySide2PySide6,归功于 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 语法,我们可以保持整个流程与之前相同,包括自然地处理异常。

我们可以使用 ThreadPoolExecutorQThreads 重新编写第一个示例,但这将需要重大的重写。

文档

有关完整文档,请参阅 https://qt-async-threads.readthedocs.io/en/latest

与其他库的差异

有一些出色的库允许使用异步框架与 Qt 结合使用

这些库完全集成了它们各自的框架,允许应用程序异步地与套接字、线程、文件系统、任务、取消系统进行通信,使用其他异步库(如 httpx),等等。

它们本身非常强大,然而,它们有一个缺点,即它们要求您的 main 入口点也必须是 async,这可能在现有应用程序中很难适应。

qt-async-threads,另一方面,只关注一个功能:允许用户利用 async/await 语法以 更自然地处理线程,而无需对现有应用程序进行重大重构。

许可证

根据 MIT 许可证发布。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源代码分布

qt-async-threads-0.6.0.tar.gz (33.4 kB 查看哈希值)

上传时间 源代码

构建分布

qt_async_threads-0.6.0-py3-none-any.whl (13.2 kB 查看哈希值)

上传时间 Python 3

支持者