Why does this script crash Python on OS X?

classic Classic list List threaded Threaded
6 messages Options
bht
Reply | Threaded
Open this post in threaded view
|

Why does this script crash Python on OS X?

bht
The script below crashes Python on OS X (EPD 7.0-2 & 7.1.2) when the
ComboBox is used. Similar code would also crash python on Windows as
well, but somehow in the process of reducing this from a ~10,000
program to a ~100 line example, the Windows problem went away.

The code reconfigures the contents of a ScrolledWindow. Since on OS X,
the ScrolledWindow will have scrollbars as children, a panel is placed
inside the ScrolledWindow and I use various methods to clear that out,
so that I can place new stuff inside (see comments), but nothing I I
have tried works properly. Is there some extra step that is needed to
safely destroy widgets?

Brian

#================================================================
import wx

def OnSizeType(event):
    w = event.GetEventObject()
    topframe =
w.GetParent().GetParent().GetParent().GetParent().GetParent()
    topframe.Mode = w.GetValue()
    updateDataPanel(topframe)

def updateDataPanel(topframe):
    def TopSizer(topframe):
        topSizer = wx.BoxSizer(wx.HORIZONTAL)
        sizeType = wx.ComboBox(topframe.dataPanel,wx.ID_ANY,
                               value=topframe.Mode,
                               choices=['isotropic','ellipsoidal'],
                               style=wx.CB_READONLY|wx.CB_DROPDOWN)
        sizeType.Bind(wx.EVT_COMBOBOX, OnSizeType)
        topSizer.Add(sizeType)
        return topSizer
    def IsoSizer(topframe):
        isoSizer = wx.BoxSizer(wx.HORIZONTAL)
        sizeVal = wx.TextCtrl(topframe.dataPanel,wx.ID_ANY,
            '1.1',style=wx.TE_PROCESS_ENTER)
        isoSizer.Add(sizeVal,0,wx.ALIGN_CENTER_VERTICAL)
        return isoSizer
    def EllSizeDataSizer(topframe):
        dataSizer = wx.FlexGridSizer(1,6,5,5)
        for Pa,val,ref in zip(['S11','S22','S33','S12','S13','S23'],
                              [5.0,5.1,5.2,5.3,5.4,5.5],
                              [True,] + 5*[False,]):
            sizeVal = wx.TextCtrl(topframe.dataPanel,wx.ID_ANY,'%.3f'%
(val),
                                  style=wx.TE_PROCESS_ENTER)
            dataSizer.Add(sizeVal,0,wx.ALIGN_CENTER_VERTICAL)
        return dataSizer

    if topframe.dataPanel is None:
        topframe.dataPanel = wx.Panel(topframe.dataScroll)
    else:
        # cleanup attempt #1 -- delete sizer contents. Crashes python
on OS X
        topframe.dataPanel.GetSizer().Clear(True)

        # cleanup attempt #2 -- delete panel's children. Crashes
python on OS X
        #topframe.dataPanel.DestroyChildren()

        # cleanup attempt #3 -- delete panel. Crashes python on OS X
        #topframe.dataPanel.Destroy()
        #topframe.dataPanel = wx.Panel(topframe.dataScroll)

        # cleanup attempt #4 -- remove sizer contents. Windows remain
        #topframe.dataPanel.GetSizer().Clear()

        # cleanup attempt #5 -- do nothing here but use deleteOld
option on SetSizer
        # children are not shown, but keep piling up
        #print 'Panel child
