I have created a module called DelayedResult.py (still prototype, but
works) that helps use multithread in GUI apps, something many novice
users are not comfortable with. It's basic use case is:
- you have a task, triggered by a GUI event, that takes more than
1/10th sec to execute, so you loose GUI response in that time
- starting the task from the event handler actually involves going
through one or more layers with pre-processing at each level; e.g.
handler calls slowTask, which does some setup stuff then itself
calls lowerLevelSlowTask, which starts the thread and returns
immediately
- when the task is done, the result is new data that must be pushed to
the GUI
- once the result is available, each (or at least one) level needs to
do some post-processing before the final result can be sent to GUI
DelayedResult makes it really easy to get the final result back from the
thread to the GUI, without having to know anything about creating new
event types or CallAfter etc. It manages the reverse chain of "callers",
makes it a lot easier to transform a single thread impl to a
multi-thread implementation, and makes it easy to see what is pre- and
post-processing of the slow task. It also limits the amount of
synchronization code needed. You need WxAppTester.py to run the module
test.
Example (pseudo code):
# Single threaded case:
handle_download(self, event):
# get file from web server:
fileObj = serverProxy.get_file( theFile ) # blocks/takes long
ask_user_where_to_save() # post-processing
update_tree_view()
class ServerProxy:
...
get_file(self, theFile):
... pre-processing, e.g. add complete path to theFile ...
fileObj = NetServices.get_file( theFilePath ) # blocks/takes long
... post-processing using fileObj ...
return fileObj
class NetServices:
get_file(self, filePath):
... pre-processing ...
... use urlopen etc... blocks here until got it ...
return fileObj
# Transform for two threaded case using DelayedResult. Note how
# the structure is the same but post- stuff are now split into
# separate functions:
class YourController:
handle_download(self, event):
# get file from web server:
delayedResult = DelayedResult( self.__haveNewResult )
# now returns "immediately" with nothing:
serverProxy.get_file( theFile, delayedResult )
__haveNewResult(self, delayedResult):
fileObj = delayedResult.getResult()
ask_user_where_to_save() # post-processing
update_tree_view()
class ServerProxy:
...
get_file(self, theFile, delayedResult):
... pre-processing, e.g. add complete path to theFile ...
delayedResult.prepend( self.__haveNewResult )
# now returns "immediately" with nothing:
NetServices.get_file( theFilePath, delayedResult )
__haveNewResult(self, delayedResult):
something = delayedResult.getResult()
... post-processing using something ...
return fileObj
class NetServices:
...
get_file(self, filePath, delayedResult):
... pre-processing ...
def threadedFn(delayedResult):
fileObj = urlopen(...) # blocks/takes long
delayedResult.sendResult(fileObj)
thread.start_new(threadedFn, (delayedResult,))
Would it make sense to have it in wx.lib?
Oliver
DelayedResult.py (4.52 KB)
WxAppTester.py (3.18 KB)