app crashes when calling self.Close() on wx.Frame

* Note: this is a looong post *

This is Python 2.5 with wxPython 2.8.7.1 Unicode on Debian/
Testing.

Summary:

I have a similar problem. My application has a single top
level frame. When I close the application (via close box
top-right or via menu item or via keyboard shortcut) it runs
fine through the OnClose() handler of the frame (which is
bound to wx.EVT_CLOSE) but never reaches the OnExit() of
wx.App - it just hangs, no exception, no segfault.

Things tried:

I have removed for good the one global reference the the
frame instance (evil, I know).

I have reduced threading to just the one GUI thread.

None of that helped.

I am using RedirectStdio()/RestoreStdio() in
OnInit()/OnExit(), respectively, if that is of interest.

Data:

This is the backtrace which probably isn't too helpful:

#0 0xb7fed424 in __kernel_vsyscall ()
#1 0xb7efdadb in *__GI___poll (fds=0x91fbf00, nfds=2, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:83
#2 0xb61821bb in g_poll () from /usr/lib/libglib-2.0.so.0
#3 0xb6abe867 in ?? () from /usr/lib/libwx_gtk2u_core-2.8.so.0
#4 0xb6174c42 in ?? () from /usr/lib/libglib-2.0.so.0
#5 0x091fbf00 in ?? ()
#6 0x00000002 in ?? ()
#7 0xffffffff in ?? ()
#8 0x091fbf00 in ?? ()
#9 0x00000002 in ?? ()
#10 0x0960eed8 in ?? ()
#11 0xb61ed558 in ?? () from /usr/lib/libglib-2.0.so.0
#12 0xb61ed580 in ?? () from /usr/lib/libglib-2.0.so.0
#13 0xbf977ae4 in ?? ()
#14 0xb61ed558 in ?? () from /usr/lib/libglib-2.0.so.0
#15 0xb61ed580 in ?? () from /usr/lib/libglib-2.0.so.0
#16 0x08f3f114 in ?? ()
#17 0x00000001 in ?? ()
#18 0x08f3f110 in ?? ()
#19 0x091fbf00 in ?? ()
#20 0xb6abe820 in ?? () from /usr/lib/libwx_gtk2u_core-2.8.so.0
#21 0xb7fbc370 in ?? () from /lib/i686/cmov/libpthread.so.0
#22 0xb7fba890 in ?? () from /lib/i686/cmov/libpthread.so.0
#23 0x08f3f114 in ?? ()
#24 0x00000000 in ?? ()

I did install debugging symbols for libc6/python/wxWidgets.
Running python-dbg complains about the wrong mxDateTime
module being installed (works just fine with non-debug
Python, it's probably just that there's not mxdatetime-dbg
available on Debian).

Any other debugging symbols I need to install ?

The frame's OnClose() does this:

  def OnClose(self, event):
    """This is the wx.EVT_CLOSE handler.

    - framework still functional
    """
    _log.debug('gmTopLevelFrame.OnClose() start')
    self._clean_exit()
    self.Destroy()
    gmLog2.flush()
    _log.debug('gmTopLevelFrame.OnClose() end')
    return True

Again, it runs all the way through - I can the the last
debug statement in the log file. I then hangs *before*
reaching the apps:

  def OnExit(self):
    """Called internally by wxPython after EVT_CLOSE has been handled on last frame.

    - after destroying all application windows and controls
    - before wx.Windows internal cleanup
    """
    print "App OnExit"
    _log.debug('gmApp.OnExit() start')

    self.__shutdown_user_activity_timer(self)
    print "user activity timer stopped"

    if _cfg.get(option = 'debug'):
      self.RestoreStdio()
      sys.stdin = sys.__stdin__
      sys.stdout = sys.__stdout__
      sys.stderr = sys.__stderr__
    _log.debug('gmApp.OnExit() end')

I do NOT see the print statement either in the redirection
window nor on the console neither do I find the log
statement in the log file.

(the sys.__std* dance is probably redundant, right ?)

