Tim van der Leeuw wrote:
Steven Watanabe wrote:
We've got multiple threads in our application, the gui thread
and worker threads. A worker thread wants to display a modal
dialog. I've read on this list that you can't call a wx gui
function in a non-gui thread. But is it safe to create a wx
MessageDialog in a worker thread, pass it to the gui thread and
let the gui thread call ShowModal(), using a Python semaphore to
block the worker thread until the result is available?
If it isn't, is there a recommended way to accomplish this?
Thanks. -Steven.
I would just pass a message to the gui-thread that you want it to
show a dialog using wx.CallAfter (which is thread-safe) and pubsub
(which isn't).
Well, but then how to get an answer back from the GUI thread, back to
the Worker thread? That's what I solved, for my program, using the
Event() object and setting extra properties on that Event object.
The Worker Thread waits until the GUI thread calls event.set(), and then
reads out the results from the Event object.
Regards,
--Tim
Other possibilities:
- If a sender in a thread wants *one* result from a listener in main
thread, the simplest is to use wxCallAfter twice, and not use pubsub at
all: wxCallAfter used once to send the query to listener, and listener
calls wxCallAfter when result available. The disadvantage of this is
sender must know about listener, AND listener must know about sender.
The latter coupling is easy to break: sender, when doing the wxCallAfter
function call, also passes a reference to itself (or to a method of
itself where result should go). Pseudo-ishly:
# main thread:
def guiListener(arg1, resultCB):
show modal dialog box, get result
resultCB(result)
# worker thread: ask for result, wait until we get it
self.haveResult = False
wxCallAfter(guiListener, arg1, self.storeResult) # ***
while not self.haveResult: sleep(1)
use result
# worker method; will be called by guiListener from main thread:
def storeResult(self, result):
self.result = result
self.haveResult = True
- If you also want sender to not know who to ask for a result, you could
use pubsub.sendMesage instead of guiListener in line ***:
wxCallAfter(pubsub.sendMessage, "newDataAvailable",
arg1=arg1, resultCB=self)
which will call pubsub.sendMessage from main thread, for topic
"newDataAvailable" and given topic message arguments. There could be
more than one listener that will get that message, doesn't matter; but
if more than one listener calls the result callback, the sender must be
able to handle multiple results (or have it just use the latest etc).
I don't see much point in publishing the dialog result from main thread
unless there are several parts of your code that would be interested in
the result. In this case guiListener can use pubsub.sendMessage, which
would call storeResult in main thread, just like the first method does.
It may be necessary to mutex the read/write of self.haveResult (nothing
specific to pubsub) but in a lot of cases you won't even need that.
Note that a more pubsub-ish design of the worker thread would be "a
worker thread wants to know something from user"; it shouldn't care
whether it's via a dialog box or console shell window or from a cached
answer in a file etc. It just wants an answer!
Oliver
···
On Tue, Aug 19, 2008 at 4:44 PM, Mike Driscoll <mike@pythonlibrary.org > <mailto:mike@pythonlibrary.org>> wrote: