A wxPython Sizers Tutorial

I volunteered to write some tutorials on common GUI layouts in wxPython. The following example came from Malcolm, one of the members of the wxPython user’s group.

Example: Simple data collection form where icon and text are right
justified along the vertical axis of colons.

icon - title
icon - text: - single line input control
icon - text: - single line input control
icon - text: - single line input control
icon - text: - multi-line text/list control that stretches vertically
ok - cancel

First, we’ll create a wx.Frame to contain all the widgets and we’ll create a wx.Panel to “skin” the frame so that it looks normal on all platforms.

import wx

class MyForm(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, title='My Form')

        # Add a panel so it looks correct on all platforms
        self.panel = wx.Panel(self, wx.ID_ANY)

if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = MyForm().Show()

The next step is to figure out how to create an icon. I will be using wx.ArtProvider for the bitmap since it offers generic cross-platform bitmaps/icons and I’ll place the bitmap in a wx.StaticBitmap widget. Check out the sample code below:

bmp = wx.ArtProvider.GetBitmap(wx.ART_INFORMATION, wx.ART_OTHER, (16, 16))
titleIco = wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)

Let’s go over what’s happening here. The first argument to GetBitmap is the art id, the second is the client (such as wx.ART_TOOLBAR or wx.ART_MENU) and the third is the size of the icon. The StaticBitmap’s arguments are pretty much self-explanatory. You can see both in action in the wxPython Demo as well.

Next we’ll put all the code together and stick them in sizers. I’ll be using BoxSizer’s only for this example.

import wx

class MyForm(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, title='My Form') 

        # Add a panel so it looks correct on all platforms
        self.panel = wx.Panel(self, wx.ID_ANY)

        bmp = wx.ArtProvider.GetBitmap(wx.ART_INFORMATION, wx.ART_OTHER, (16, 16))
        titleIco = wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)
        title = wx.StaticText(self.panel, wx.ID_ANY, 'My Title')

        bmp = wx.ArtProvider.GetBitmap(wx.ART_TIP, wx.ART_OTHER, (16, 16))
        inputOneIco = wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)
        labelOne = wx.StaticText(self.panel, wx.ID_ANY, 'Input 1')
        inputTxtOne = wx.TextCtrl(self.panel, wx.ID_ANY, '')

        inputTwoIco = wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)
        labelTwo = wx.StaticText(self.panel, wx.ID_ANY, 'Input 2')
        inputTxtTwo = wx.TextCtrl(self.panel, wx.ID_ANY, '')

        inputThreeIco = wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)
        labelThree = wx.StaticText(self.panel, wx.ID_ANY, 'Input 3')
        inputTxtThree = wx.TextCtrl(self.panel, wx.ID_ANY, '')

        inputFourIco = wx.StaticBitmap(self.panel, wx.ID_ANY, bmp)
        labelFour = wx.StaticText(self.panel, wx.ID_ANY, 'Input 4')
        inputTxtFour = wx.TextCtrl(self.panel, wx.ID_ANY, '')

        okBtn = wx.Button(self.panel, wx.ID_ANY, 'OK')        
        cancelBtn = wx.Button(self.panel, wx.ID_ANY, 'Cancel')
        self.Bind(wx.EVT_BUTTON, self.onOK, okBtn)
        self.Bind(wx.EVT_BUTTON, self.onCancel, cancelBtn)

        topSizer        = wx.BoxSizer(wx.VERTICAL)
        titleSizer      = wx.BoxSizer(wx.HORIZONTAL)
        inputOneSizer   = wx.BoxSizer(wx.HORIZONTAL)
        inputTwoSizer   = wx.BoxSizer(wx.HORIZONTAL)
        inputThreeSizer = wx.BoxSizer(wx.HORIZONTAL)
        inputFourSizer  = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer        = wx.BoxSizer(wx.HORIZONTAL)

        titleSizer.Add(titleIco, 0, wx.ALL, 5)
        titleSizer.Add(title, 0, wx.ALL, 5)
        inputOneSizer.Add(inputOneIco, 0, wx.ALL, 5)
        inputOneSizer.Add(labelOne, 0, wx.ALL, 5)

        inputOneSizer.Add(inputTxtOne, 1, wx.ALL|wx.EXPAND, 5)

        inputTwoSizer.Add(inputTwoIco, 0, wx.ALL, 5)
        inputTwoSizer.Add(labelTwo, 0, wx.ALL, 5)
        inputTwoSizer.Add(inputTxtTwo, 1, wx.ALL|wx.EXPAND, 5)
        inputThreeSizer.Add(inputThreeIco, 0, wx.ALL, 5)
        inputThreeSizer.Add(labelThree, 0, wx.ALL, 5)
        inputThreeSizer.Add(inputTxtThree, 1, wx.ALL|wx.EXPAND, 5)

        inputFourSizer.Add(inputFourIco, 0, wx.ALL, 5)
        inputFourSizer.Add(labelFour, 0, wx.ALL, 5)
        inputFourSizer.Add(inputTxtFour, 1, wx.ALL|wx.EXPAND, 5)

        btnSizer.Add(okBtn, 0, wx.ALL, 5)
        btnSizer.Add(cancelBtn, 0, wx.ALL, 5)

        topSizer.Add(titleSizer, 0, wx.CENTER)
        topSizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 5)
        topSizer.Add(inputOneSizer, 0, wx.ALL|wx.EXPAND, 5)
        topSizer.Add(inputTwoSizer, 0, wx.ALL|wx.EXPAND, 5)
        topSizer.Add(inputThreeSizer, 0, wx.ALL|wx.EXPAND, 5)
        topSizer.Add(inputFourSizer, 0, wx.ALL|wx.EXPAND, 5)
        topSizer.Add(wx.StaticLine(self.panel), 0, wx.ALL|wx.EXPAND, 5)
        topSizer.Add(btnSizer, 0, wx.ALL|wx.CENTER, 5)


    def onOK(self, event):
        # Do something
        print 'onOK handler'
    def onCancel(self, event):

    def closeProgram(self):

# Run the program
if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = MyForm().Show()

If you run this code, you should see something like this:

So, how do these sizers work anyway? Let’s take a look. Here’s the basic style:

mySizer.Add(window, proportion, flag(s), border, userData)

The first argument can be a window/widget, sizer or size. The second is a proportion, which allows the developer to specify how much an item is stretched when the parent window is resized. The third is a flag or series of flags which control alignment, border and resizing. The fourth argument is the border, which is the number of pixels of white space around the widget that’s been added. The final one is userData, which I never use. However, according the “wxPython in Action” book, it’s used for passing in extra data to the sizer for its algorithm.

The three bit flags I use are wx.ALL, wx.EXPAND and wx.CENTER: wx.ALL is used to put x number of pixels on all sides of the widget; wx.EXPAND tells the sizer to allow the widget to expand or stretch when the parent is stretched; wx.CENTER will center the widget horizontally and vertically within the widget.

As you can see, I created a separate BoxSizer for any set of two or more widgets that needed to be aligned next to each other horizontally. I also created one master BoxSizer that was oriented vertically so that I could “stack” the other sizers in it. I also stuck in two wx.StaticLine widgets as separators at the appropriate places.

Finally, I connect the panel to the topSizer using the SetSizer() method. I also decided to use the sizer’s Fit() method to tell the sizer to calculate the size based on the window (i.e. the frame). You could also set it up by setting a minimum size for the containing widget using the SetMinSize() method.

Now you have learned the basics of setting up a form using BoxSizers.

Download: wxPythonSizer Tutorial

Further Reading:

2 thoughts on “A wxPython Sizers Tutorial”

Comments are closed.