|
78 | 78 | from PyQt5.QtCore import pyqtSlot as Slot |
79 | 79 |
|
80 | 80 | QApplication = QtWidgets.QApplication |
| 81 | + AllEvents = QtCore.QEventLoop.ProcessEventsFlags(0x00) |
81 | 82 |
|
82 | 83 | elif QtModuleName == "PyQt6": |
83 | 84 | from PyQt6 import QtWidgets |
84 | 85 | from PyQt6.QtCore import pyqtSlot as Slot |
85 | 86 |
|
86 | 87 | QApplication = QtWidgets.QApplication |
| 88 | + AllEvents = QtCore.QEventLoop.ProcessEventsFlag(0x00) |
87 | 89 |
|
88 | 90 | elif QtModuleName == "PySide2": |
89 | 91 | from PySide2 import QtWidgets |
90 | 92 | from PySide2.QtCore import Slot |
91 | 93 |
|
92 | 94 | QApplication = QtWidgets.QApplication |
| 95 | + AllEvents = QtCore.QEventLoop.ProcessEventsFlags(0x00) |
93 | 96 |
|
94 | 97 | elif QtModuleName == "PySide6": |
95 | 98 | from PySide6 import QtWidgets |
96 | 99 | from PySide6.QtCore import Slot |
97 | 100 |
|
98 | 101 | QApplication = QtWidgets.QApplication |
| 102 | + AllEvents = QtCore.QEventLoop.ProcessEventsFlags(0x00) |
99 | 103 |
|
100 | 104 | from ._common import with_logger # noqa |
101 | 105 |
|
@@ -142,9 +146,15 @@ def run(self): |
142 | 146 | else: |
143 | 147 | self._logger.debug("Setting Future result: %s", r) |
144 | 148 | future.set_result(r) |
| 149 | + finally: |
| 150 | + # Release potential reference |
| 151 | + r = None # noqa |
145 | 152 | else: |
146 | 153 | self._logger.debug("Future was canceled") |
147 | 154 |
|
| 155 | + # Delete references |
| 156 | + del command, future, callback, args, kwargs |
| 157 | + |
148 | 158 | self._logger.debug("Thread #%s stopped", self.__num) |
149 | 159 |
|
150 | 160 | def wait(self): |
@@ -228,7 +238,7 @@ def __exit__(self, *args): |
228 | 238 |
|
229 | 239 | def _format_handle(handle: asyncio.Handle): |
230 | 240 | cb = getattr(handle, "_callback", None) |
231 | | - if isinstance(getattr(cb, '__self__', None), asyncio.tasks.Task): |
| 241 | + if isinstance(getattr(cb, "__self__", None), asyncio.tasks.Task): |
232 | 242 | return repr(cb.__self__) |
233 | 243 | return str(handle) |
234 | 244 |
|
@@ -286,7 +296,11 @@ def timerEvent(self, event): # noqa: N802 |
286 | 296 | handle._run() |
287 | 297 | dt = time.time() - t0 |
288 | 298 | if dt >= loop.slow_callback_duration: |
289 | | - self._logger.warning('Executing %s took %.3f seconds', _format_handle(handle), dt) |
| 299 | + self._logger.warning( |
| 300 | + "Executing %s took %.3f seconds", |
| 301 | + _format_handle(handle), |
| 302 | + dt, |
| 303 | + ) |
290 | 304 | finally: |
291 | 305 | loop._current_handle = None |
292 | 306 | else: |
@@ -331,10 +345,7 @@ class _QEventLoop: |
331 | 345 | ... assert x + y == 4 |
332 | 346 | ... await asyncio.sleep(.1) |
333 | 347 | >>> |
334 | | - >>> loop = QEventLoop(app) |
335 | | - >>> asyncio.set_event_loop(loop) |
336 | | - >>> with loop: |
337 | | - ... loop.run_until_complete(xplusy(2, 2)) |
| 348 | + >>> asyncio.run(xplusy(2, 2), loop_factory=lambda:QEventLoop(app)) |
338 | 349 |
|
339 | 350 | If the event loop shall be used with an existing and already running QApplication |
340 | 351 | it must be specified in the constructor via already_running=True |
@@ -417,7 +428,9 @@ def stop(*args): |
417 | 428 | self.run_forever() |
418 | 429 | finally: |
419 | 430 | future.remove_done_callback(stop) |
420 | | - self.__app.processEvents() # run loop one last time to process all the events |
| 431 | + self.__app.eventDispatcher().processEvents( |
| 432 | + AllEvents |
| 433 | + ) # run loop one last time to process all the events |
421 | 434 | if not future.done(): |
422 | 435 | raise RuntimeError("Event loop stopped before Future completed.") |
423 | 436 |
|
@@ -801,6 +814,8 @@ def _error_handler(task): |
801 | 814 | task.result() |
802 | 815 | except Exception: |
803 | 816 | sys.excepthook(*sys.exc_info()) |
| 817 | + except asyncio.CancelledError: |
| 818 | + pass |
804 | 819 |
|
805 | 820 | def outer_decorator(fn): |
806 | 821 | @Slot(*args, **kwargs) |
@@ -871,28 +886,34 @@ def helper(): |
871 | 886 | QtCore.QTimer.singleShot(0, helper) |
872 | 887 | return await future |
873 | 888 |
|
874 | | -class QEventLoopPolicyMixin: |
875 | | - def new_event_loop(self): |
876 | | - return QEventLoop(QApplication.instance() or QApplication(sys.argv)) |
877 | 889 |
|
| 890 | +def _get_qevent_loop(): |
| 891 | + return QEventLoop(QApplication.instance() or QApplication(sys.argv)) |
878 | 892 |
|
879 | | -class DefaultQEventLoopPolicy( |
880 | | - QEventLoopPolicyMixin, |
881 | | - asyncio.DefaultEventLoopPolicy, |
882 | | -): |
883 | | - pass |
884 | 893 |
|
| 894 | +if sys.version_info >= (3, 12): |
885 | 895 |
|
886 | | -@contextlib.contextmanager |
887 | | -def _set_event_loop_policy(policy): |
888 | | - old_policy = asyncio.get_event_loop_policy() |
889 | | - asyncio.set_event_loop_policy(policy) |
890 | | - try: |
891 | | - yield |
892 | | - finally: |
893 | | - asyncio.set_event_loop_policy(old_policy) |
894 | | - |
| 896 | + def run(*args, **kwargs): |
| 897 | + return asyncio.run( |
| 898 | + *args, |
| 899 | + **kwargs, |
| 900 | + loop_factory=_get_qevent_loop, |
| 901 | + ) |
| 902 | +else: |
| 903 | + # backwards compatibility with event loop policies |
| 904 | + class DefaultQEventLoopPolicy(asyncio.DefaultEventLoopPolicy): |
| 905 | + def new_event_loop(self): |
| 906 | + return _get_qevent_loop() |
| 907 | + |
| 908 | + @contextlib.contextmanager |
| 909 | + def _set_event_loop_policy(policy): |
| 910 | + old_policy = asyncio.get_event_loop_policy() |
| 911 | + asyncio.set_event_loop_policy(policy) |
| 912 | + try: |
| 913 | + yield |
| 914 | + finally: |
| 915 | + asyncio.set_event_loop_policy(old_policy) |
895 | 916 |
|
896 | | -def run(*args, **kwargs): |
897 | | - with _set_event_loop_policy(DefaultQEventLoopPolicy()): |
898 | | - return asyncio.run(*args, **kwargs) |
| 917 | + def run(*args, **kwargs): |
| 918 | + with _set_event_loop_policy(DefaultQEventLoopPolicy()): |
| 919 | + return asyncio.run(*args, **kwargs) |
0 commit comments