The main difference to notice here, though, is that we call the object pulled off the
queue, and the producer threads have been generalized to place a success or failure
callback on the queue in response to exits and exceptions. Moreover, the actions that
run in producer threads receive a progress status function which, when called, simply
adds a progress indicator callback to the queue to be dispatched by the main thread.
We can use this, for example, to show progress in the GUI during network downloads.
Example 10-20. PP4E\Gui\Tools\threadtools.py
"""
#################################################################################
System-wide thread interface utilities for GUIs.
Implements a single thread callback queue and checker timer loop shared by
all the windows in a program; worker threads queue their exit and progress
actions to be run in the main thread; this doesn't block the GUI - it just
spawns operations and manages and dispatches exits and progress; worker
threads can overlap freely with the main thread, and with other workers.
Using a queue of callback functions and arguments is more useful than a
simple data queue if there can be many kinds of threads running at the
same time - each kind may have different implied exit actions.
Because GUI API is not completely thread-safe, instead of calling GUI
update callbacks directly after thread main action, place them on a shared
queue, to be run from a timer loop in the main thread, not a child thread;
this also makes GUI update points less random and unpredictable; requires
threads to be split into main action, exit actions, and progress action.
Assumes threaded action raises an exception on failure, and has a 'progress'
callback argument if it supports progress updates; also assumes callbacks
are either short-lived or update as they run, and that queue will contain
callback functions (or other callables) for use in a GUI app - requires a
widget in order to schedule and catch 'after' event loop callbacks; to use
this model in non-GUI contexts, could use simple thread timer instead.
#################################################################################
"""
run even if no threads # in standard lib now
try: # raise ImportError to
import _thread as thread # run with GUI blocking
except ImportError: # if threads not available
import _dummy_thread as thread # same interface, no threads
shared cross-process queue
named in shared global scope, lives in shared object memory
import queue, sys
threadQueue = queue.Queue(maxsize=0) # infinite size
#################################################################################
IN MAIN THREAD - periodically check thread completions queue; run implied GUI
actions on queue in this main GUI thread; one consumer (GUI), and multiple
producers (load, del, send); a simple list may suffice too: list.append and
GUIs, Threads, and Queues | 641