Handling a custom modal dialog

Greetings,

I'm working on a program with two main threads of execution: one handles the main GUI-related stuff for WXPython, and the other monitors a local server for certain events. Depending on the event that the monitoring thread receives, it either updates the main UI or has to ask the user to do something, e.g. accept or reject an incoming call. The first scenario works beautifully-- I just call methods of my UI components, e.g. frame.setStatusBar(someMessage). However, the second scenario is proving much trickier.

Essentially, I have created a window subclassing WX.Dialog which holds my prompt and two buttons (Accept and Deny). I want this to open in response to the incoming call event from my non-GUI thread. Obviously, the dialog can't open directly from there as it isn't processing the GUI events. So, I have subclassed a thread which launches the Dialog.ShowModal() method when started, and when a result is received, raises a custom event to be handled by the main GUI thread. What ends up happening, however, is that the dialog pops up, but the app stops responding. I'm positive something is blocking the event queue, but I'm not certain what that could be at this point.

Searching through this archive has yielded similar issues from newcomers to the WX framework, but none quite fit what I am trying to do. I would greatly welcome any leads on what I'm doing wrong as this will be the first of quite a few dialogs I need to run in parallel with the main UI.

Thanks and best regards,
Steve

When you show a Dialog modally, it will block the main application. This is normal. A dialog basically has its own main loop that it run during that time. If you don’t want to block your GUI, then don’t show the dialog modally. Just Show it.

···

Mike Driscoll

Blog: http://blog.pythonlibrary.org

Or are you saying that the dialog stops responding too?

···

Mike Driscoll

Blog: http://blog.pythonlibrary.org

Hi Mike,

That's correct. The dialog appears, but as soon as it is drawn, it locks up. The prompt and buttons I added don't appear. If I do a normal Show(), everything works except that obviously the dialog shows up and then immediately vanishes (hence my thinking that showing modally in a separate thread would work).

Thanks,
Steve

···

On 9/23/2011 3:39 PM, Mike Driscoll wrote:

Or are you saying that the dialog stops responding too?

-------------------
Mike Driscoll

Blog: http://blog.pythonlibrary.org

Are you creating another wx.App instance in your thread? Or are you calling your main GUI thread and having it do it? Are you using a thread-safe method, like wx.PostEvent or wx.CallAfter when you communicate with the GUI thread?

Can you create a small runnable example? See http://wiki.wxpython.org/MakingSampleApps

···

Mike Driscoll

Blog: http://blog.pythonlibrary.org

Hi, Mike.

I am calling wx.PostEvent after I've retrieved the user's choice. I am working on a sample to hopefully demonstrate the problem and will send it along once I have it ready.
Thanks,
Steve

···

On 9/23/2011 4:05 PM, Mike Driscoll wrote:

Are you creating another wx.App instance in your thread? Or are you calling
your main GUI thread and having it do it? Are you using a thread-safe
method, like wx.PostEvent or wx.CallAfter when you communicate with the GUI
thread?

Can you create a small runnable example? See
MakingSampleApps - wxPyWiki

-------------------
Mike Driscoll

Blog: http://blog.pythonlibrary.org

Hi,

···

On Fri, Sep 23, 2011 at 3:48 PM, Stephen Clower <stephen.clower@gmail.com> wrote:

Hi, Mike.

I am calling wx.PostEvent after I've retrieved the user's choice. I am
working on a sample to hopefully demonstrate the problem and will send it
along once I have it ready.

And your retrieving the user choice from a Dialog that your opening on
the other thread?

If so then thats your problem your getting a deadlock by using a UI
object on a non UI thread.

Cody

Hi, Cody.

So, even though the dialog is effectively being launched from within the GUI thread, it will still deadlock? Is the only solution to have separate wx.App instances for each of these dialogs and also run them in their own threads? If so, how is it that the built-in wx.MessageDialogs which wrap the Win32 equivalents don't suffer from this problem?

