aboutsummaryrefslogtreecommitdiffstats
path: root/examples/async/eratosthenes
diff options
context:
space:
mode:
Diffstat (limited to 'examples/async/eratosthenes')
-rw-r--r--examples/async/eratosthenes/eratosthenes_asyncio.py59
-rw-r--r--examples/async/eratosthenes/eratosthenes_trio.py14
-rw-r--r--examples/async/eratosthenes/requirements_trio.txt (renamed from examples/async/eratosthenes/requirements.txt)0
3 files changed, 45 insertions, 28 deletions
diff --git a/examples/async/eratosthenes/eratosthenes_asyncio.py b/examples/async/eratosthenes/eratosthenes_asyncio.py
index 87d56e6fa..f63058cdf 100644
--- a/examples/async/eratosthenes/eratosthenes_asyncio.py
+++ b/examples/async/eratosthenes/eratosthenes_asyncio.py
@@ -6,10 +6,8 @@ from PySide6.QtGui import (QColor, QFont, QPalette)
from PySide6.QtWidgets import (QApplication, QGridLayout, QLabel, QMainWindow, QVBoxLayout, QWidget)
import asyncio
-import outcome
import signal
import sys
-import traceback
from random import randint
@@ -61,7 +59,9 @@ class MainWindow(QMainWindow):
widget.setPalette(palette)
-class Eratosthenes():
+class Eratosthenes(QObject):
+
+ done_signal = Signal()
""" This Sieve of Eratosthenes runs on a configurable tick (default
0.1 seconds). At each tick, a new subroutine will be created
@@ -69,6 +69,7 @@ class Eratosthenes():
these subroutines also operates on the same tick. """
def __init__(self, num, window, tick=0.1):
+ super().__init__()
self.num = num
self.sieve = [True] * self.num
self.base = 0
@@ -119,15 +120,17 @@ class Eratosthenes():
"🥳 Congratulations! You found all the prime numbers and solved mathematics. 🥳"
)
+ # This signals to the guest run when there are no more asyncio tasks
+ # left so its event loop can finish.
+ self.done_signal.emit()
-class AsyncHelper(QObject):
- trigger_signal = Signal()
+class AsyncHelper(QObject):
class ReenterQtObject(QObject):
""" This is a QObject to which an event will be posted, allowing
- Trio to resume when the event is handled. event.fn() is the
- next entry point of the Trio event loop. """
+ asyncio to resume when the event is handled. event.fn() is
+ the next entry point of the asyncio event loop. """
def event(self, event):
if event.type() == QEvent.User + 1:
event.fn()
@@ -136,22 +139,26 @@ class AsyncHelper(QObject):
class ReenterQtEvent(QEvent):
""" This is the QEvent that will be handled by the ReenterQtObject.
- self.fn is the next entry point of the Trio event loop. """
+ self.fn is the next entry point of the asyncio event loop. """
def __init__(self, fn):
super().__init__(QEvent.Type(QEvent.User + 1))
self.fn = fn
- def __init__(self, entry=None):
+ def __init__(self, worker, entry):
super().__init__()
self.reenter_qt = self.ReenterQtObject()
self.entry = entry
self.loop = asyncio.new_event_loop()
+ self.done = False
- def set_entry(self, entry):
- self.entry = entry
+ self.worker = worker
+ if hasattr(self.worker, "start_signal") and isinstance(self.worker.start_signal, Signal):
+ self.worker.start_signal.connect(self.on_worker_started)
+ if hasattr(self.worker, "done_signal") and isinstance(self.worker.done_signal, Signal):
+ self.worker.done_signal.connect(self.on_worker_done)
@Slot()
- def launch_guest_run(self):
+ def on_worker_started(self):
""" To use asyncio and Qt together, one must run the asyncio
event loop as a "guest" inside the Qt "host" event loop. """
if not self.entry:
@@ -159,13 +166,23 @@ class AsyncHelper(QObject):
asyncio.set_event_loop(self.loop)
self.loop.create_task(self.entry())
self.loop.call_soon(self.next_guest_run_schedule)
+ self.done = False # Set this explicitly as we might want to restart the guest run.
self.loop.run_forever()
+ @Slot()
+ def on_worker_done(self):
+ """ When all our current asyncio tasks are finished, we must end
+ the "guest run" lest we enter a quasi idle loop of switching
+ back and forth between the asyncio and Qt loops. We can
+ launch a new guest run by calling launch_guest_run() again. """
+ self.done = True
+
def continue_loop(self):
""" This function is called by an event posted to the Qt event
- loop to restart the asyncio event loop. """
- self.loop.call_soon(self.next_guest_run_schedule)
- self.loop.run_forever()
+ loop to continue the asyncio event loop. """
+ if not self.done:
+ self.loop.call_soon(self.next_guest_run_schedule)
+ self.loop.run_forever()
def next_guest_run_schedule(self):
""" This function serves to pause and re-schedule the guest
@@ -189,13 +206,13 @@ if __name__ == "__main__":
app = QApplication(sys.argv)
main_window = MainWindow(rows, cols)
eratosthenes = Eratosthenes(num, main_window)
- async_helper = AsyncHelper(entry=eratosthenes.start)
+ async_helper = AsyncHelper(eratosthenes, eratosthenes.start)
- # This establishes the entry point for the Trio guest run. It varies
- # depending on how and when its event loop is to be triggered, e.g.,
- # from the beginning (as here) or rather at a specific moment like
- # a button press.
- QTimer.singleShot(0, async_helper.launch_guest_run)
+ # This establishes the entry point for the asyncio guest run. It
+ # varies depending on how and when its event loop is to be
+ # triggered, e.g., from the beginning (as here) or rather at a
+ # specific moment like a button press.
+ QTimer.singleShot(0, async_helper.on_worker_started)
main_window.show()
diff --git a/examples/async/eratosthenes/eratosthenes_trio.py b/examples/async/eratosthenes/eratosthenes_trio.py
index 5fb2a35be..862a4fddd 100644
--- a/examples/async/eratosthenes/eratosthenes_trio.py
+++ b/examples/async/eratosthenes/eratosthenes_trio.py
@@ -61,7 +61,7 @@ class MainWindow(QMainWindow):
widget.setPalette(palette)
-class Eratosthenes():
+class Eratosthenes(QObject):
""" This Sieve of Eratosthenes runs on a configurable tick (default
0.1 seconds). At each tick, a new subroutine will be created
@@ -69,6 +69,7 @@ class Eratosthenes():
these subroutines also operates on the same tick. """
def __init__(self, num, window, tick=0.1):
+ super().__init__()
self.num = num
self.sieve = [True] * self.num
self.base = 0
@@ -119,8 +120,6 @@ class Eratosthenes():
class AsyncHelper(QObject):
- trigger_signal = Signal()
-
class ReenterQtObject(QObject):
""" This is a QObject to which an event will be posted, allowing
Trio to resume when the event is handled. event.fn() is the
@@ -138,13 +137,14 @@ class AsyncHelper(QObject):
super().__init__(QEvent.Type(QEvent.User + 1))
self.fn = fn
- def __init__(self, entry=None):
+ def __init__(self, worker, entry):
super().__init__()
self.reenter_qt = self.ReenterQtObject()
self.entry = entry
- def set_entry(self, entry):
- self.entry = entry
+ self.worker = worker
+ if hasattr(self.worker, "start_signal") and isinstance(self.worker.start_signal, Signal):
+ self.worker.start_signal.connect(self.launch_guest_run)
@Slot()
def launch_guest_run(self):
@@ -183,7 +183,7 @@ if __name__ == "__main__":
app = QApplication(sys.argv)
main_window = MainWindow(rows, cols)
eratosthenes = Eratosthenes(num, main_window)
- async_helper = AsyncHelper(entry=eratosthenes.start)
+ async_helper = AsyncHelper(eratosthenes, eratosthenes.start)
# This establishes the entry point for the Trio guest run. It varies
# depending on how and when its event loop is to be triggered, e.g.,
diff --git a/examples/async/eratosthenes/requirements.txt b/examples/async/eratosthenes/requirements_trio.txt
index e2cc10204..e2cc10204 100644
--- a/examples/async/eratosthenes/requirements.txt
+++ b/examples/async/eratosthenes/requirements_trio.txt