count:',len(topframe.dataPanel.GetChildren())

    mainSizer = wx.BoxSizer(wx.VERTICAL)
    if topframe.Mode == 'isotropic':
        isoSizer = wx.BoxSizer(wx.HORIZONTAL)
        isoSizer.Add(TopSizer(topframe),0,wx.ALIGN_CENTER_VERTICAL)
        isoSizer.Add(IsoSizer(topframe),0,wx.ALIGN_CENTER_VERTICAL)
        mainSizer.Add(isoSizer)
    elif topframe.Mode == 'ellipsoidal':
        ellSizer = wx.BoxSizer(wx.HORIZONTAL)
        ellSizer.Add(TopSizer(topframe),0,wx.ALIGN_CENTER_VERTICAL)
        mainSizer.Add(ellSizer)
        mainSizer.Add(EllSizeDataSizer(topframe))

    #topframe.dataPanel.SetSizer(mainSizer,deleteOld=True)
    topframe.dataPanel.SetSizer(mainSizer)
    mainSizer.Fit(topframe.childFrame)
    Size = mainSizer.GetMinSize()
    Size[0] += 40
    Size[1] = max(Size[1],250) + 20
    topframe.dataPanel.SetSize(Size)
    topframe.dataScroll.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
    Size[1] = min(Size[1],450)
    topframe.childFrame.setSizePosLeft(Size)

def UpdatePhaseData(topframe):
    topframe.dataScroll = wx.ScrolledWindow(topframe.childNB)
    topframe.childNB.AddPage(topframe.dataScroll,'Data')
    Texture = wx.ScrolledWindow(topframe.childNB)
    topframe.childNB.AddPage(Texture,'Texture')

    updateDataPanel(topframe)

class ChildFrame(wx.Frame):
    def __init__(self,parent):
        wx.Frame.__init__(self,parent=parent,
                          style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
        self.Show()

    def setSizePosLeft(self,Width):
        clientSize = wx.ClientDisplayRect()
        Width[1] = min(Width[1],clientSize[2]-300)
        Width[0] = max(Width[0],300)
        self.SetSize(Width)
        self.SetPosition(wx.Point(clientSize[2]-
Width[0],clientSize[1]+250))

class GSNoteBook(wx.Notebook):
    def __init__(self, parent):
        wx.Notebook.__init__(self, parent, -1, name='', style=
wx.BK_TOP)
        self.SetSize(parent.GetClientSize())

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title = u'Main window')
        self.childFrame = ChildFrame(parent=self)
        self.childFrame.SetMenuBar(wx.MenuBar())
        self.childFrame.SetLabel('Child Frame')
        self.childFrame.CreateStatusBar()
        self.childNB = GSNoteBook(parent=self.childFrame)
        self.dataScroll = None
        self.dataPanel = None
        self.Mode = 'isotropic'

        UpdatePhaseData(self)

if __name__ == '__main__':
    import sys
    app = wx.PySimpleApp()
    frame = MyFrame()
    frame.Show(True)
    app.MainLoop()


--
To unsubscribe, send email to [hidden email]
or visit http://groups.google.com/group/wxPython-users?hl=en
Reply | Threaded
Open this post in threaded view
|

Re: Why does this script crash Python on OS X?

Andrea Gavana
On 8 December 2011 19:11, bht wrote:

<snip>
>
> #================================================================
> import wx
>
> def OnSizeType(event):
>    w = event.GetEventObject()
>    topframe =
> w.GetParent().GetParent().GetParent().GetParent().GetParent()

^^^^^^

You have got to be kidding... 5 GetParent() in a row is the absolute
record up to now. I would suggest you to use wx.GetTopLevelParent(w)
if "topframe" is an instance of wx.Frame.

Other than that, I am sorry I can't offer a solution as I am not a Mac
person. However, as a recommendation, I would suggest you to post a
small, runnable sample so we may actually be able to run your code and
check for problems.

Andrea.

"Imagination Is The Only Weapon In The War Against Reality."
http://xoomer.alice.it/infinity77/

--
To unsubscribe, send email to [hidden email]
or visit http://groups.google.com/group/wxPython-users?hl=en
Reply | Threaded
Open this post in threaded view
|

Re: Why does this script crash Python on OS X?

Cody Precord
In reply to this post by bht
Hi,

On Thu, Dec 8, 2011 at 12:11 PM, bht <[hidden email]> wrote:

