pop atomic?; 4E: runs at most N actions per timer event: looping through all
queued callbacks on each timer event may block GUI indefinitely, but running
only one can take a long time or consume CPU for timer events (e.g., progress);
assumes callback is either short-lived or updates display as it runs: after a
callback run, the code here reschedules and returns to event loop and updates;
because this perpetual loop runs in main thread, does not stop program exit;
#################################################################################
def threadChecker(widget, delayMsecs=100, perEvent=1): # 10x/sec, 1/timer
for i in range(perEvent): # pass to set speed
try:
(callback, args) = threadQueue.get(block=False) # run <= N callbacks
except queue.Empty:
break # anything ready?
else:
callback(*args) # run callback here
widget.after(delayMsecs, # reset timer event
lambda: threadChecker(widget, delayMsecs, perEvent)) # back to event loop
#################################################################################
IN A NEW THREAD - run action, manage thread queue puts for exits and progress;
run action with args now, later run on* calls with context; calls added to
queue here are dispatched in main thread only, to avoid parallel GUI updates;
allows action to be fully ignorant of use in a thread here; avoids running
callbacks in thread directly: may update GUI in thread, since passed func in
shared memory called in thread; progress callback just adds callback to queue
with passed args; don't update in-progress counters here: not finished till
exit actions taken off queue and dispatched in main thread by threadChecker;
#################################################################################
def threaded(action, args, context, onExit, onFail, onProgress):
try:
if not onProgress: # wait for action in this thread
action(args) # assume raises exception if fails
else:
def progress(any):
threadQueue.put((onProgress, any + context))
action(progress=progress, *args)
except:
threadQueue.put((onFail, (sys.exc_info(), ) + context))
else:
threadQueue.put((onExit, context))
def startThread(action, args, context, onExit, onFail, onProgress=None):
thread.start_new_thread(
threaded, (action, args, context, onExit, onFail, onProgress))
#################################################################################
a thread-safe counter or flag: useful to avoid operation overlap if threads
update other shared state beyond that managed by the thread callback queue
#################################################################################
642 | Chapter 10: GUI Coding Techniques