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

615 views
Skip to first unread message

david

unread,
Jul 11, 2009, 1:23:51 PM7/11/09
to wxPython-users
Hi,

Not sure where my problem is, but I can duplicate it with the code
below.
Basically i'm trying to use a single wx.Frame that will have widgets
shared between different wx.panels (things like navigation buttons,
etc).
I'm pretty sure i'm doing something wrong because when I attempt to
close the app via self.Close() in the wx.Frame class, I get a python
exception as the app. closes. The exception is a stack trace so I
didnt include it. But here is the code that causes it - could someone
comment on whether i'm doing this incorrectly? I'm pretty sure it has
to do with how i'm tying in the wx.panel because self.Close() doesnt
crash when I do not add the wx.Panel to the wx.Frame:

import wx

class TextTest(wx.Frame):
def __init__(self, parent, id):
wx.Frame.__init__(self, parent, id, 'Text Convertor blah blah blah
BLAH',
style=wx.DEFAULT_FRAME_STYLE ^ (wx.RESIZE_BORDER
| wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX))

self.SetIcon(wx.Icon('icons\\Sidewinder.ico', wx.BITMAP_TYPE_ICO))
self.CenterOnScreen()

#Set common Frame elements for all screens - essentially just the
vertical image and horizontal navigation buttons
vsizer = wx.BoxSizer(wx.VERTICAL)
#horizontral sizer for button menu at bottom of frame
hsizer = wx.BoxSizer(wx.HORIZONTAL)

#horizontal box for bitmap image
bitmapbox = wx.BoxSizer(wx.HORIZONTAL)
sb = wx.StaticBitmap(self, wx.ID_ANY, wx.BitmapFromImage(wx.Image
('icons\\StartTest.png', wx.BITMAP_TYPE_ANY)))
bitmapbox.Add(sb, 0, wx.ALL, 5)
#vertical box for holding main part of panel (rt of the image)
vmainbox = wx.BoxSizer(wx.VERTICAL)

#add horizontal elements
bitmapbox.Add(vmainbox, 0, wx.ALL, 5)
vsizer.Add(bitmapbox, 0, wx.ALL | wx.LEFT, 5)
vsizer.Add(wx.StaticLine(self, wx.ID_ANY), 0, wx.ALL | wx.EXPAND,
5)

#lower panel buttons
about = wx.Button(self, wx.ID_ANY, "About")
next = wx.Button(self, wx.ID_ANY, "Next")
cancel = wx.Button(self, wx.ID_ANY, "Cancel")

hrt_sizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(about, 0, wx.ALL, 5)
hsizer.AddSpacer((435,0))
hrt_sizer.Add(next, 0, wx.ALL, 5)
hrt_sizer.Add(cancel, 0, wx.ALL, 5)
hsizer.Add(hrt_sizer, 0, wx.RIGHT)
vsizer.Add(hsizer, 0, wx.BOTTOM)

self.SetSizerAndFit(vsizer)

self.Bind(wx.EVT_BUTTON, self.OnAboutClick, about)
self.Bind(wx.EVT_BUTTON, self.OnCancelClick, cancel)
self.Bind(wx.EVT_BUTTON, self.OnNextClick, next)

#Global vars for this application window
self.vmainbox = vmainbox
self.vsizer = vsizer
self.next = next

#self.SetSizer(vsizer)

self.genericpanel = GenericPanel(self)
self.panel = self.genericpanel
self.Fit()
self.panel.Show()
self.panel.Layout()


def OnCancelClick(self, evt):
self.Close(True)

def OnAboutClick(self, evt):
info = wx.AboutDialogInfo()
info.Name = NAME
info.Version = VERSION
info.Copyright = COPYRIGHT
info.Description = DESCRIPTION
info.WebSite = WEBSITE
info.License = LICENSE
wx.AboutBox(info)

def OnNextClick(self, evt):
self.panel.Show()

#################################
# Generic Panel
#################################
class GenericPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, wx.ID_ANY)
self.parent = parent
self.__do_layout()

def __do_layout(self):
#vmainbox = wx.BoxSizer(wx.VERTICAL)
#main boxsizer for main part of screen - to the right of the
banner image
mainbox = wx.BoxSizer(wx.HORIZONTAL)
title = wx.StaticText(self, wx.ID_ANY, "Welcome to the TextTool",
(0,0))
title.SetFont(wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD))
mainbox.Add(title, 0, wx.EXPAND, 1)

