Help creating dialog box

I am a very experienced python programmer, but I've never worked with
wxpython before. I have a client that has a wxpython app that calls
some functions that have their own exception handlers. If an exception
occurs, those functions catch them and return back to the caller. The
caller doesn't know an exception occurred, hence the user is not
notified. My client would like the app modified so that a dialog box
informing the user of the error is displayed. So I modified the code
thusly:

try:
    doWork()
except Exception, info:
    d = wx.MessageDialog(None, str(info), 'Error', wx.OK |
wx.ICON_ERROR)
    d.ShowModal()

Each time I run the program and cause an exception I seem to get
totally different behavior. Sometimes I get the dialog box I want.
Sometimes I get a huge dialog box with a giant OK button. Sometimes I
get a blank dialog box. Sometime I get no dialog box and I get this
error:

(python2.5:9244): Gdk-CRITICAL **: gdk_window_set_geometry_hints:
assertion `GDK_IS_WINDOW (window)' failed
(python2.5:9244): Gdk-CRITICAL **: gdk_window_resize: assertion
`GDK_IS_WINDOW (window)' failed

Sometime I get this error:

Xlib: unexpected async reply (sequence 0x17b5)!

Sometimes I get this:

Xlib: sequence lost (0x1191d > 0x1931) in reply type 0x1c!

Sometimes I get this:

python2.5: cairo-ft-font.c:561: _cairo_ft_unscaled_font_unlock_face:
Assertion `unscaled->lock > 0' failed.
Abort (core dumped)

Can someone help me with this - what is the proper way for me to
create a dialog box in this instance?

TIA!
-larry

At least a couple of the errors make it sound like you are trying to create and use the message dialog from some other thread than the main GUI thread. Is that true? If so then that is your problem, you can not create or manipulate UI objects from other threads. On some platforms it is just a matter of thread safety, on others those objects are actually owned by the thread that creates them and that thread must have its own message loop for them to be able to work properly. So the easy answer is just don't do anything with a UI object from anywhere except the UI thread.

Now the good news is that we have a very easy way to toss stuff over to the UI thread from other worker threads when you need to show a message like this, or update the UI in any other way. You can just move the UI specific stuff into its own function or method, and use wx.CallAfter to invoke that function sometime in the near future. The way it works is that it takes the callable and any args that you give it, puts them into an event, and then posts that event to the pending event queue. The GUI thread will then process that queue when it finishes any current events and then it will call that callable and pass it any args you specified in the wx.CallAfter.

If you need the worker thread to wait for a response then there are ways to do that too, but for the needs you described above wx.CallAfter should do the trick.

···

On 6/11/12 2:56 PM, Larry.Martell@gmail.com wrote:

I am a very experienced python programmer, but I've never worked with
wxpython before. I have a client that has a wxpython app that calls
some functions that have their own exception handlers. If an exception
occurs, those functions catch them and return back to the caller. The
caller doesn't know an exception occurred, hence the user is not
notified. My client would like the app modified so that a dialog box
informing the user of the error is displayed. So I modified the code
thusly:

try:
     doWork()
except Exception, info:
     d = wx.MessageDialog(None, str(info), 'Error', wx.OK |
wx.ICON_ERROR)
     d.ShowModal()

Each time I run the program and cause an exception I seem to get
totally different behavior. Sometimes I get the dialog box I want.
Sometimes I get a huge dialog box with a giant OK button. Sometimes I
get a blank dialog box. Sometime I get no dialog box and I get this
error:

(python2.5:9244): Gdk-CRITICAL **: gdk_window_set_geometry_hints:
assertion `GDK_IS_WINDOW (window)' failed
(python2.5:9244): Gdk-CRITICAL **: gdk_window_resize: assertion
`GDK_IS_WINDOW (window)' failed

Sometime I get this error:

Xlib: unexpected async reply (sequence 0x17b5)!

Sometimes I get this:

Xlib: sequence lost (0x1191d> 0x1931) in reply type 0x1c!

Sometimes I get this:

python2.5: cairo-ft-font.c:561: _cairo_ft_unscaled_font_unlock_face:
Assertion `unscaled->lock> 0' failed.
Abort (core dumped)

Can someone help me with this - what is the proper way for me to
create a dialog box in this instance?

--
Robin Dunn
Software Craftsman

I am a very experienced python programmer, but I've never worked with
wxpython before. I have a client that has a wxpython app that calls
some functions that have their own exception handlers. If an exception
occurs, those functions catch them and return back to the caller. The
caller doesn't know an exception occurred, hence the user is not
notified. My client would like the app modified so that a dialog box
informing the user of the error is displayed. So I modified the code
thusly:

try:
doWork()
except Exception, info:
d = wx.MessageDialog(None, str(info), 'Error', wx.OK |
wx.ICON_ERROR)
d.ShowModal()

Each time I run the program and cause an exception I seem to get
totally different behavior. Sometimes I get the dialog box I want.
Sometimes I get a huge dialog box with a giant OK button. Sometimes I
get a blank dialog box. Sometime I get no dialog box and I get this
error:

