Part A - Starting an AsyncWorker¶
Let’s start by using the AsyncWorker's default task to see the signals it emits and how to receive
those signals by connecting them to callbacks. By default, the AsyncWorker will perform a demo task of
counting to five with a delay of 1 second between counts. Generally a worker emits three kinds of signals,
a started signal, a progress signal, and a
finished signal (you can also define/emit custom signals, but more on that
later).
The started signal indicates that a worker has started it’s long-running task. It
contains the id of the worker which can be used to differentiate between active workers. Note that to emit this signal,
the worker must call its emit_start() method at the top of run()
(more on overriding the run() method in Part B). Below is a callback that will be triggered
on the started signal:
def worker_started_callback(worker_id: str):
print(f'Worker with id {worker_id} has started its task\n')
The progress signal passes along an AsyncWorkerProgress object
containing a progress value,
status message, and the worker's id.
Here is a simple callback function that will print out the progress of an AsyncWorker:
def progress_callback(progress: AsyncWorkerProgress):
print(f'Progress Received, value: {progress.value}, message: {progress.message}')
The callback receives the progress as a parameter. Within the callback, the progress value
and message attributes are extracted and printed to the console.
Now let’s take a look at the completion callback. This is the function that gets called when the worker is done running
it’s task. It gets connected to the worker’s finished signal and receives the
worker’s results as an AsyncWorkerResults object:
def completion_callback(results: AsyncWorkerResults):
print(f'\nWorker Complete!')
print(f'\tWarnings: {results.warnings}')
print(f'\tErrors: {results.errors}')
print(f'\tResults: {results.results_dict}')
sys.exit() # Exit the App event loop
The results object contains the worker’s warnings and errors (it also contains the results of the worker, those will be explained in Part B). This completion callback prints out the warnings, errors, and results, then calls sys.exit() to end the App event Loop.
Now that the callbacks are taken care of, let’s look at how an AsyncWorker is started with an
AsyncManager:
1def run_main():
2 # Create an instance of QApplication. This allows us to start a Qt event loop.
3 app = QApplication()
4 # Create the Async Manager
5 manager = AsyncManager()
6 # Create the Worker
7 worker = AsyncWorker()
8 # Connect the Worker's signals to their callbacks
9 worker.signals.started.connect(worker_started_callback)
10 worker.signals.progress.connect(progress_callback)
11 worker.signals.finished.connect(completion_callback)
12 # Start the Worker
13 manager.start_worker(worker)
14 # Start the App Event Loop
15 app.exec()
The general logic is as follows:
Create an instance of
AsyncManagerCreate an instance of the worker
Connect the worker’s signals to their callbacks (line 9-11)
Start the worker by passing it to the manager’s
start_worker()method
This logic is wrapped in a QApplication so that it can run within a Qt event loop. Here’s what the full python script looks like:
1from PySide6.QtWidgets import QApplication
2from PySink import AsyncManager, AsyncWorker
3from PySink import AsyncWorkerProgress, AsyncWorkerResults
4import sys
5
6
7# Function to be called whenever a worker's task has started
8def worker_started_callback(worker_id: str):
9 print(f'Worker with id {worker_id} has started its task\n')
10
11
12# Function to be called whenever progress is updated
13def progress_callback(progress: AsyncWorkerProgress):
14 print(f'Progress Received, value: {progress.value}, message: {progress.message}')
15
16
17# Function to be called when the worker is finished
18def completion_callback(results: AsyncWorkerResults):
19 print(f'\nWorker Complete!')
20 print(f'\tWarnings: {results.warnings}')
21 print(f'\tErrors: {results.errors}')
22 print(f'\tResults: {results.results_dict}')
23 sys.exit() # Exit the App event loop
24
25
26def run_main():
27 # Create an instance of QApplication. This allows us to start a Qt event loop.
28 app = QApplication()
29 # Create the Async Manager
30 manager = AsyncManager()
31 # Create the Worker
32 worker = AsyncWorker()
33 # Connect the Worker's signals to their callbacks
34 worker.signals.started.connect(worker_started_callback)
35 worker.signals.progress.connect(progress_callback)
36 worker.signals.finished.connect(completion_callback)
37 # Start the Worker
38 manager.start_worker(worker)
39 # Start the App Event Loop
40 app.exec()
41
42
43run_main()
After running the script, the following lines will be printed to the console as the worker runs:
1Worker with id 88f864a0-ba66-4b73-9a9a-38437d7225ce has started its task
2
3Progress Received, value: 5, message: Starting
4Progress Received, value: 23.0, message: Step 1
5Progress Received, value: 41.0, message: Step 2
6Progress Received, value: 59.0, message: Step 3
7Progress Received, value: 77.0, message: Step 4
8Progress Received, value: 95.0, message: Step 5
9
10Worker Complete!
11 Warnings: []
12 Errors: []
13 Results: {'demo_result': 'Demo Result Value'}
As indicated in the console output, the worker first fired it’s started signal,
intermittently fired its progress signal as it worked, then finally fired it’s
finished signal when its task was complete. All of this was done in a background
thread which frees up the UI thread to handle user input (if there was one present). In Example 1, we will see
how to actually set up a full PySide Application with PySink, but before that let’s see how to customize an AsyncWorker in
Part B - Defining Custom AsyncWorkers.