For what it's worth here is the apps OnInit():

  def OnInit(self):

    self.__starting_up = True

    gmExceptionHandlingWidgets.install_wx_exception_handler()
    gmExceptionHandlingWidgets.set_client_version(_cfg.get(option = 'client_version'))

    _log.info('display: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)))

    # set this so things like "wx.StandardPaths.GetDataDir()" work as expected
    self.SetAppName(u'gnumed')
    self.SetVendorName(u'The GNUmed Development Community.')
    paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
    paths.init_paths(wx = wx, app_name = u'gnumed')

    if not self.__setup_prefs_file():
      return False

    gmExceptionHandlingWidgets.set_sender_email(gmSurgery.gmCurrentPractice().user_email)

    self.__guibroker = gmGuiBroker.GuiBroker()
    self.__setup_platform()

    if not self.__establish_backend_connection():
      return False

    self.__check_for_updates()

    if _cfg.get(option = 'slave'):
      if not self.__setup_scripting_listener():
        return False

    # FIXME: load last position from backend
    frame = gmTopLevelFrame(None, -1, _('GNUmed client'), (640,440))
    frame.CentreOnScreen(wx.BOTH)
    self.SetTopWindow(frame)
    frame.Show(True)

    if _cfg.get(option = 'debug'):
      self.RedirectStdio()
      self.SetOutputWindowAttributes(title = _('GNUmed stdout/stderr window'))
      # print this so people know what this window is for
      # and don't get suprised when it pops up later
      print '---=== GNUmed startup ===---'
      print _('redirecting STDOUT/STDERR to this log window')
      print '---=== GNUmed startup ===---'

    self.__setup_user_activity_timer()
    self.__register_events()

    wx.CallAfter(self._do_after_init)

    return True

And here's the frame's __init__():

  def __init__(self, parent, id, title, size=wx.DefaultSize):

    wx.Frame.__init__(self, parent, id, title, size, style = wx.DEFAULT_FRAME_STYLE)

    self.__gb = gmGuiBroker.GuiBroker()
    self.__pre_exit_callbacks = []
    self.bar_width = -1
    self.menu_id2plugin = {}

    _log.info('workplace is >>>%s<<<', gmSurgery.gmCurrentPractice().active_workplace)

    self.__setup_main_menu()
    self.setup_statusbar()
    self.SetStatusText(_('You are logged in as %s%s.%s (%s). DB account <%s>.') % (
      gmTools.coalesce(_provider['title'], ''),
      _provider['firstnames'][:1],
      _provider['lastnames'],
      _provider['short_alias'],
      _provider['db_user']
    ))

    self.__set_window_title_template()
    self.__update_window_title()
    self.__set_window_icon()

    self.__register_events()

    self.LayoutMgr = gmHorstSpace.cHorstSpaceLayoutMgr(self, -1)
    self.vbox = wx.BoxSizer(wx.VERTICAL)
    self.vbox.Add(self.LayoutMgr, 10, wx.EXPAND | wx.ALL, 1)

    self.SetAutoLayout(True)
    self.SetSizerAndFit(self.vbox)

    self.__set_GUI_size()

Eventually, the full code is here:

  http://cvs.savannah.gnu.org/viewvc/gnumed/gnumed/client/wxpython/gmGuiMain.py?root=gnumed&view=log

Thanks for any insight !

Karsten

···

--
GPG key ID E4071346 @ wwwkeys.pgp.net
E167 67FD A291 2BEA 73BD 4537 78B9 A9F9 E407 1346

Karsten Hilbert wrote:

* Note: this is a looong post *

This is Python 2.5 with wxPython 2.8.7.1 Unicode on Debian/
Testing.

Summary:

I have a similar problem. My application has a single top
level frame. When I close the application (via close box
top-right or via menu item or via keyboard shortcut) it runs
fine through the OnClose() handler of the frame (which is
bound to wx.EVT_CLOSE) but never reaches the OnExit() of
wx.App - it just hangs, no exception, no segfault.

Do you have any frames that were just hidden, not closed and/or destroyed? What about dialogs or taskbar icons that were not destroyed?

···

--
Robin Dunn
Software Craftsman

> This is Python 2.5 with wxPython 2.8.7.1 Unicode on Debian/
> Testing.
>
>
> Summary:
>
> I have a similar problem. My application has a single top
> level frame. When I close the application (via close box
> top-right or via menu item or via keyboard shortcut) it runs
> fine through the OnClose() handler of the frame (which is
> bound to wx.EVT_CLOSE) but never reaches the OnExit() of
> wx.App - it just hangs, no exception, no segfault.

Do you have any frames that were just hidden

I am not aware of a second wx.Frame instance in use. How can
I make sure *that* late in the code ?

not closed and/or destroyed?

Do wx.MiniFrames count ? Ah but then we only use those on
Mac for emulating popup windows which isn't involved here on
a stock PC.

What about dialogs or taskbar icons that were not destroyed?

Taskbar icons aren't in use. Dialogs, hm, any way I can find
out during shutdown of the app ?

The strange thing is this application closed normally until
a few weeks ago and I haven't been able to identify any
suspicious changes. The old version still closes normally so
it is unlikely to be due to the underlying libraries ...

Karsten

···

On Thu, Jul 16, 2009 at 12:39:46PM -0700, Robin Dunn wrote:
--
GPG key ID E4071346 @ wwwkeys.pgp.net
E167 67FD A291 2BEA 73BD 4537 78B9 A9F9 E407 1346

Karsten Hilbert wrote:

not closed and/or destroyed?

Do wx.MiniFrames count ?

Yes.

Ah but then we only use those on
Mac for emulating popup windows which isn't involved here on
a stock PC.

What about dialogs or taskbar icons that were not destroyed?

Taskbar icons aren't in use. Dialogs, hm, any way I can find
out during shutdown of the app ?

Look at the list returned from wx.GetTopLevelWindows(). It should only have one entry, which would be the frame that is closing.

···

--
Robin Dunn
Software Craftsman

Karsten Hilbert wrote:
...

The frame's OnClose() does this:

  def OnClose(self, event):
    """This is the wx.EVT_CLOSE handler.

    - framework still functional
    """
    _log.debug('gmTopLevelFrame.OnClose() start')
    self._clean_exit()
    self.Destroy()
    gmLog2.flush()
    _log.debug('gmTopLevelFrame.OnClose() end')
    return True

Shouldn't there be an "event.Skip()" in this handler? Or is calling self.Destroy() enough?

Werner

It seems sufficient - it used to work w/o one and again
works w/o it.

Karsten

···

On Fri, Jul 17, 2009 at 08:39:28AM +0200, Werner F. Bruhin wrote:

> The frame's OnClose() does this:
>
> def OnClose(self, event):
> """This is the wx.EVT_CLOSE handler.
>
> - framework still functional
> """
> _log.debug('gmTopLevelFrame.OnClose() start')
> self._clean_exit()
> self.Destroy()
> gmLog2.flush()
> _log.debug('gmTopLevelFrame.OnClose() end')
> return True
>
>
Shouldn't there be an "event.Skip()" in this handler? Or is calling
self.Destroy() enough?

--
GPG key ID E4071346 @ wwwkeys.pgp.net
E167 67FD A291 2BEA 73BD 4537 78B9 A9F9 E407 1346

Amazing - there was a dangling dialog from the startup
sequence in the apps OnInit() ...

It now closes just fine again.

Thanks,
Karsten

···

On Thu, Jul 16, 2009 at 02:02:54PM -0700, Robin Dunn wrote:

>> What about dialogs or taskbar icons that were not destroyed?
>
> Taskbar icons aren't in use. Dialogs, hm, any way I can find
> out during shutdown of the app ?

Look at the list returned from wx.GetTopLevelWindows(). It should only
have one entry, which would be the frame that is closing.

--
GPG key ID E4071346 @ wwwkeys.pgp.net
E167 67FD A291 2BEA 73BD 4537 78B9 A9F9 E407 1346

Werner,

Karsten Hilbert wrote:

...> The frame's OnClose() does this:

> def OnClose(self, event):
> """This is the wx.EVT_CLOSE handler.

> - framework still functional
> """
> _log.debug('gmTopLevelFrame.OnClose() start')
> self._clean_exit()
> self.Destroy()
> gmLog2.flush()
> _log.debug('gmTopLevelFrame.OnClose() end')
> return True

Shouldn't there be an "event.Skip()" in this handler? Or is calling
self.Destroy() enough?

Werner

I've done this too and it works fine for me as well without the
event.Skip(). If you want to make your system crash, use self.Close()
(when bound to EVT_CLOSE) so your program calls the event recursively.
I've done that a couple of times before I figured out that I'm still
dumb when it comes to events. The Destroy() seems to short-circuit
this and maybe that's why it doesn't need the Skip().

- Mike

···

On Jul 17, 1:39 am, "Werner F. Bruhin" <wbru...@gmail.com> wrote:

Werner F. Bruhin wrote:

Karsten Hilbert wrote:
...

The frame's OnClose() does this:

  def OnClose(self, event):
    """This is the wx.EVT_CLOSE handler.

    - framework still functional
    """
    _log.debug('gmTopLevelFrame.OnClose() start')
    self._clean_exit()
    self.Destroy()
    gmLog2.flush()
    _log.debug('gmTopLevelFrame.OnClose() end')
    return True

Shouldn't there be an "event.Skip()" in this handler? Or is calling self.Destroy() enough?

It's enough. Calling Destroy is basically all that the default EVT_CLOSE handler does.

···

--
Robin Dunn
Software Craftsman

Is there any approach preferable of the other ? Using
evt.Skip() or calling self.Destroy() myself ?

Karsten

···

On Fri, Jul 17, 2009 at 11:18:05AM -0700, Robin Dunn wrote:

> Karsten Hilbert wrote:
> ...
>> The frame's OnClose() does this:
>>
>> def OnClose(self, event):
>> """This is the wx.EVT_CLOSE handler.
>>
>> - framework still functional
>> """
>> _log.debug('gmTopLevelFrame.OnClose() start')
>> self._clean_exit()
>> self.Destroy()
>> gmLog2.flush()
>> _log.debug('gmTopLevelFrame.OnClose() end')
>> return True
>>
>>
> Shouldn't there be an "event.Skip()" in this handler? Or is calling
> self.Destroy() enough?

It's enough. Calling Destroy is basically all that the default
EVT_CLOSE handler does.

--
GPG key ID E4071346 @ wwwkeys.pgp.net
E167 67FD A291 2BEA 73BD 4537 78B9 A9F9 E407 1346