Thanks for the input.
Steve

···

On 9/23/2011 4:56 PM, Cody wrote:

Hi,

On Fri, Sep 23, 2011 at 3:48 PM, Stephen Clower > <stephen.clower@gmail.com> wrote:

Hi, Mike.

I am calling wx.PostEvent after I've retrieved the user's choice. I am
working on a sample to hopefully demonstrate the problem and will send it
along once I have it ready.

And your retrieving the user choice from a Dialog that your opening on
the other thread?

If so then thats your problem your getting a deadlock by using a UI
object on a non UI thread.

Cody

Hi,

Hi, Cody.

So, even though the dialog is effectively being launched from within the GUI
thread, it will still deadlock? Is the only solution to have separate wx.App
instances for each of these dialogs and also run them in their own threads?
If so, how is it that the built-in wx.MessageDialogs which wrap the Win32
equivalents don't suffer from this problem?

No, from your description it sounded to me like your calling
ShowModal() from a non UI thread. The call to show the dialog must
occur in the context of the UI thread or you can run into deadlock
situations.

You can only have 1 wxApp object per process, so can't create one in
each thread.

So I guess the question is where are you calling ShowModal from?

Cody

···

On Fri, Sep 23, 2011 at 4:28 PM, Stephen Clower <stephen.clower@gmail.com> wrote:

Hi, Cody.

At the moment, ShowModal() is called from the secondary thread so your explanation makes perfect sense. If I call ShowModal in response to something like a menu event directly in the GUI thread, then everything opens and closes as I would expect sans deadlock. However, the problem at hand is that the thread responsible for monitoring the network traffic must some how cause the GUI thread to show the incoming call dialog so that the user can respond to it. My thinking was that if I create a function inside my frame's class definition to display the message that I could then call it from the monitoring thread. My thought process is obviously in error as this doesn't work as I would expect. Does this help, and if so, is what I'm wanting to do possible with this framework?

Thanks,
Steve

···

On 9/23/2011 6:20 PM, Cody wrote:

Hi,

On Fri, Sep 23, 2011 at 4:28 PM, Stephen Clower > <stephen.clower@gmail.com> wrote:

Hi, Cody.

So, even though the dialog is effectively being launched from within the GUI
thread, it will still deadlock? Is the only solution to have separate wx.App
instances for each of these dialogs and also run them in their own threads?
If so, how is it that the built-in wx.MessageDialogs which wrap the Win32
equivalents don't suffer from this problem?

No, from your description it sounded to me like your calling
ShowModal() from a non UI thread. The call to show the dialog must
occur in the context of the UI thread or you can run into deadlock
situations.

You can only have 1 wxApp object per process, so can't create one in
each thread.

So I guess the question is where are you calling ShowModal from?

Cody

Hi,

Hi, Cody.

At the moment, ShowModal() is called from the secondary thread so your
explanation makes perfect sense. If I call ShowModal in response to
something like a menu event directly in the GUI thread, then everything
opens and closes as I would expect sans deadlock. However, the problem at
hand is that the thread responsible for monitoring the network traffic must
some how cause the GUI thread to show the incoming call dialog so that the
user can respond to it. My thinking was that if I create a function inside
my frame's class definition to display the message that I could then call it
from the monitoring thread. My thought process is obviously in error as this
doesn't work as I would expect. Does this help, and if so, is what I'm
wanting to do possible with this framework?

Certainly, you just cant be creating and directly calling on UI
objects from non UI threads.

My understanding is that you want to block on the secondary thread
while waiting for some user input?

If so then you just need to use some thread synchronization technique
while you wait for the UI thread to show the dialog and return a value
(i.e a Semaphore).

There is a useful class and decorator function in the wxPython
Cookbook for doing just this sort of thing :wink: But basically need to do
something like this.

