Display update after detaching the last panel from a flex grid sizer -- Destroy? Not quite.

After further testing …

The code below is working, as far as it goes, but the OnDelete code is
still not getting rid of everything. I know that because as soon as
I add a new panel (with additional code of course), the supposedly
deleted panels come back!

What am I still missing here?

Bob

···

Date: Sun, 29 Oct 2006 00:43:45
-0400

To: wxPython users

From: Bob Klahn bobstones@comcast.net

Subject: Fwd: Re: [wxPython-users] Display update after detaching the
last panel from a flex grid sizer – Destroy?

At 09:58 PM 10/28/2006, I > > wrote:

Bob Klahn wrote:

What do I need to do to the
sample code below to cause the display to update after deleting (vial
Panel | Detach leading panel) the last panel?

Detach doesn’t destroy the window, so it is still where it was when the
sizer stopped managing it. If you want it to be totally gone you
need to Destroy() yourself.

Hmmm, then why do all but the last panel disappear from the display when
I detach them?? I haven’t destroyed any of them.

Hmmm, did garbage collection delete all but the last window? If so,
then what was my program holding onto that prevented the last detach
operation from (indirectly) deleting the window?

Here’s my revised demo code, which seems to work, followed by an
additional question or two:


`import wx

class MainFrame(wx.Frame):

def init(self, parent, id, title):

   wx.Frame.__init__(self, parent, -1,

title)

   self.menu = wx.Menu()

   item_DELETE = self.menu.Append(101,

“&Delete leading panel”)

   menuBar = wx.MenuBar()

   menuBar.Append(self.menu,

“&Panel”,)

   self.SetMenuBar(menuBar)


   self.Bind(wx.EVT_MENU,

self.OnDelete, item_DELETE)

   pnl = wx.Panel(self,-1)


   self.mainsizer =

wx.BoxSizer(wx.VERTICAL)

   self.fgs = wx.FlexGridSizer(1, 3, 1,
  1. rows, cols, vgap, hgap

    self.fgs.AddGrowableRow(0)

    self.fgs.AddGrowableCol(0)

    self.fgs.AddGrowableCol(1)

    self.fgs.AddGrowableCol(2)

    self.fgs.Add(ChildPanel(pnl,
    -1,‘win00’), flag=wx.EXPAND)

    self.fgs.Add(ChildPanel(pnl,
    -1,‘win01’), flag=wx.EXPAND)

    self.fgs.Add(ChildPanel(pnl,
    -1,‘win02’), flag=wx.EXPAND)

    pnl.SetSizer(self.fgs)

    self.mainsizer.Add(pnl, 1,
    wx.EXPAND)

self.SetSizer(self.mainsizer)

   self.fgs.Layout()

   self.mainsizer.Layout()

def OnDelete(self,evt):

    """Detach and

destroy flex grid sizer’s leading panel"“”

    sizer_items =

self.fgs.GetChildren()

    win =

sizer_items[0].GetWindow()

self.fgs.Detach(0)

    win.Destroy()


    self.fgs.Layout()

self.mainsizer.Layout()

class ChildPanel(wx.Panel):

def init(self, parent, id, title):

   wx.Panel.__init__(self, parent, -1,

style=wx.RAISED_BORDER)

   pnl = wx.Panel(self,-1)

pnl.SetBackgroundColour(wx.BLUE)

   st_ttl = wx.StaticText(self, -1,

title, size=(20,20)

     ,

style=wx.ALIGN_CENTER|wx.RAISED_BORDER|wx.ST_NO_AUTORESIZE)

st_ttl.SetBackgroundColour(wx.WHITE)

   bs = wx.BoxSizer(wx.VERTICAL)

   bs.Add(st_ttl, 0,

wx.EXPAND)

   bs.Add(pnl, 1, wx.EXPAND)


   self.SetSizer(bs)

   bs.Layout()

if name == ‘main’:

app = wx.App(redirect=False)

frame = MainFrame(None, -1, “Titled-panels-in-frame
detach test”)

frame.Show()

app.MainLoop()

`

(1) Have I coded my OnDelete method the way you would code it?

(2) Since I’m now destroying each child panel, the sizer detach, which
I’ve commented out, appears to be unnecessary. Or would it be good
form to code it anyway?

(3) A more fundamental, OO-novice question: I seemed to need all
those “self”'s above, but I gotta believe there’s a cleaner,
tighter way. ??

Bob

Hi Bob,

The code below is working, as far as it goes, but the OnDelete code is still
not getting rid of everything. I know that because as soon as I add a new
panel (with additional code of course), the supposedly deleted panels come
back!

You should keep the self.fgs.Detach(0) where it is, and not commented
out. Currently, there is no wx.Sizer method that will both detach and
destroy a wxWindow item in a single step, so what you usually do is:

MySizer.Detach(MyWindow) # or MySizer.Detach(windowIndex)
MyWindow.Destroy()
MySizer.Layout()

(1) Have I coded my OnDelete method the way you would code it?

Yes, I don't see anything strange there. You should probably check that:

win = sizer_items[0].GetWindow()

Is really a window or not (it may be another wx.Sizer or a spacer,
which will kill your app).

(2) Since I'm now destroying each child panel, the sizer detach, which I've
commented out, appears to be unnecessary. Or would it be good form to code
it anyway?

You should leave it in the code.

(3) A more fundamental, OO-novice question: I seemed to need all those
"self"'s above, but I gotta believe there's a cleaner, tighter way. ??

No, there isn't, as far as I know. You could use "me" instead of
"self", but it doesn't answer to your question. The fact is, all your
properties like:

- fgs;
- mainsizer;
- menu;

and so on, belong to the class MainFrame. The only way you can recover
them from a method to another method is to store them as a properties
in "self". There are workaround to reduce the number of properties you
need to store. For example:

1) You don't need to store self.menu, as you are not using it anywhere
else. Just use:

menu = wx.Menu()

2) You could avoid to store mainsizer as a MainFrame property. In the
"OnDelete" method, you could do something like:

<snip>
self.fgs.Layout()
self.GetSizer().Layout()

3) In theory, you could also avoid to store fgs as a MainFrame
property, but it begins to be cumbersome:

def OnDelete(self,evt):
        """Detach and destroy flex grid sizer's leading panel"""
        mainsizer = self.GetSizer()
        mainpanel = self.mainsizer.GetChildren()[0].GetWindow()
        fgs = mainpanel.GetSizer()
        sizer_items = fgs.GetChildren()
        win = sizer_items[0].GetWindow()

        fgs.Detach(0)
        win.Destroy()

        fgs.Layout()
        mainsizer.Layout()

Hope this helps.

Andrea.

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

Bob Klahn wrote:

After further testing ...

The code below is working, as far as it goes, but the OnDelete code is still not getting rid of everything. I know that because as soon as I add a new panel (with additional code of course), the supposedly deleted panels come back!

That shouldn't be possible if you're still calling Destroy.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!