All associated wiki pages:

BoxSizerFromTheGroundUp

BoxSizerFromTheGroundUp/ControlsPadding

BoxSizerFromTheGroundUp/ControlsResizing

BoxSizerFromTheGroundUp/NestedBoxSizers

BoxSizerFromTheGroundUp/NestedControlGroups

BoxSizerFromTheGroundUp/DivideAndConquer

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

DUAL_CONTROL_CLASS_1.PY

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.

DUAL_CONTROL_CLASS_2.PNG

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 :


BoxSizerFromTheGroundUp

BoxSizerFromTheGroundUp/ControlsPadding

BoxSizerFromTheGroundUp/ControlsResizing

BoxSizerFromTheGroundUp/NestedBoxSizers

BoxSizerFromTheGroundUp/NestedControlGroups

BoxSizerFromTheGroundUp/DivideAndConquer

BoxSizerFromTheGroundUp/NestedBoxSizers (last edited 2011-03-26 02:40:07 by pool-71-244-98-82)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.