1) Create a semaphore (on background thread)
2) Use CallAfter (or post event) to the UI thread to tell it to show
your dialog. Pass along with it the semaphore object.
3) aquire() the semaphore (on background thread). This will cause the
thread to block.
4) At the end of your routine for showing the dialog on the UI thread
call release() on the semaphore.
5) The background thread will resume execution.

Cody

···

On Fri, Sep 23, 2011 at 5:37 PM, Stephen Clower <stephen.clower@gmail.com> wrote:

Search for wxAnyThread.

···

On 9/23/11 3:51 PM, Cody wrote:

Hi,

On Fri, Sep 23, 2011 at 5:37 PM, Stephen Clower > <stephen.clower@gmail.com> wrote:

Hi, Cody.

At the moment, ShowModal() is called from the secondary thread so your
explanation makes perfect sense. If I call ShowModal in response to
something like a menu event directly in the GUI thread, then everything
opens and closes as I would expect sans deadlock. However, the problem at
hand is that the thread responsible for monitoring the network traffic must
some how cause the GUI thread to show the incoming call dialog so that the
user can respond to it. My thinking was that if I create a function inside
my frame's class definition to display the message that I could then call it
from the monitoring thread. My thought process is obviously in error as this
doesn't work as I would expect. Does this help, and if so, is what I'm
wanting to do possible with this framework?

Certainly, you just cant be creating and directly calling on UI
objects from non UI threads.

My understanding is that you want to block on the secondary thread
while waiting for some user input?

If so then you just need to use some thread synchronization technique
while you wait for the UI thread to show the dialog and return a value
(i.e a Semaphore).

There is a useful class and decorator function in the wxPython
Cookbook for doing just this sort of thing :wink: But basically need to do
something like this.

1) Create a semaphore (on background thread)
2) Use CallAfter (or post event) to the UI thread to tell it to show
your dialog. Pass along with it the semaphore object.
3) aquire() the semaphore (on background thread). This will cause the
thread to block.
4) At the end of your routine for showing the dialog on the UI thread
call release() on the semaphore.
5) The background thread will resume execution.

--
Robin Dunn
Software Craftsman

Thanks, Robin and Cody. WXAnyThread looks like exactly what I need for this project. I will also see about snagging a copy of the WXPython cookbook as I suspect it may hold other samples that I can use to save me some development time.

Best regards,
Steve

···

On 9/23/2011 10:20 PM, Robin Dunn wrote:

On 9/23/11 3:51 PM, Cody wrote:

Hi,

On Fri, Sep 23, 2011 at 5:37 PM, Stephen Clower >> <stephen.clower@gmail.com> wrote:

Hi, Cody.

At the moment, ShowModal() is called from the secondary thread so your
explanation makes perfect sense. If I call ShowModal in response to
something like a menu event directly in the GUI thread, then everything
opens and closes as I would expect sans deadlock. However, the problem at
hand is that the thread responsible for monitoring the network traffic must
some how cause the GUI thread to show the incoming call dialog so that the
user can respond to it. My thinking was that if I create a function inside
my frame's class definition to display the message that I could then call it
from the monitoring thread. My thought process is obviously in error as this
doesn't work as I would expect. Does this help, and if so, is what I'm
wanting to do possible with this framework?

Certainly, you just cant be creating and directly calling on UI
objects from non UI threads.

My understanding is that you want to block on the secondary thread
while waiting for some user input?

If so then you just need to use some thread synchronization technique
while you wait for the UI thread to show the dialog and return a value
(i.e a Semaphore).

There is a useful class and decorator function in the wxPython
Cookbook for doing just this sort of thing :wink: But basically need to do
something like this.

1) Create a semaphore (on background thread)
2) Use CallAfter (or post event) to the UI thread to tell it to show
your dialog. Pass along with it the semaphore object.
3) aquire() the semaphore (on background thread). This will cause the
thread to block.
4) At the end of your routine for showing the dialog on the UI thread
call release() on the semaphore.
5) The background thread will resume execution.

Search for wxAnyThread.