API Reference
qtinter provides the following functions and classes in its
public API:
Context managers for asyncio-Qt interop:
using_asyncio_from_qt()enables Qt-driven code to use asyncio-based components.using_qt_from_asyncio()enables asyncio-driven code to use Qt-based components.
Helper functions to make interp code fit naturally into the current coding pattern:
asyncsignal()makes a Qt signal awaitable; useful for asyncio-driven code.asyncsignalstream()exposes a Qt signal as an asynchronous iterator; useful for asyncio-driven code.asyncslot()connects a coroutine function to a Qt signal; useful for Qt-driven code.modal()allows the asyncio event loop to continue running in a nested Qt event loop.multisignal()collects multiple Qt signals and re-emits them with a tag.run_task()creates anasyncio.Taskand eagerly executes its first step.
Loop factory to create event loop objects directly:
new_event_loop()creates an asyncio-compatible logical event loop object that runs on top of a physical Qt event loop.
Low-level classes that do the actual work of bridging Qt and asyncio:
Private API that supports the internal implementation of qtinter.
Context managers
- qtinter.using_asyncio_from_qt()
Context manager that enables enclosed Qt-driven code to use asyncio-based libraries.
Your code is Qt-driven if it calls
app.exec()or equivalent as its entry point.Example:
app = QtWidgets.QApplication([]) with qtinter.using_asyncio_from_qt(): app.exec()
- qtinter.using_qt_from_asyncio()
Context manager that enables enclosed asyncio-driven code to use Qt components.
Your code is asyncio-driven if it calls
asyncio.run()or equivalent as its entry point.Note
This context manager modifies the global (per-interpreter) asyncio event loop policy. Do not use this context manager if your code uses event loops from multiple threads. Instead, call
new_event_loop()to create an event loop object and call its methods directly. Since Python 3.11, useasyncio.Runnerand passnew_event_loopas its loop_factory parameter.
Helper functions
- async qtinter.asyncsignal(signal: BoundSignal[Unpack[Ts]]) Tuple[Unpack[Ts]]
Wait for signal to emit and return the emitted arguments in a
tuple.signal must be a bound Qt signal object, i.e. a bound PyQt5.QtCore.pyqtSignal, PyQt6.QtCore.pyqtSignal, PySide2.QtCore.Signal or PySide6.QtCore.Signal, or an object with a
connectmethod of equivalent semantics, such as an instance ofmultisignal.signal is connected to using an AutoConnection when the returned coroutine object is awaited. It is disconnected from after the signal is emitted once.
Note
Signals that require immediate response from the slot cannot be used with this function. An example is proxyAuthenticationRequired.
Note
This function will wait indefinitely if the signal is never emitted, e.g. if the sender object is deleted before emitting a signal. To handle the latter situation, keep a strong reference to the sender object, or listen to its destroyed signal.
- qtinter.asyncsignalstream(signal: BoundSignal[Unpack[Ts]]) AsyncIterator[Tuple[Unpack[Ts]]]
Return an asynchronous iterator that produces the emitted arguments from signal as a
tuple.signal is connected to via an AutoConnection before the function returns. It is disconnected from when the returned iterator object is deleted. Emitted arguments in the interim are stored in an internal buffer that grows without bound. It is advised to consume the iterator timely to avoid exhausting memory.
Example:
timer = QtCore.QTimer() timer.setInterval(1000) timer.start() what = 'tick' async for _ in qtinter.asyncsignalstream(timer.timeout): print(what) what = 'tock' if what == 'tick' else 'tick'
- qtinter.asyncslot(fn: Callable[[Unpack[Ts]], Coroutine[T]], *, task_runner: Callable[[Coroutine[T]], asyncio.Task[T]] = qtinter.run_task) Callable[[Unpack[Ts]], asyncio.Task[T]]
Return a callable object wrapping coroutine function fn so that it can be connected to a Qt signal.
When the returned wrapper is called, fn is called with the same arguments to produce a coroutine object. The coroutine object is then passed to task_runner to create an
asyncio.Taskobject that handles its execution. The task object is returned by the wrapper.The default task_runner,
run_task, eagerly executes the task until the firstyield,returnorraise(whichever comes first) before returning the task object. The remainder of the coroutine is scheduled for later execution.Note
asyncslot()keeps a strong reference to any task object it creates until the task completes.Note
If fn is a (bound) method object, the returned wrapper will also be a method object whose lifetime is equal to that of fn, except that a strong reference to the returned wrapper keeps fn alive.
- qtinter.modal(fn: Callable[[Unpack[Ts]], T]) Callable[[Unpack[Ts]], Coroutine[T]]
Return a coroutine function that wraps a regular function fn. The coroutine function takes the same arguments as fn.
When the returned coroutine function is called and awaited, fn is scheduled to be called as interleaved code immediately after the caller is suspended. The result (exception) of fn is returned (raised) by the coroutine.
Note
This function is similar to
asyncio.loop.run_in_executor()except that fn is executed in the same thread as interleaved code.This function is designed to be called from a coroutine to schedule an fn that creates a nested Qt event loop. In this case, the logical asyncio event loop is allowed to continue running without nesting. For example:
await qtinter.modal(QtWidgets.QMessageBox.warning)(self, "Title", "Message")
- class qtinter.multisignal(signal_map: Mapping[BoundSignal, Any])
Collect multiple bound signals and re-emit their arguments along with their mapped value.
A
multisignalobject defines the following instance method:- connect(slot: Callable[[Any, Tuple], Any]) None
Connect slot to each signal in (the keys of) signal_map, such that if signal s is mapped to v in signal_map and is emitted with arguments
*args, slot is called withvandargsas arguments from the thread that calledconnect(). Its return value is ignored.The sender objects of signals in signal_map must be alive when
connect()is called, or the process will crash with SIGSEGV.
multisignalobjects do not have a disconnect method. The connections are automatically disconnected when the sender (of a signal) or the receiver (of slot) is deleted.multisignalmay be used withasyncsignal()to listen to multiple signals.Example:
fast_timer = QtCore.QTimer() slow_timer = QtCore.QTimer() # ... ms = qtinter.multisignal({ fast_timer.timeout: 'fast', slow_timer.timeout: 'slow', }) ms.connect(print) # Output: # fast () # slow ()
- qtinter.run_task(coro: Coroutine[T], *, allow_task_nesting: bool = True, name: Optional[str] = None, context: Optional[contextvars.Context] = None) asyncio.Task[T]
Create an
asyncio.Taskwrapping the coroutine coro and execute it immediately until the firstyield,returnorraise, whichever comes first. The remainder of the coroutine is scheduled for later execution. Return theasyncio.Taskobject.If allow_task_nesting is
True(the default), this method is allowed to be called from a running task — the calling task is ‘suspended’ before executing the first step of coro and ‘resumed’ after that step completes. If allow_task_nesting isFalse, this method can only be called from a callback.An asyncio event loop must be running when this function is called.
Since Python 3.8: Added the name parameter.
Since Python 3.11: Added the context parameter.
Loop factory
- qtinter.new_event_loop() asyncio.AbstractEventLoop
Return a new instance of an asyncio-compatible event loop object that runs on top of a Qt event loop.
Use this function instead of
using_qt_from_asyncio()if your code uses different types of event loops from multiple threads. For example, starting from Python 3.11, if your code usesasyncio.Runneras its entry point, pass this function as the loop_factory parameter when constructingasyncio.Runner.
Low-level classes
You normally do not need to use these low-level API directly.
Event loop interface
All event loop objects below are derived from the abstract base class
QiBaseEventLoop.
- class qtinter.QiBaseEventLoop
Counterpart to the (undocumented)
asyncio.BaseEventLoopclass, implemented on top of a Qt event loop.In addition to asyncio’s Event Loop Methods, this class defines the following methods for Qt interop:
- exec_modal(fn: Callable[[], Any]) None
Schedule fn to be called as interleaved code (i.e. not as a callback) immediately after the current callback completes. The return value of fn is ignored.
This method must be called from a coroutine or callback. There can be at most one pending fn at any time.
If the current callback raises
KeyboardInterruptorSystemExit, fn will be called the next time the loop is run.
- set_mode(mode: QiLoopMode) None:
Set loop operating mode to mode.
This method can only be called when the loop is not closed and not running, and no stop is pending.
A newly created loop object is in
QiLoopMode.OWNERmode.
- start() None:
Start the loop (i.e. put it into running state) and return without waiting for it to stop.
This method can only be called in guest mode and when the loop is not already running.
- class qtinter.QiLoopMode
An
enum.Enumthat defines the possible operating modes of aQiBaseEventLoop. Its members are:- OWNER
Appropriate for use with asyncio-driven code.
- GUEST
Appropriate for use with Qt-driven code.
- NATIVE
Appropriate for running clean-up code.
For details on the semantics of these modes, see Loop modes.
Event loop objects
- class qtinter.QiDefaultEventLoop
In Python 3.7: alias to
QiSelectorEventLoop.Since Python 3.8: alias to
QiSelectorEventLoopon Unix andQiProactorEventLoopon Windows.
- class qtinter.QiProactorEventLoop(proactor=None)
Counterpart to
asyncio.ProactorEventLoop, implemented on top of a Qt event loop.Availability: Windows.
- class qtinter.QiSelectorEventLoop(selector=None)
Counterpart to
asyncio.SelectorEventLoop, implemented on top of a Qt event loop.
Event loop policy objects
- class qtinter.QiDefaultEventLoopPolicy
In Python 3.7: alias to
QiSelectorEventLoopPolicy.Since Python 3.8: alias to
QiSelectorEventLoopPolicyon Unix andQiProactorEventLoopPolicyon Windows.
- class qtinter.QiProactorEventLoopPolicy
Event loop policy that creates
QiProactorEventLoop.Availability: Windows.
- class qtinter.QiSelectorEventLoopPolicy
Event loop policy that creates
QiSelectorEventLoop.
Private API
The following classes and functions are used internally to support
qtinter’s implementation. They are documented here solely
for developing qtinter, and are subject to change at any time.
- class qtinter.SemiWeakRef(o, ref=weakref.ref)
Return an object that is deleted when o is deleted, except that a strong reference to the returned object in user code keeps o alive.
ref should be a weak reference class suitable for o: If o is a method object, ref should be set to
weakref.WeakMethod; otherwise, ref should be set toweakref.ref.- referent()
Return o if it is still live, or
None.
- qtinter.copy_signal_arguments(args: Tuple[Unpack[Ts]]) Tuple[Unpack[Ts]]
Return a copy of signal arguments args where necessary.
In PyQt5/6, signal arguments passed to a slot may be temporary objects whose lifetime is only valid during the slot’s execution. In order to use the signal arguments after the slot returns, one must call this function to make a copy of them, or the program may crash with SIGSEGV when the signal arguments are accessed later.
PySide2/6 already passes a copy of the signal arguments to slots, whose lifetime is controlled by the usual Python mechanisms. This function returns args as is in this case.
- qtinter.get_positional_parameter_count(fn: Callable) int
Return the number of positional parameters of fn, or
-1if fn takes variadic positional parameters (*args).Raises
TypeErrorif fn takes any keyword-only parameter without a default.
- qtinter.transform_slot(slot: Callable[[Unpack[Ts]], T], transform: Callable[[Callable[[Unpack[Ts]], T], Tuple[Unpack[Rs]], Unpack[Es]], R], *extras: Unpack[Es]) Callable[[Unpack[Rs]], R]
Return a callable wrapper that takes variadic arguments
*args, such thatwrapper(*arg)returnstransform(slot, args, *extra).If slot is a bound method object, wrapper will also be a bound method object with the same lifetime as slot, except that a strong reference to wrapper keeps slot alive.
If slot is not a bound method object, wrapper will be a function object that holds a strong reference to slot.