widgets get piled up in a sizer

Hello,

I'd be grateful for the help with a problem with a sizer. I want to
place the sizer on a panel which is smaller than the frame.
Unfortunately, all the widgets get positioned in the top left corner,
one over each other, instead of lining up in a grid. Obviously, I'm
doing something wrong but I can't figure out what. Thanks in advance!

Eli Nazarova

import wx

class View(wx.Frame):
    def __init__(self, parent, id):
        wx.Frame.__init__(self, parent, -1, 'Test', size=(400, 400))
        self.panel0 = wx.Panel(self, size=(400, 100), pos=(0,0))
        self.panel0.SetBackgroundColour(wx.BLACK)
        self.panel = wx.Panel(self, size=(400, 300), pos=(0, 100))
        self.panel.SetBackgroundColour(wx.WHITE)
        self.sizer = wx.GridSizer(cols = 2, hgap = 10, vgap=10)
        labels = ['one', 'two', 'three', 'four']
        for label in labels:
            self.sizer.Add(wx.StaticText(self, -1, label), 0)
        self.panel.SetSizer(self.sizer)
        self.TopSizer.Fit(self.panel)
        self.Show()

if __name__ == '__main__':
    app = wx.PySimpleApp()
    view = View(None, -1)
    app.MainLoop()

Hello,

I'd be grateful for the help with a problem with a sizer. I want to
place the sizer on a panel which is smaller than the frame.
Unfortunately, all the widgets get positioned in the top left corner,
one over each other, instead of lining up in a grid. Obviously, I'm
doing something wrong but I can't figure out what. Thanks in advance!
Eli Nazarova

import wx
self.panel0 = wx.Panel(self, size=(400, 100), pos=(0,0))

The first panel (or any window) on a frame--if there is just one as
the frame's child--is automatically expanded to the full size of the
frame. I usually do that and make that the main panel (here, your
self.panel0). Notice how its parent is self. No need to give it a
size and position then, based on that rule of auto-resizing the child
of the frame.

   self\.panel = wx\.Panel\(self, size=\(400, 300\), pos=\(0, 100\)\)

This now has the same parent--self, the frame--as the self.panel0
panel. You don't want that. I think you want that this is the child
of self.panel0. If so, you'd write:

    self.panel = wx.Panel(self.panel0)

Since you are using sizers, you might as well get out of the habit of
providing sizes and positions unless you really need them. Usually
you don't, as in sizer based layouts typically panels are sized
dynamically as a proportion of their parent.

   self\.sizer = wx\.GridSizer\(cols = 2, hgap = 10, vgap=10\)
   labels = \['one', 'two', 'three', 'four'\]
   for label in labels:
       self\.sizer\.Add\(wx\.StaticText\(self, \-1, label\), 0\)

Here you are adding a wx.StaticText to self (the frame itself). You
want to add it to self.panel, your inner panel, like this:

    for label in labels:
        self.sizer.Add(wx.StaticText(self.panel, -1, label), 0)

   self\.panel\.SetSizer\(self\.sizer\)

That's fine. But now you will want to call this, too:

    self.panel.Layout()

Which will force the sizer to layout all the children.

   self\.TopSizer\.Fit\(self\.panel\)

self.TopSizer has not yet been defined, and so this code will not run.
Maybe you meant self.sizer?

HTH,
Che

···

On Fri, Apr 29, 2011 at 9:09 PM, Eli Nazarova <eli.nazarova@gmail.com> wrote:

Yech, sorry about the formatting of my last email; I had the window
expanded in Gmail...won't do that again.

Strike that--it is just that I had text zoomed in Firefox and in that
view the line breaks were bad. Looks fine in normal zoom. Ignore all
this.

···

On Fri, Apr 29, 2011 at 11:09 PM, C M <cmpython@gmail.com> wrote:

Yech, sorry about the formatting of my last email; I had the window
expanded in Gmail...won't do that again.

Your sizer child-parent relationship and your wx.widget child-parent relationship are mismatched. When you see widgets punched up like that here is the first thing you should check:

You add the wx.StaticText widgets as children widgets to the frame but ask the panel’s sizer to layout widgets in the frame. The panel’s sizer is designed to layout the panel’s children widgets on the panel, not the frame.

As CM mentioned;

self.sizer.Add(wx.StaticText(self, -1, label), 0)

should be:

self.sizer.Add(wx.StaticText(self.panel, -1, label), 0)

Said another way.

If you want the frame to place and size (layout) its direct children widgets you need to add a frame sizer and then add the frame’s children widgets to the frame’s sizer.

If you want the panel to place and size (layout) its direct children widgets you need to add a panel sizer and then add the panel’s children widgets to the panel’s sizer.

If the frame has one and only one child widget, the frame fills its client area with that widget and you don’t need to create a sizer for the frame (I believe).

If a panel has one and only one child widget - I don’t know if the panel autosizes the child widget to fill it’s entire area. I don’t think so. I think the child widget is sized by size=() parameter.

If the frame widget has more then one child widget, such as multiple panels or StaticText controls you should add a sizer to the frame. Then add those child widgets to the frame’s sizer. This is not an all-to-common practice. Or you could do absolute positioning (not good).

Thank you, Che, Dev Player, it works now!

···

On 4/29/2011 11:08 PM, C M wrote:

On Fri, Apr 29, 2011 at 9:09 PM, Eli Nazarova<eli.nazarova@gmail.com> wrote:

Hello,

I'd be grateful for the help with a problem with a sizer. I want to
place the sizer on a panel which is smaller than the frame.
Unfortunately, all the widgets get positioned in the top left corner,
one over each other, instead of lining up in a grid. Obviously, I'm
doing something wrong but I can't figure out what. Thanks in advance!
Eli Nazarova

import wx
        self.panel0 = wx.Panel(self, size=(400, 100), pos=(0,0))

The first panel (or any window) on a frame--if there is just one as
the frame's child--is automatically expanded to the full size of the
frame. I usually do that and make that the main panel (here, your
self.panel0). Notice how its parent is self. No need to give it a
size and position then, based on that rule of auto-resizing the child
of the frame.

        self.panel = wx.Panel(self, size=(400, 300), pos=(0, 100))

This now has the same parent--self, the frame--as the self.panel0
panel. You don't want that. I think you want that this is the child
of self.panel0. If so, you'd write:

     self.panel = wx.Panel(self.panel0)

Since you are using sizers, you might as well get out of the habit of
providing sizes and positions unless you really need them. Usually
you don't, as in sizer based layouts typically panels are sized
dynamically as a proportion of their parent.

        self.sizer = wx.GridSizer(cols = 2, hgap = 10, vgap=10)
        labels = ['one', 'two', 'three', 'four']
        for label in labels:
            self.sizer.Add(wx.StaticText(self, -1, label), 0)

Here you are adding a wx.StaticText to self (the frame itself). You
want to add it to self.panel, your inner panel, like this:

     for label in labels:
         self.sizer.Add(wx.StaticText(self.panel, -1, label), 0)

        self.panel.SetSizer(self.sizer)

That's fine. But now you will want to call this, too:

     self.panel.Layout()

Which will force the sizer to layout all the children.

        self.TopSizer.Fit(self.panel)

self.TopSizer has not yet been defined, and so this code will not run.
  Maybe you meant self.sizer?

HTH,
Che