> The script below crashes Python on OS X (EPD 7.0-2 & 7.1.2) when the
> ComboBox is used. Similar code would also crash python on Windows as
> well, but somehow in the process of reducing this from a ~10,000
> program to a ~100 line example, the Windows problem went away.
>
> The code reconfigures the contents of a ScrolledWindow. Since on OS X,
> the ScrolledWindow will have scrollbars as children, a panel is placed
> inside the ScrolledWindow and I use various methods to clear that out,
> so that I can place new stuff inside (see comments), but nothing I I
> have tried works properly. Is there some extra step that is needed to
> safely destroy widgets?
>
> Brian
>
> #================================================================
> import wx
>
> def OnSizeType(event):
>    w = event.GetEventObject()
>    topframe =
> w.GetParent().GetParent().GetParent().GetParent().GetParent()

If your looking for the toplevel window (i.e the parent Frame) This
can be simplifed to this

topframe = w.TopLevelParent


The crash is probably because you are creating most of the widgets as
children of the Frame then adding them to a sizer that belongs to
panel that is a child (or grandchild, ...) of the Frame.

The hierarchy is all out of line, windows should be children of of the
window they will be displayed in/on, and added to the sizers of their
direct parent.

i.e) Pseudo-code

MyFrame
    - MyPanel (child of MyFrame)
      - ComboBox (child of MyPanel)

class MyFrame(wx.Frame):
     def __init__(...):
          super()
          panel = MyPanel(self)
          sizer = wx.BoxSizer()
          sizer.Add(panel)
          self.SetSizer(sizer)

class MyPanel(wx.Panel):
      def __init__(...):
           super()
           combo = wx.ComboBox(self)
           sizer = wx.BoxSizer()
           sizer.Add(combo)
           self.SetSizer(sizer)



Cody

--
To unsubscribe, send email to [hidden email]
or visit http://groups.google.com/group/wxPython-users?hl=en
Reply | Threaded
Open this post in threaded view
|

Re: Why does this script crash Python on OS X?

Chris Barker - NOAA Federal
In reply to this post by Andrea Gavana
On 12/8/11 11:11 AM, Andrea Gavana wrote:

> On 8 December 2011 19:11, bht wrote:
>
> <snip>
>>
>> #================================================================
>> import wx
>>
>> def OnSizeType(event):
>>     w = event.GetEventObject()
>>     topframe =
>> w.GetParent().GetParent().GetParent().GetParent().GetParent()
>
> ^^^^^^
>
> You have got to be kidding... 5 GetParent() in a row is the absolute
> record up to now.

Indeed! this is very fragile coding structure. See the "Law Of Demeter":

http://en.wikipedia.org/wiki/Law_of_Demeter

Your code will be very hard to re-use or re-factor -- a given Window
should not need to know very much at all about where it lives in the
hierarchy of windows.

> I would suggest you to use wx.GetTopLevelParent(w)
> if "topframe" is an instance of wx.Frame.

