All associated wiki pages:
BoxSizerFromTheGroundUp/NestedBoxSizers |
||
This page's Table of Contents:
Multiple BoxSizers: Nesting
In the following layout it's not possible to group the four controls with a single BoxSizer because they are arranged in a two dimensional pattern. BoxSizers can only stack along one axis. This 2-D pattern is "regular" in that a StaticText and a TextCtrl can be duplicated. This is easily done by subclassing a container control such as a wx.Panel and positioning a StaticText above a TextCtrl.
While the necessary thought process can be done entirely "in your head", it's much more informative to annotate the above sketch to determine exactly all the necessary control groups and spacers.
Your own hand sketches don't need to be nearly as fancy as this, but I want to make sure you understand what I'm trying to do.This drawing can be broken down into its two sizers. You'll be able to figure out how to write the sizer code for each just by looking at the drawings. The filled rectangles are spacers of one sort or another and the empty rectangles represent BozSizers - blue for wx.VERTICAL (think "blue sky": up and down), green for wx.HORIZONTAL (think "green landscape": left and right).
We're ready to start coding now that the details of the controls' nesting and duplication are completely understood.. The first task is to create a StaticText_over_top_a_TextCtrl panel class. Keep in mind that we will want to duplicate this panel, so it will be designed with no hard-coded text.That is, the string value for its StaticText will be passed in using a parameter.
1 class TxtCtrlPanel( wx.Panel ) :
2
3 def __init__( self, parent, caption_txt ) :
4
5 wx.Panel.__init__( self, parent=parent, id=-1 )
6
7 #-----
8
9 self.caption_stTxt = wx.StaticText( self, -1, caption_txt )
10 self.caption_stTxt.BackgroundColour = ( 'wHIte' )
11
12 self.list_txtCtrl = wx.TextCtrl( self, -1, style=wx.TE_MULTILINE, size=(200, 150) )
13
14 #-----
15
16 panel_vertSizer = wx.BoxSizer( wx.VERTICAL )
17
18 panel_vertSizer.Add( self.caption_stTxt, proportion=0, flag=wx.CENTER )
19
20 als.AddLinearSpacer( panel_vertSizer, 2 )
21
22 panel_vertSizer.Add( self.list_txtCtrl, proportion=0, flag=wx.ALIGN_CENTER )
23
24 self.SetSizer( panel_vertSizer ) # Invoke the sizer.
25 self.Fit() # Make "self" (the panel) shrink to the minimum size required by the controls.
26
27 #end __init__
28
29 def GetControls( self ) :
30
31 return self.caption_stTxt, self.list_txtCtrl
32
33 #end TxtCtrlPanel class
Both controls have been .Add()ed with the flag=wx.CENTER parameter ensuring both controls are centered by the sizer. The wider control has no need for this parameter, but which of the two controls will be wider generally can't be known ahead of time.
Line #16 in the following code retrieves the instantiated controls for later use for when when text is inserted into the TextCtrl. Even the StaticText string can be changed if there is a need. The "static" name part in the "StaticText" means that the control is not for interactive use, such as with a TextCtrl, a Button or a CheckBox.
1 class MainFrame( wx.Frame ) :
2 """ A very basic single BoxSizer demo """
3
4 def __init__( self, parent ) :
5
6 wx.Frame.__init__ ( self, parent, -1, title='Simple Single Sizer Demo',
7 size=(400, 300), style=wx.DEFAULT_FRAME_STYLE )
8 self.Position = (100, 0)
9
10 self.SetBackgroundColour( (215, 150, 250) ) # yellow
11 sacrificial_frmCtrl = wx.Panel( self ).Hide()
12
13 #-----
14
15 txtCtl_panel_1 = TxtCtrlPanel( self, 'My TextCtrl Caption #1' )
16 stTxt_1, txtCtl_1 = txtCtl_panel_1.GetControls()
17
18 #-----
19
20 allCtrls_vertSizer = wx.BoxSizer( wx.HORIZONTAL )
21
22 self.SetSizer( allCtrls_vertSizer )
23 self.Layout() # self.Fit() # always use one or the other
24
25 #end __init__
26
27 #end MainFrame class
28
29 #==========================================================
30
31 if __name__ == '__main__' :
32
33 app = wx.PySimpleApp( redirect=False )
34 appFrame = MainFrame( None ).Show()
35 app.MainLoop()
36
37 #end if
The demo app can now be modified to add a second TxtCtrlPanel now that its class is proven to work properly so far. To align the two panels horizontally there needs to be a horizontal sizer added in the Frame class to hold them. Three spacers will also be added to separate the panels from each other and the edges of the page. In addition, the entire page needs to be handled by an overall vertical sizer.
The second panel's caption was changed after the panel was already created with an initial caption string. It has been changed by accessing the StaticText object retrieved in the Frame code. Also note that the width of the right hand StaticText is wider than its TextCtrl while the left hand caption is narrower. This demonstrates why the parameter wx.CENTER or one of its three synonyms needs to be given in every .Add() statement for each control to be centered in the sizer.
1 class MainFrame( wx.Frame ) :
2 """ A 2-control class with a BoxSizer demo """
3
4 def __init__( self ) :
5
6 #----- Configure the Frame.
7
8 wx.Frame.__init__ ( self, None, title='DUAL_CONTROL_CLASS_2.PY',
9 size=(600, 300), style=wx.DEFAULT_FRAME_STYLE )
10 self.Position = (100, 0)
11
12 # A panel is needed for tab-traversal and platform background color capabilities.
13 # The first control instantiated in a Frame automatically expands
14 # to the Frame's client size. This is unique to Frames.
15 frm_pnl = wx.Panel( self )
16 frm_pnl.BackgroundColour = (250, 250, 150) # yellow
17
18 #----- Create the controls
19
20 # Create all the page's controls either individually or using classes.
21
22 ctrlsPanel_1 = TxtCtrlPanel( frm_pnl, 'My TextCtrl Caption #1' )
23 stTxt_1, txtCtl_1 = ctrlsPanel_1.GetControls() # Retrieve the control objects.
24
25 ctrlsPanel_2 = TxtCtrlPanel( frm_pnl, 'My TextCtrl Caption #2' )
26 stTxt_2, txtCtl_2 = ctrlsPanel_2.GetControls()
27
28 # Let's change the original caption "after the fact".
29 stTxt_2.Label = 'Caption Redefined After Panel Class Instantiation'
30 ctrlsPanel_2.LayoutAgain()
31
32 #----- Create the sizers and add the controls to it.
33
34 panelCtrls_horzSizer = wx.BoxSizer( wx.HORIZONTAL )
35
36 als.AddLinearSpacer( panelCtrls_horzSizer, 35 )
37 panelCtrls_horzSizer.Add( ctrlsPanel_1 )
38 als.AddLinearSpacer( panelCtrls_horzSizer, 15 )
39 panelCtrls_horzSizer.Add( ctrlsPanel_2 )
40 als.AddLinearSpacer( panelCtrls_horzSizer, 35 )
41
42 frmPnl_vertSizer = wx.BoxSizer( wx.VERTICAL )
43
44 als.AddLinearSpacer( frmPnl_vertSizer, 35 )
45 frmPnl_vertSizer.Add( panelCtrls_horzSizer )
46
47 als.AddLinearSpacer( frmPnl_vertSizer, 35 )
48
49 #----- Create the sizer and add the controls to it.
50
51 # "There can be only one.", Duncan MacLeod from the clan MacLeod.
52 # That is, a page can have exactly 1 or none top level sizer.
53 # Sizer nesting can effectively have infinite levels.
54 frm_pnl.SetSizer( frmPnl_vertSizer )
55 frm_pnl.Fit()
56
57 #end __init__
58
59 #end MainFrame class
60
61 #==============================================================================
62
63 if __name__ == '__main__' :
64
65 app = wx.PySimpleApp( redirect=False )
66 appFrame = MainFrame().Show()
67 app.MainLoop()
68
69 #end if
1 #-----
2
3 lightbulb_image = wx.Image( 'LIGHTBULB.64x64.PNG', wx.BITMAP_TYPE_ANY)
4 lightbulb_bmap = lightbulb_image.ConvertToBitmap()
5
6 lightbulb_horzSizer = wx.BoxSizer( wx.HORIZONTAL )
7 for idx in xrange( 8 ) :
8 aLightbulb_stBmap = wx.StaticBitmap( self, -1, lightbulb_bmap )
9 lightbulb_horzSizer.Add( aLightbulb_stBmap, flag=wx.CENTER )
10
11 page_vertSizer.Add( lightbulb_horzSizer, flag=wx.ALIGN_CENTRE ) # note synonym
12
13 #-----
Just for fun, I'll throw in some file-based graphic images.
Creating More Complicated Layouts
Adding controls to the page sizer is very easy when it's planned out, especially when control classes for multiple grouped controls are used. I think you can see by now that laying out pages can very quickly become very, very complicated. This brings us to the next topic concerning a technique to handle complexity in general :