diff options
Diffstat (limited to 'sources/pyside6/PySide6')
| -rw-r--r-- | sources/pyside6/PySide6/QtAsyncio/events.py | 48 |
1 files changed, 41 insertions, 7 deletions
diff --git a/sources/pyside6/PySide6/QtAsyncio/events.py b/sources/pyside6/PySide6/QtAsyncio/events.py index 7e578e547..edd42646f 100644 --- a/sources/pyside6/PySide6/QtAsyncio/events.py +++ b/sources/pyside6/PySide6/QtAsyncio/events.py @@ -1,7 +1,7 @@ # Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -from PySide6.QtCore import QDateTime, QCoreApplication, QTimer, QThread, Slot +from PySide6.QtCore import QCoreApplication, QDateTime, QEventLoop, QObject, QTimer, QThread, Slot from . import futures from . import tasks @@ -22,6 +22,37 @@ __all__ = [ ] +class QAsyncioExecutorWrapper(QObject): + + def __init__(self, func: typing.Callable, *args: typing.Tuple) -> None: + super().__init__() + self._loop: QEventLoop + self._func = func + self._args = args + self._result = None + self._exception = None + + def _cb(self): + try: + self._result = self._func(*self._args) + except BaseException as e: + self._exception = e + self._loop.exit() + + def do(self): + # This creates a new event loop and dispatcher for the thread, if not already created. + self._loop = QEventLoop() + asyncio.events._set_running_loop(self._loop) + QTimer.singleShot(0, self._loop, lambda: self._cb()) + self._loop.exec() + if self._exception is not None: + raise self._exception + return self._result + + def exit(self): + self._loop.exit() + + class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy): def __init__(self, application: typing.Optional[QCoreApplication] = None) -> None: super().__init__() @@ -45,7 +76,7 @@ class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy): return QAsyncioEventLoop(self._application) -class QAsyncioEventLoop(asyncio.BaseEventLoop): +class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject): """ Implements the asyncio API: https://docs.python.org/3/library/asyncio-eventloop.html @@ -71,7 +102,8 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop): self._loop.call_soon_threadsafe(self._future.set_exception, e) def __init__(self, application: QCoreApplication) -> None: - super().__init__() + asyncio.BaseEventLoop.__init__(self) + QObject.__init__(self) self._application: QCoreApplication = application self._thread = QThread.currentThread() @@ -206,6 +238,8 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop): typing.Optional[contextvars.Context] = None) -> "QAsyncioHandle": if self.is_closed(): raise RuntimeError("Event loop is closed") + if context is None: + context = contextvars.copy_context() return self.call_soon(callback, *args, context=context) def call_later(self, delay: typing.Union[int, float], # type: ignore[override] @@ -213,8 +247,7 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop): context: typing.Optional[contextvars.Context] = None) -> "QAsyncioHandle": if not isinstance(delay, (int, float)): raise TypeError("delay must be an int or float") - return self.call_at(self.time() + delay, callback, *args, - context=context) + return self.call_at(self.time() + delay, callback, *args, context=context) def call_at(self, when: typing.Union[int, float], # type: ignore[override] callback: typing.Callable, *args: typing.Any, @@ -402,8 +435,9 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop): raise RuntimeError("Event loop is closed") if executor is None: executor = self._default_executor + wrapper = QAsyncioExecutorWrapper(func, *args) return asyncio.futures.wrap_future( - executor.submit(func, *args), loop=self + executor.submit(wrapper.do), loop=self ) def set_default_executor(self, @@ -478,7 +512,7 @@ class QAsyncioHandle(): def _schedule_event(self, timeout: int, func: typing.Callable) -> None: if not self._loop.is_closed() and not self._loop._quit_from_outside: - QTimer.singleShot(timeout, func) + QTimer.singleShot(timeout, self._loop, func) def _start(self) -> None: self._schedule_event(self._timeout, lambda: self._cb()) |