(python2.5:9244): Gdk-CRITICAL **: gdk_window_set_geometry_hints:
assertion `GDK_IS_WINDOW (window)' failed
(python2.5:9244): Gdk-CRITICAL **: gdk_window_resize: assertion
`GDK_IS_WINDOW (window)' failed

Sometime I get this error:

Xlib: unexpected async reply (sequence 0x17b5)!

Sometimes I get this:

Xlib: sequence lost (0x1191d> 0x1931) in reply type 0x1c!

Sometimes I get this:

python2.5: cairo-ft-font.c:561: _cairo_ft_unscaled_font_unlock_face:
Assertion `unscaled->lock> 0' failed.
Abort (core dumped)

Can someone help me with this - what is the proper way for me to
create a dialog box in this instance?

At least a couple of the errors make it sound like you are trying to create
and use the message dialog from some other thread than the main GUI thread.
Is that true?

Yes.

If so then that is your problem, you can not create or
manipulate UI objects from other threads. On some platforms it is just a
matter of thread safety, on others those objects are actually owned by the
thread that creates them and that thread must have its own message loop for
them to be able to work properly. So the easy answer is just don't do
anything with a UI object from anywhere except the UI thread.

Now the good news is that we have a very easy way to toss stuff over to the
UI thread from other worker threads when you need to show a message like
this, or update the UI in any other way. You can just move the UI specific
stuff into its own function or method, and use wx.CallAfter to invoke that
function sometime in the near future. The way it works is that it takes the
callable and any args that you give it, puts them into an event, and then
posts that event to the pending event queue. The GUI thread will then
process that queue when it finishes any current events and then it will call
that callable and pass it any args you specified in the wx.CallAfter.

If you need the worker thread to wait for a response then there are ways to
do that too, but for the needs you described above wx.CallAfter should do
the trick.

Way cool! Thanks much Robin. That works. The only small issue now is
that since the dialog box is not tied to the parent, it displays in
the middle of the screen. Is there some way I can get it display on
top of the parent window? (Which admittedly I don't have a handle to
in the function that catches the exception.)

···

On Mon, Jun 11, 2012 at 4:12 PM, Robin Dunn <robin@alldunn.com> wrote:

On 6/11/12 2:56 PM, Larry.Martell@gmail.com wrote:

Could you have access (or give yourself access) to it from the function that is invoked by wx.CallAfter?

If not and if there is only one main frame in the application then you could use wx.GetApp().GetTopWindow() That will return whatever was passed to app.SetTopWindow() or the first item in the top-level window list if SetTopWindow was never called.

OTOH, native message dialogs may not be centered on parent depending on the platform, so you may want to make your own dialog class for that if centering is important.

···

On 6/11/12 3:48 PM, Larry Martell wrote:

On Mon, Jun 11, 2012 at 4:12 PM, Robin Dunn<robin@alldunn.com> wrote:

If you need the worker thread to wait for a response then there are ways to
do that too, but for the needs you described above wx.CallAfter should do
the trick.

Way cool! Thanks much Robin. That works. The only small issue now is
that since the dialog box is not tied to the parent, it displays in
the middle of the screen. Is there some way I can get it display on
top of the parent window? (Which admittedly I don't have a handle to
in the function that catches the exception.)

--
Robin Dunn
Software Craftsman

wx.GetApp().GetTopWindow() worked perfectly! Thanks again!

···

On Mon, Jun 11, 2012 at 5:03 PM, Robin Dunn <robin@alldunn.com> wrote:

On 6/11/12 3:48 PM, Larry Martell wrote:

On Mon, Jun 11, 2012 at 4:12 PM, Robin Dunn<robin@alldunn.com> wrote:

If you need the worker thread to wait for a response then there are ways
to

do that too, but for the needs you described above wx.CallAfter should do
the trick.

Way cool! Thanks much Robin. That works. The only small issue now is
that since the dialog box is not tied to the parent, it displays in
the middle of the screen. Is there some way I can get it display on
top of the parent window? (Which admittedly I don't have a handle to
in the function that catches the exception.)

Could you have access (or give yourself access) to it from the function that
is invoked by wx.CallAfter?

If not and if there is only one main frame in the application then you could
use wx.GetApp().GetTopWindow() That will return whatever was passed to
app.SetTopWindow() or the first item in the top-level window list if
SetTopWindow was never called.

So implementing this has caused another issue. This app asynchronously
talks to other servers. If the other servers exit I end up with many,
many dialog boxes stacked up on each other (one for each outstanding
request there was when the other servers exited). In my function that
is passed to CallAfter() is there some way for me to test to see if
there are any outstanding MessageDialogs, and if so, not create
another one?

···

On Tue, Jun 12, 2012 at 9:41 AM, Larry Martell <larry.martell@gmail.com> wrote:

On Mon, Jun 11, 2012 at 5:03 PM, Robin Dunn <robin@alldunn.com> wrote:

On 6/11/12 3:48 PM, Larry Martell wrote:

On Mon, Jun 11, 2012 at 4:12 PM, Robin Dunn<robin@alldunn.com> wrote:

If you need the worker thread to wait for a response then there are ways
to

do that too, but for the needs you described above wx.CallAfter should do
the trick.

Way cool! Thanks much Robin. That works. The only small issue now is
that since the dialog box is not tied to the parent, it displays in
the middle of the screen. Is there some way I can get it display on
top of the parent window? (Which admittedly I don't have a handle to
in the function that catches the exception.)

Could you have access (or give yourself access) to it from the function that
is invoked by wx.CallAfter?

If not and if there is only one main frame in the application then you could
use wx.GetApp().GetTopWindow() That will return whatever was passed to
app.SetTopWindow() or the first item in the top-level window list if
SetTopWindow was never called.

wx.GetApp().GetTopWindow() worked perfectly! Thanks again!

If you need the worker thread to wait for a response then there are ways
to

do that too, but for the needs you described above wx.CallAfter should do
the trick.

Way cool! Thanks much Robin. That works. The only small issue now is
that since the dialog box is not tied to the parent, it displays in
the middle of the screen. Is there some way I can get it display on
top of the parent window? (Which admittedly I don't have a handle to
in the function that catches the exception.)

Could you have access (or give yourself access) to it from the function that
is invoked by wx.CallAfter?

If not and if there is only one main frame in the application then you could
use wx.GetApp().GetTopWindow() That will return whatever was passed to
app.SetTopWindow() or the first item in the top-level window list if
SetTopWindow was never called.

wx.GetApp().GetTopWindow() worked perfectly! Thanks again!

So implementing this has caused another issue. This app asynchronously
talks to other servers. If the other servers exit I end up with many,
many dialog boxes stacked up on each other (one for each outstanding
request there was when the other servers exited). In my function that
is passed to CallAfter() is there some way for me to test to see if
there are any outstanding MessageDialogs, and if so, not create
another one?

I think I've solved this. What I did was save the handle in a class
variable (that I initialize to None), and then test Enabled, and only
create the dialog is it's False, like this:

def _processException(self, msg):
    if self._modalDialog is None or not self._modalDialog.Enabled:
        self._modalDialog =
wx.MessageDialog(wx.GetApp().GetTopWindow(), str(msg), 'Error', wx.OK

wx.ICON_ERROR)

        self._modalDialog.ShowModal()

This seems to work - I'm getting just one dialog box instead of a
whole bunch like before. Is this a valid and accepted way to
accomplish this?

···

On Tue, Jun 12, 2012 at 1:16 PM, Larry Martell <larry.martell@gmail.com> wrote:

On Tue, Jun 12, 2012 at 9:41 AM, Larry Martell <larry.martell@gmail.com> wrote:

On Mon, Jun 11, 2012 at 5:03 PM, Robin Dunn <robin@alldunn.com> wrote:

On 6/11/12 3:48 PM, Larry Martell wrote:

On Mon, Jun 11, 2012 at 4:12 PM, Robin Dunn<robin@alldunn.com> wrote:

Yes, that is a valid approach. You should also do the following after the ShowModal():

  self._modalDialog.Destroy()
  self._modalDialog = None

···

On 6/12/12 3:36 PM, Larry Martell wrote:

I think I've solved this. What I did was save the handle in a class
variable (that I initialize to None), and then test Enabled, and only
create the dialog is it's False, like this:

def _processException(self, msg):
     if self._modalDialog is None or not self._modalDialog.Enabled:
         self._modalDialog =
wx.MessageDialog(wx.GetApp().GetTopWindow(), str(msg), 'Error', wx.OK
> wx.ICON_ERROR)
         self._modalDialog.ShowModal()

This seems to work - I'm getting just one dialog box instead of a
whole bunch like before. Is this a valid and accepted way to
accomplish this?

--
Robin Dunn
Software Craftsman

Thanks again, Robin.

···

On Wed, Jun 13, 2012 at 11:39 AM, Robin Dunn <robin@alldunn.com> wrote:

On 6/12/12 3:36 PM, Larry Martell wrote:

I think I've solved this. What I did was save the handle in a class
variable (that I initialize to None), and then test Enabled, and only
create the dialog is it's False, like this:

def _processException(self, msg):
if self._modalDialog is None or not self._modalDialog.Enabled:
self._modalDialog =
wx.MessageDialog(wx.GetApp().GetTopWindow(), str(msg), 'Error', wx.OK
> wx.ICON_ERROR)
self._modalDialog.ShowModal()

This seems to work - I'm getting just one dialog box instead of a
whole bunch like before. Is this a valid and accepted way to
accomplish this?

Yes, that is a valid approach. You should also do the following after the
ShowModal():

   self\.\_modalDialog\.Destroy\(\)
   self\.\_modalDialog = None