descwelcome = wx.StaticText(self, wx.ID_ANY, "blah blah text")
descwelcome.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL))
fwtype = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY,
'Selection'), wx.VERTICAL)
fwtype.AddSpacer((10,10))
ckptck = wx.RadioButton(self, wx.ID_ANY, "Selection A",
style=wx.RB_GROUP)
pixck = wx.RadioButton(self, wx.ID_ANY, "Selection B")
fwtype.Add(ckptck, 0, wx.EXPAND, 5)
fwtype.AddSpacer((10,10))
fwtype.Add(pixck, 0, wx.EXPAND, 5)
fwtype.AddSpacer((10,20))

self.parent.vmainbox.Add(mainbox, 0, wx.TOP, 5)
self.parent.vmainbox.Add(descwelcome, 0, wx.TOP, 5)
self.parent.vmainbox.Add(fwtype, 0, wx.TOP | wx.EXPAND, 5)

#self.SetSizerAndFit(
self.SetSizerAndFit(self.parent.vsizer)

#self.Bind(wx.EVT_BUTTON, self.OnNextClick, self.parent.next)

#Panel Radio Buttons for user selection
self.ckptck = ckptck
self.pixck = pixck

def DisplayBurbGrid(self):
self.vmainbox.Hide(self.descwelcome, recursive=True)
self.vmainbox.Hide(self.fwtype, recursive=True)

def OnNextClick(self, evt):
pass


if __name__ =='__main__':
app = wx.PySimpleApp()
frame = TextTest(parent=None, id=-1)
frame.Show()
app.MainLoop()


Thanks!

Robin Dunn

unread,
Jul 11, 2009, 4:57:44 PM7/11/09
to wxPytho...@googlegroups.com
david wrote:
> Hi,
>
> Not sure where my problem is, but I can duplicate it with the code
> below.
> Basically i'm trying to use a single wx.Frame that will have widgets
> shared between different wx.panels (things like navigation buttons,
> etc).
> I'm pretty sure i'm doing something wrong because when I attempt to
> close the app via self.Close() in the wx.Frame class, I get a python
> exception as the app. closes. The exception is a stack trace so I
> didnt include it. But here is the code that causes it - could someone
> comment on whether i'm doing this incorrectly? I'm pretty sure it has
> to do with how i'm tying in the wx.panel because self.Close() doesnt
> crash when I do not add the wx.Panel to the wx.Frame:

> self.SetSizerAndFit(self.parent.vsizer)

Don't do this. Give the panel its own sizer, and in the parent Add()
the panel to the parent's sizer like you do for any other widget located
on the parent. By setting the panel's sizer to be one that is already
used in the parent you are causing the sizer object to be deleted twice
when the windows are destroyed.


--
Robin Dunn
Software Craftsman
http://wxPython.org

david

unread,
Jul 11, 2009, 7:07:20 PM7/11/09
to wxPython-users
Fantastic, thanks Robin, that did the trick and helps make more sense
of the relationship between panel/frame.

David

Karsten Hilbert

unread,
Jul 16, 2009, 11:36:28 AM7/16/09
to wxPytho...@googlegroups.com
* 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

Robin Dunn

unread,
Jul 16, 2009, 3:39:46 PM7/16/09
to wxPytho...@googlegroups.com
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?

Karsten Hilbert

unread,
Jul 16, 2009, 4:55:47 PM7/16/09
to wxPytho...@googlegroups.com
On Thu, Jul 16, 2009 at 12:39:46PM -0700, Robin Dunn wrote:

> > 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 ...

Robin Dunn

unread,
Jul 16, 2009, 5:02:54 PM7/16/09
to wxPytho...@googlegroups.com
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.

Werner F. Bruhin

unread,
Jul 17, 2009, 2:39:28 AM7/17/09
to wxPytho...@googlegroups.com
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

Karsten Hilbert

unread,
Jul 17, 2009, 5:09:05 AM7/17/09
to wxPytho...@googlegroups.com

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

Karsten Hilbert

unread,
Jul 17, 2009, 5:10:45 AM7/17/09
to wxPytho...@googlegroups.com
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.

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

It now closes just fine again.

Thanks,

Mike Driscoll

unread,
Jul 17, 2009, 9:25:02 AM7/17/09
to wxPython-users
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

Robin Dunn

unread,
Jul 17, 2009, 2:18:05 PM7/17/09
to wxPytho...@googlegroups.com

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

Karsten Hilbert

unread,
Jul 17, 2009, 6:17:55 PM7/17/09
to wxPytho...@googlegroups.com
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.

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

Reply all
Reply to author
Forward
0 new messages