Skip to main content

qt-async-threads

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 allows Qt applications to use convenient async/await syntax to run computational intensive or IO operations in threads, selectively changing the code slightly to provide a more responsive UI.

The objective of this library is to provide a simple and convenient way to improve UI responsiveness in existing Qt applications by using async/await, while at the same time not requiring large scale refactorings.

Supports PyQt5, PyQt6, PySide2, and PySide6 thanks to qtpy.

Example

The widget below downloads pictures of cats when the user clicks on a button (some parts omitted for brevity):

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")

This works well, but while the pictures are being downloaded the UI will freeze a bit, becoming unresponsive.

With qt-async-threads, we can easily change the code to:

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")

By using a QtAsyncRunner instance and changing the slot to an async function, the runner.run calls will run the requests in a thread, without blocking the Qt event loop, making the UI snappy and responsive.

Thanks to the async/await syntax, we can keep the entire flow in the same function as before, including handling exceptions naturally.

We could rewrite the first example using a ThreadPoolExecutor or QThreads, but that would require a significant rewrite.

Documentation

For full documentation, please see https://qt-async-threads.readthedocs.io/en/latest.

Differences with other libraries

There are excellent libraries that allow to use async frameworks with Qt:

Those libraries fully integrate with their respective frameworks, allowing the application to asynchronously communicate with sockets, threads, file system, tasks, cancellation systems, use other async libraries (such as httpx), etc.

They are very powerful in their own right, however they have one downside in that they require your main entry point to also be async, which might be hard to accommodate in an existing application.

qt-async-threads, on the other hand, focuses only on one feature: allow the user to leverage async/await syntax to handle threads more naturally, without the need for major refactorings in existing applications.

License

Distributed under the terms of the MIT license.

Metadata

Release files for qt-async-threads 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qt-async-threads 0.6.0
File Size Uploaded
qt-async-threads-0.6.0.tar.gz 33.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qt-async-threads 0.6.0
File Interpreter ABI Platform
qt_async_threads-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 46.6 kB

Release files / qt-async-threads-0.6.0.tar.gz

Download URL qt-async-threads-0.6.0.tar.gz
Size 33.4 kB
Tags Source
SHA-256 checksum
How to use checksums
921973c8d7f67b1b9891f0114f335adb1bacaf7eb7b4bc1fa54afbbfb7711279
BLAKE2b-256 checksum
How to use checksums
e30a2e894dc389a6a0680d87ce9b05bada21ddeabb2c0994e969428991caceac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/4.0.1 CPython/3.11.8

Release files / qt_async_threads-0.6.0-py3-none-any.whl

Download URL qt_async_threads-0.6.0-py3-none-any.whl
Size 13.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4cbb534573247f1212bc6575cbc8bebef8a5b814313eff364fa812e506f91a3f
BLAKE2b-256 checksum
How to use checksums
fa34358d069cddfc62699ba12302529ab47c87262e88ac1e41b2cb055bedf3d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/4.0.1 CPython/3.11.8

Release history Release notifications | RSS feed

This release

0.6.0 This release

2 release files

0.5.2

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page