So far, the sizer examples we've shown have been deliberately contrived to display the functionality of the sizers. However, you might be wondering how to use sizers to build a real layout, and we hope the folowing example will give you some ideas. Figure 11.16 shows a moderately complicated layout built with various sizers.
A more realistic sizer example
A more realistic sizer example
The code used to create figure 11.16 is shown in listing 11.11. The code looks complex, but we'll go through it piece by piece.
Listing 11.11 Using sizers to build the address form import wx class TestFrame(wx.Frame):
wx.Frame._init_(self, None, panel = wx.Panel(self)
# First create the controls topLbl = wx.StaticText(panel,
topLbl.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
nameLbl = wx.StaticText(panel, -1, "Name:") name = wx.TextCtrl(panel, -1, "");
addrLbl = wx.StaticText(panel, -1, "Address:") addr1 = wx.TextCtrl(panel, -1, ""); addr2 = wx.TextCtrl(panel, -1, "");
cstLbl = wx.StaticText(panel, city = wx.TextCtrl(panel, -1, state = wx.TextCtrl(panel, -1, zip = wx.TextCtrl(panel, -1,
1, "City, State, Zip:") "", size=(150,-1)); "", size=(50,-1)); "", size=(70,-1));
phoneLbl = wx.StaticText(panel, -1, "Phone:") phone = wx.TextCtrl(panel, -1, "");
emailLbl = wx.StaticText(panel, -1, email = wx.TextCtrl(panel, -1, "");
saveBtn = wx.Button(panel, -1, "Save") cancelBtn = wx.Button(panel, -1, "Cancel")
# Now do the layout.
# mainSizer is the top-level one that manages everything mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(topLbl, 0, wx.ALL, 5) mainSizer.Add(wx.StaticLine(panel), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)
# addrSizer is a grid that holds all of the address info addrSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5) addrSizer.AddGrowableCol(1) addrSizer.Add(nameLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) addrSizer.Add(name, 0, wx.EXPAND) addrSizer.Add(addrLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) addrSizer.Add(addr1, 0, wx.EXPAND) addrSizer.Add((10,10)) # some empty space addrSizer.Add(addr2, 0, wx.EXPAND)
Columns for address
Row with empty space addrSizer.Add(cstLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
# the city, state, zip fields are in a sub-sizer cstSizer = wx.BoxSizer(wx.HORIZONTAL) cstSizer.Add(city, 1)
cstSizer.Add(state, 0, wx.LEFT|wx.RIGHT, 5) cstSizer.Add(zip)
addrSizer.Add(cstSizer, 0, wx.EXPAND)
Nested horizontal addrSizer.Add(phoneLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) addrSizer.Add(phone, 0, wx.EXPAND) addrSizer.Add(emailLbl, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL) addrSizer.Add(email, 0, wx.EXPAND)
# now add the addrSizer to the mainSizer mainSizer.Add(addrSizer, 0, wx.EXPAND|wx.ALL, 10)
Phone and email
Flex sizer added to main
# The buttons sizer will put them in a row with resizeable
# gaps between and on either side of the buttons btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add((2 0,2 0), 1) btnSizer.Add(saveBtn) btnSizer.Add((2 0,2 0), 1) btnSizer.Add(cancelBtn) btnSizer.Add((2 0,2 0), 1)
Button row mainSizer.Add(btnSizer, 0, wx.EXPAND|wx.BOTTOM, 10)
# Fit the frame to the needs of the sizer. The frame will
# automatically resize the panel as needed. Also prevent the
# frame from getting smaller than this size. mainSizer.Fit(self) mainSizer.SetSizeHints(self)
app = wx.PySimpleApp()
O The first part of the code is the creation of the widgets used in the window, which begins at this line. We create them all before adding the sizers.
© The primary sizer in this layout is mainSizer, a vertical box sizer. The first elements added to mainSizer are the top static text label, and a static line.
d The next element in the sizer is addrSizer, which is a flex grid sizer with two columns that is used to hold the rest of the address information. The left column of addrSizer is designed for the static text captions, while the right one gets the text controls. This means that the labels and controls need to be added in an alternating order to keep the grid correct. You can see that nameLbl, name, addrLbl, and addr1 are the first four elements added to the flex grid.
O The next row is different, since the second address line has no caption. In that case, a (10, 10) sized chunk of empty space is added, and then the addr2 control.
Q The next line is different again, as the "City, State, Zip" line take three different text controls, so a horizontal box sizer cstSizer is created. The three controls are added to cstSizer and then the box sizer is added to addrSizer.
® The phone and email lines are added to the flex sizer.
Q The address flex sizer is officially added to the main sizer.
© The button row is added as a horizontal box sizer with some empty spacer elements to separate the buttons. Notice how the spacers are given a proportion of 1 and the buttons are left with the default of 0.
That ends the element layout, after that the sizer is fitted and given a size hint to prevent the frame from getting any smaller.
Before reading the next paragraph or running the example, try to figure out how this frame will respond to growth in the horizontal and vertical dimension.
If the window is resized vertically, none of the elements will move. This is because the main sizer is a vertical box sizer being resized in its primary direction, and none of its top-level elements were added with a proportion greater than zero. If the window is resized horizontally, the main sizer is a vertical box sizer being resized in the secondary direction, and therefore all its elements with the wx.expand flag stretch horizontally. This means the label at the top doesn't grow, but the static line and the subsizers stretch horizontally. The flex grid sizer for the addresses specifies column 1 as growable, meaning that the second column containing the text controls will stretch. Within the "City, State, Zip" row, it is the city element which is given a proportion of 1, and will stretch, while the state and ZIP controls stay the same size. The buttons will stay the same size since they have a proportion of zero, but the empty space before, between, and after them will equally divide up the extra horizontal space since they each have a proportion of 1.
So, if you guessed that the grown window would look like figure 11.17, you were right.
Notice how the elements that we wanted to stretch horizontally have, in fact, stretched, but the widgets are still displayed in basically the same position relative to each other. Despite the stretching, the window still looks good and is still usable.
■ Sizers are a solution to the problem of managing layout in a wxPython program. Rather than manually specifying the size and position of each element in the layout, you can add the elements to a sizer, and the sizer is responsible for placing each element on the screen. Sizers are particularly good at managing layout when the user resizes the frame manually.
■ All wxPython sizers are instances of a subclass of wx.Sizer. To use a sizer, you need to associate it with a container widget. Then, as child widgets are added to the container, you must also add them to the sizer. Finally, you call the sizer's Fit() method to trigger the sizer's algorithm for placement and layout.
■ All sizers start with information about the minimal preferred size for each of its children. Each one uses a different mechanism for placing the widgets, so the same group of widgets will look different when placed inside a different sizer.
■ Perhaps the simplest sizer in wxPython is the grid sizer (wx.GridSizer). In a grid sizer, elements are placed in a two-dimensional grid based on the order in which they are added to the sizer, starting with the top left and moving across and then down to the bottom right. Typically, you set the number of columns in the grid, and the sizer determines how many rows it needs, although you can specify both dimensions if you want.
■ All sizers have various methods for adding widgets to the sizer. Since the order in which children are added to the sizer is important in the final layout, various methods are used to add a new widget to the front, back, or arbitrary spot in the list. When a widget is added to the sizer, other properties can be set which control how the child element changes when the sizer is grown or shrunk. The sizer can also be configured to place a border gap around some or all sides of the object.
Was this article helpful?