Python library for using asyncio in Qt-based applications
npx @tessl/cli install tessl/pypi-qasync@0.28.0Python library for using asyncio in Qt-based applications, providing an implementation of the PEP 3156 event loop that enables coroutines and asyncio functionality to run directly inside Qt application event loops in the main thread.
pip install qasyncimport qasyncCommon usage patterns:
from qasync import QApplication, QEventLoop, QThreadExecutor, asyncSlot, asyncClose, asyncWrapimport asyncio
import sys
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel
from qasync import QApplication, QEventLoop, asyncSlot, asyncClose
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setLayout(QVBoxLayout())
self.lbl_status = QLabel("Idle", self)
self.layout().addWidget(self.lbl_status)
@asyncClose
async def closeEvent(self, event):
# Run async cleanup before app closes
await asyncio.sleep(0.1) # Example async operation
@asyncSlot()
async def on_button_clicked(self):
# Handle button click asynchronously
self.lbl_status.setText("Processing...")
await asyncio.sleep(2) # Simulate async work
self.lbl_status.setText("Complete!")
if __name__ == "__main__":
app = QApplication(sys.argv)
app_close_event = asyncio.Event()
app.aboutToQuit.connect(app_close_event.set)
main_window = MainWindow()
main_window.show()
# Python 3.12+ syntax
asyncio.run(app_close_event.wait(), loop_factory=QEventLoop)
# For Python 3.11 and older
# qasync.run(app_close_event.wait())qasync bridges Python's asyncio ecosystem with Qt's event-driven architecture through several key components:
The library automatically detects the available Qt framework (PyQt5, PyQt6, PySide2, or PySide6) and adapts accordingly, making it framework-agnostic while maintaining full asyncio compatibility.
Framework-agnostic QApplication that automatically detects and adapts to available Qt frameworks (PyQt5, PyQt6, PySide2, PySide6), providing a unified interface for Qt application initialization.
class QApplication:
"""
Qt Application class that automatically adapts to the available Qt framework.
Provides the same interface as Qt's QApplication but automatically detects
and uses the correct implementation from PyQt5/6 or PySide2/6.
"""
def __init__(self, args=None): ...
def exec(self): ... # PyQt6/PySide6
def exec_(self): ... # PyQt5/PySide2
def aboutToQuit: ... # Signal
def instance(self): ... # Class method
def processEvents(self): ...Qt-compatible asyncio event loop that runs coroutines alongside Qt's native event processing, enabling responsive GUI applications with asynchronous operations.
class QEventLoop:
def __init__(app=None, set_running_loop=False, already_running=False): ...
def run_forever(): ...
def run_until_complete(future): ...
def stop(): ...
def close(): ...Thread pool executor using Qt's QThread for CPU-intensive tasks, providing concurrent.futures.Executor-compatible interface for parallel processing.
class QThreadExecutor:
def __init__(max_workers=10, stack_size=None): ...
def submit(callback, *args, **kwargs): ...
def shutdown(wait=True): ...Decorators that enable Qt slots and event handlers to run as async coroutines, integrating asyncio seamlessly with Qt's signal-slot system.
def asyncSlot(*args, **kwargs): ...
def asyncClose(fn): ...Helper functions for wrapping blocking operations and running asyncio with Qt event loops, plus advanced event loop policy classes.
async def asyncWrap(fn, *args, **kwargs): ...
def run(*args, **kwargs): ...
class DefaultQEventLoopPolicy: ...