[wxPython] Why is this happening???

Hi,

After spending the last couple of hours scratching my head, I've finally
discovered a really odd quirk of wxPython which was confusing the hell out of
me...

I have a wxFrame containing two sub-windows. One of those sub-windows (which
I've called "border") contains a number of further sub-windows which are laid
out using a wxGridSizer (example code attached below, just to clarify what I
mean here).

I'd initially set up my "border" sub-window to be a wxWindow, but I couldn't for
the life of me get the sub-windows within my border to be laid out correctly --
until in a fit of desperation I changed the border from a wxWindow to a
wxPanel. Suddenly, it worked!

Does anyone know why this might be? The wxPython docs for wxPanel don't
mention anything about this rather non-intuitive interaction between
wxPanels/wxWindows and their sizers. Obviously, I think this is something we
should mention in the "Laying out Visual Elements" section of the wxPyWiki --
but I'd like to understand the subtleties properly before I try to write them
up!

Thanks,

- Erik.

···

#############################################################################

# Example program:

from wxPython.wx import *

class TestFrame(wxFrame):
    def __init__(self, parent, id, title):
        wxFrame.__init__(self, parent, id, title)

            leftLabel = wxStaticText(self, -1, "left")

            # The borderSizer doesn't work properly if 'border' is set up to be
            # a wxWindow, like this...

            border = wxWindow(self, -1)

            # ...but it works fine if you do this instead:

            # border = wxPanel(self, -1)

            # Strange!

            borderSizer = wxGridSizer(5, 0, 10, 10)

            for labelText in ["1", "2", "3", "4", "5", "6", "7", "8", "9"]:
                label = wxStaticText(border, 0, labelText)
                borderSizer.Add(label, 0, wxEXPAND)

            border.SetAutoLayout(true)
            border.SetSizer(borderSizer)
            borderSizer.Fit(border)

            sizer = wxBoxSizer(wxHORIZONTAL)
            sizer.Add(leftLabel, 1, wxEXPAND)
            sizer.Add(border, 1, wxEXPAND)
            self.SetAutoLayout(true)
            self.SetSizer(sizer)
            self.SetSizeHints(minW=200, minH=200)
            sizer.Fit(self)

class TestApp(wxApp):
    def OnInit(self):
        frame = TestFrame(NULL, -1, "Test")
        self.SetTopWindow(frame)
        frame.Show(TRUE)
        return TRUE

# Main program:

app = TestApp(0)
app.MainLoop()

#############################################################################

I'd initially set up my "border" sub-window to be a wxWindow, but I

couldn't for

the life of me get the sub-windows within my border to be laid out

correctly --

until in a fit of desperation I changed the border from a wxWindow to a
wxPanel. Suddenly, it worked!

There used to be some text in the docs for wxWindow::SetAutoLayout that
explains this, but somebody has removed it for some reason.

The answer is simple, the default size event handler for wxWindow does not
check the auto layout flag and doesn't call Layout(), the size event
handlers for wxFrame, wxPanel, wxScrolledWindow and others do. The reason
is also fairly simple, only windows expected to contain other windows have
this behaviour by default. If wxWindow did it then all other windows (like
wxButton, wxTreeCtrl, etc.) would either inherit the behaviour or have to
explicitly get rid of it.

Sometimes you do want a wxWindow object to do auto layout, if so it's easy
to add. Just catch EVT_SIZE and do this:

    def OnSize(self, evt):
        if self.GetAutoLayout():
            self.Layout()

···

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

Hi Robin,

There used to be some text in the docs for wxWindow::SetAutoLayout that
explains this, but somebody has removed it for some reason.

The answer is simple, the default size event handler for wxWindow does not
check the auto layout flag and doesn't call Layout(), the size event
handlers for wxFrame, wxPanel, wxScrolledWindow and others do. The reason
is also fairly simple, only windows expected to contain other windows have
this behaviour by default. If wxWindow did it then all other windows (like
wxButton, wxTreeCtrl, etc.) would either inherit the behaviour or have to
explicitly get rid of it.

Ah yes, that does make sense. It's just a pity that the standard docs don't
mention this at all....most confusing if you aren't aware of this behaviour...

Sometimes you do want a wxWindow object to do auto layout, if so it's easy
to add. Just catch EVT_SIZE and do this:

    def OnSize(self, evt):
        if self.GetAutoLayout():
            self.Layout()

Thanks for the detailed explanations! I'll definitely use this when I get to
writing the sizing section for the "Getting Started" guide...

Thanks,

- Erik.