Even better, have some system for keeping track of application-level
information that your Window can use -- maybe what you need isn't the in
the Top-level frame (or won't be later, when you re-factor)

-Chris


--
Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R            (206) 526-6959   voice
7600 Sand Point Way NE   (206) 526-6329   fax
Seattle, WA  98115       (206) 526-6317   main reception

[hidden email]

--
To unsubscribe, send email to [hidden email]
or visit http://groups.google.com/group/wxPython-users?hl=en
Reply | Threaded
Open this post in threaded view
|

Re: Why does this script crash Python on OS X?

Robin Dunn
In reply to this post by Cody Precord
On 12/8/11 11:21 AM, Cody wrote:

> Hi,
>
> On Thu, Dec 8, 2011 at 12:11 PM, bht<[hidden email]>  wrote:
>> The script below crashes Python on OS X (EPD 7.0-2&  7.1.2) when the
>> ComboBox is used. Similar code would also crash python on Windows as
>> well, but somehow in the process of reducing this from a ~10,000
>> program to a ~100 line example, the Windows problem went away.
>>
>> The code reconfigures the contents of a ScrolledWindow. Since on OS X,
>> the ScrolledWindow will have scrollbars as children, a panel is placed
>> inside the ScrolledWindow and I use various methods to clear that out,
>> so that I can place new stuff inside (see comments), but nothing I I
>> have tried works properly. Is there some extra step that is needed to
>> safely destroy widgets?
>>
>> Brian
>>
>> #================================================================
>> import wx
>>
>> def OnSizeType(event):
>>     w = event.GetEventObject()
>>     topframe =
>> w.GetParent().GetParent().GetParent().GetParent().GetParent()
>
> If your looking for the toplevel window (i.e the parent Frame) This
> can be simplifed to this
>
> topframe = w.TopLevelParent
>
>
> The crash is probably because you are creating most of the widgets as
> children of the Frame then adding them to a sizer that belongs to
> panel that is a child (or grandchild, ...) of the Frame.

Based on the OSX Crash Report I would guess that a ComboBox is being
destroyed while there are still events for it in the event queue.  When
it tries to deliver the events then it accesses a pointer that is no
longer valid.  If you want to ensure that there are no pending events
then you can do the destroy and recreation of widgets in an idle
handler, or via a function called with wx.CallAfter.

>
> The hierarchy is all out of line, windows should be children of of the
> window they will be displayed in/on, and added to the sizers of their
> direct parent.

But mis-parenting of widgets is also a common newbie mistake that can
cause problems like this, so check into that too.  Using the WIT is
helpful for this: http://wiki.wxpython.org/Widget_Inspection_Tool

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

--
To unsubscribe, send email to [hidden email]
or visit http://groups.google.com/group/wxPython-users?hl=en
bht
Reply | Threaded
Open this post in threaded view
|

Re: Why does this script crash Python on OS X?

bht
Thanks for all the comments. For the benefit of anyone stumbling on
this thread with a similar problem, let me note that Robin was right
on the money. Postponing window destruction until there are no pending
events, through use of wx.CallAfter prevents my crash. The importance
of waiting for the idle loop before destroying windows was completely
missed by this newbie. The working version of the script is below.

The value of the WIT (http://wiki.wxpython.org/Widget_Inspection_Tool)
was also something I missed.

In my defense, the highly nested reference to topframe was a quick
kludge used to avoid what might have looked like recursion. I was
concentrating on making a small code for pasting.

Also, if anyone could point out where the child/parent or sizer use
hierarchy is out of line, I would appreciate it. I still don't see any
problems.

#==================================================================
import wx
import wx.lib.inspection

def updateDataPanel(top):
    topframe = top
    def OnSizeType(event):
        w = event.GetEventObject()
        topframe.Mode = w.GetValue()
        wx.CallAfter(updateDataPanel,topframe)
    def TopSizer():
        topSizer = wx.BoxSizer(wx.HORIZONTAL)
        sizeType = wx.ComboBox(topframe.dataPanel,wx.ID_ANY,
                               value=topframe.Mode,
                               choices=['isotropic','ellipsoidal'],
                               style=wx.CB_READONLY|wx.CB_DROPDOWN)
        sizeType.Bind(wx.EVT_COMBOBOX, OnSizeType)
        topSizer.Add(sizeType)
        return topSizer
    def IsoSizer():
        isoSizer = wx.BoxSizer(wx.HORIZONTAL)
        sizeVal = wx.TextCtrl(topframe.dataPanel,wx.ID_ANY,
            '1.1',style=wx.TE_PROCESS_ENTER)
        isoSizer.Add(sizeVal,0,wx.ALIGN_CENTER_VERTICAL)
        return isoSizer
    def EllSizeDataSizer():
        dataSizer = wx.FlexGridSizer(1,6,5,5)
        for Pa,val,ref in zip(['S11','S22','S33','S12','S13','S23'],
                              [5.0,5.1,5.2,5.3,5.4,5.5],
                              [True,] + 5*[False,]):
            sizeVal = wx.TextCtrl(topframe.dataPanel,wx.ID_ANY,
                                  '%.3f'%(val),
                                  style=wx.TE_PROCESS_ENTER)
            dataSizer.Add(sizeVal,0,wx.ALIGN_CENTER_VERTICAL)
        return dataSizer

    if topframe.dataPanel is None:
        topframe.dataPanel = wx.Panel(topframe.dataScroll)
    else:
        topframe.dataPanel.GetSizer().Clear(True)

    mainSizer = wx.BoxSizer(wx.VERTICAL)
    if topframe.Mode == 'isotropic':
        isoSizer = wx.BoxSizer(wx.HORIZONTAL)
        isoSizer.Add(TopSizer(),0,wx.ALIGN_CENTER_VERTICAL)
        isoSizer.Add(IsoSizer(),0,wx.ALIGN_CENTER_VERTICAL)
        mainSizer.Add(isoSizer)
    elif topframe.Mode == 'ellipsoidal':
        ellSizer = wx.BoxSizer(wx.HORIZONTAL)
        ellSizer.Add(TopSizer(),0,wx.ALIGN_CENTER_VERTICAL)
        mainSizer.Add(ellSizer)
        mainSizer.Add(EllSizeDataSizer())

    topframe.dataPanel.SetSizer(mainSizer)
    mainSizer.Fit(topframe.childFrame)
    Size = mainSizer.GetMinSize()
    Size[0] += 40
    Size[1] = max(Size[1],250) + 20
    topframe.dataPanel.SetSize(Size)
    topframe.dataScroll.SetScrollbars(
        10,10,Size[0]/10-4,Size[1]/10-1)
    Size[1] = min(Size[1],450)
    topframe.childFrame.setSizePosLeft(Size)

def UpdatePhaseData(topframe):
    topframe.dataScroll = wx.ScrolledWindow(topframe.childNB)
    topframe.childNB.AddPage(topframe.dataScroll,'Data')
    Texture = wx.ScrolledWindow(topframe.childNB)
    topframe.childNB.AddPage(Texture,'Texture')
    updateDataPanel(topframe)

class ChildFrame(wx.Frame):
    def __init__(self,parent):
        wx.Frame.__init__(
            self,parent=parent,
            style=wx.DEFAULT_FRAME_STYLE ^ wx.CLOSE_BOX)
        self.Show()

    def setSizePosLeft(self,Width):
        clientSize = wx.ClientDisplayRect()
        Width[1] = min(Width[1],clientSize[2]-300)
        Width[0] = max(Width[0],300)
        self.SetSize(Width)
        self.SetPosition(
            wx.Point(clientSize[2]-Width[0],clientSize[1]+250))

class GSNoteBook(wx.Notebook):
    def __init__(self, parent):
        wx.Notebook.__init__(self, parent, -1, name='',
                             style= wx.BK_TOP)
        self.SetSize(parent.GetClientSize())

class MyFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title = u'Main window')
        self.childFrame = ChildFrame(parent=self)
        self.childFrame.SetMenuBar(wx.MenuBar())
        self.childFrame.SetLabel('Child Frame')
        self.childFrame.CreateStatusBar()
        self.childNB = GSNoteBook(parent=self.childFrame)
        self.dataScroll = None
        self.dataPanel = None
        self.Mode = 'isotropic'

        UpdatePhaseData(self)

if __name__ == '__main__':
    import sys
    app = wx.PySimpleApp()
    frame = MyFrame()
    frame.Show(True)
    wx.lib.inspection.InspectionTool().Show()
    app.MainLoop()


--
To unsubscribe, send email to [hidden email]
or visit http://groups.google.com/group/wxPython-users?hl=en