]> git.saurik.com Git - wxWidgets.git/commitdiff
Adding a copy of sized_controls.py to the wxPython distro so I can use it for develop...
authorKevin Ollivier <kevino@theolliviers.com>
Fri, 17 Nov 2006 16:12:36 +0000 (16:12 +0000)
committerKevin Ollivier <kevino@theolliviers.com>
Fri, 17 Nov 2006 16:12:36 +0000 (16:12 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@43472 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

wxPython/wxaddons/sized_controls.py [new file with mode: 0644]

diff --git a/wxPython/wxaddons/sized_controls.py b/wxPython/wxaddons/sized_controls.py
new file mode 100644 (file)
index 0000000..67a839f
--- /dev/null
@@ -0,0 +1,557 @@
+#----------------------------------------------------------------------
+# Name:        sized_controls.py
+# Purpose:     Implements default, HIG-compliant sizers under the hood
+#              and provides a simple interface for customizing those sizers.
+#
+# Author:      Kevin Ollivier
+#
+# Created:     26-May-2006
+# Copyright:   (c) 2006 Kevin Ollivier
+# Licence:     wxWindows license
+#----------------------------------------------------------------------
+
+import wx
+
+# For HIG info: links to all the HIGs can be found here: 
+# http://en.wikipedia.org/wiki/Human_Interface_Guidelines
+
+
+# useful defines for sizer prop values
+
+halign = {  "left": wx.ALIGN_LEFT,
+            "center": wx.ALIGN_CENTER_HORIZONTAL,
+            "centre": wx.ALIGN_CENTRE_HORIZONTAL,
+            "right": wx.ALIGN_RIGHT,
+         }
+        
+valign = {  "top": wx.ALIGN_TOP,
+            "bottom": wx.ALIGN_BOTTOM,
+            "center": wx.ALIGN_CENTER_VERTICAL,
+            "centre": wx.ALIGN_CENTRE_VERTICAL,
+         }
+         
+align = {   "center": wx.ALIGN_CENTER,
+            "centre": wx.ALIGN_CENTRE, 
+        }
+        
+border = {  "left": wx.LEFT,
+            "right": wx.RIGHT,
+            "top": wx.TOP,
+            "bottom": wx.BOTTOM,
+            "all": wx.ALL,
+         }
+           
+minsize = {   "fixed":    wx.FIXED_MINSIZE,
+              "adjust":   wx.ADJUST_MINSIZE,
+          }
+                
+misc_flags = {   "expand": wx.EXPAND, }
+
+
+# My attempt at creating a more intuitive replacement for nesting box sizers
+class TableSizer(wx.PySizer):
+    def __init__(self, rows=0, cols=0):
+        wx.PySizer.__init__(self)
+        self.rows = rows
+        self.cols = cols
+        self.fixed_width = 0
+        self.fixed_height = 0
+        self.hgrow = 0
+        self.vgrow = 0
+        
+        self.row_widths = []
+        self.col_heights = []
+        
+        # allow us to use 'old-style' proportions when emulating box sizers
+        self.isHorizontal = (self.rows == 1 and self.cols == 0)
+        self.isVertical = (self.cols == 1 and self.rows == 0)
+        
+    def CalcNumRowsCols(self):
+        numrows = self.rows
+        numcols = self.cols
+        numchild = len(self.GetChildren())
+        
+        if numrows == 0 and numcols == 0:
+            return 0, 0
+        
+        if numrows == 0:
+            rows, mod = divmod(numchild, self.cols)
+            if mod > 0:
+                rows += 1
+            numrows = rows
+            
+        if numcols == 0:
+            cols, mod = divmod(numchild, self.rows)
+            if mod > 0:
+                cols += 1
+            numcols = cols
+            
+        return numrows, numcols
+        
+    def CalcMin(self):
+        numrows, numcols = self.CalcNumRowsCols() 
+        numchild = len(self.GetChildren())
+        
+        if numchild == 0:
+            return wx.Size(10, 10)
+            
+        if numrows == 0 and numcols == 0:
+            print "TableSizer must have the number of rows or columns set. Cannot continue."
+            return wx.Size(10, 10)
+       
+        self.row_widths = [0 for x in range(0, numrows)]
+        self.col_heights = [0 for x in range(0, numcols)]
+        currentRow = 0
+        currentCol = 0
+        counter = 0
+        self.hgrow = 0
+        self.vgrow = 0
+
+        # get the max row width and max column height
+        for item in self.GetChildren():            
+            if self.cols != 0:
+                currentRow, currentCol = divmod(counter, numcols)
+            else:
+                currentCol, currentRow = divmod(counter, numrows)
+            
+            if item.IsShown():
+                width, height = item.CalcMin()
+                
+                if self.isVertical and item.GetProportion() > 0:
+                    self.hgrow += item.GetProportion()
+                elif self.isHorizontal and item.GetProportion() > 0:
+                    self.vgrow += item.GetProportion()
+                
+                if width > self.row_widths[currentRow]:
+                    self.row_widths[currentRow] = width
+                
+                if height > self.col_heights[currentCol]:
+                    self.col_heights[currentCol] = height
+            
+            counter += 1
+                
+        minwidth = 0
+        for row_width in self.row_widths:
+            minwidth += row_width
+            
+        minheight = 0
+        for col_height in self.col_heights:
+            minheight += col_height
+                
+        self.fixed_width = minwidth
+        self.fixed_height = minheight
+        
+        return wx.Size(minwidth, minheight)
+        
+    def RecalcSizes(self):
+        numrows, numcols = self.CalcNumRowsCols()
+        numchild = len(self.GetChildren())
+        
+        if numchild == 0:
+            return
+        currentRow = 0
+        currentCol = 0
+        counter = 0
+        
+        print "cols %d, rows %d" % (self.cols, self.rows)
+        print "fixed_height %d, fixed_width %d" % (self.fixed_height, self.fixed_width)
+        #print "self.GetSize() = " + `self.GetSize()`
+        
+        row_widths = [0 for x in range(0, numrows)]
+        col_heights = [0 for x in range(0, numcols)]
+        item_sizes = [0 for x in range(0, len(self.GetChildren()))]
+        grow_sizes = [0 for x in range(0, len(self.GetChildren()))]
+        
+        curHPos = 0
+        curVPos = 0
+        curCol = 0
+        curRow = 0
+        # first, we set sizes for all children, and while doing so, calc
+        # the maximum row heights and col widths. Then, afterwards we handle
+        # the positioning of the controls                
+        
+        for item in self.GetChildren():
+            if self.cols != 0:
+                currentRow, currentCol = divmod(counter, numcols)
+            else:
+                currentCol, currentRow = divmod(counter, numrows)
+            if item.IsShown():
+                item_minsize = item.GetMinSizeWithBorder()
+                width = item_minsize[0]
+                height = item_minsize[1]
+                
+                print "row_height %d, row_width %d" % (self.col_heights[currentCol], self.row_widths[currentRow])
+                growable_width = (self.GetSize()[0]) - width
+                growable_height = (self.GetSize()[1]) - height
+                
+                #if not self.isVertical and not self.isHorizontal:
+                #    growable_width = self.GetSize()[0] - self.row_widths[currentRow]
+                #    growable_height = self.GetSize()[1] - self.col_heights[currentCol]
+                
+                #print "grow_height %d, grow_width %d" % (growable_height, growable_width)
+                
+                item_vgrow = 0
+                item_hgrow = 0
+                # support wx.EXPAND for box sizers to be compatible
+                if item.GetFlag() & wx.EXPAND:
+                    if self.isVertical:
+                        if self.hgrow > 0 and item.GetProportion() > 0:
+                            item_hgrow = (growable_width * item.GetProportion()) / self.hgrow
+                        item_vgrow = growable_height 
+
+                    elif self.isHorizontal: 
+                        if self.vgrow > 0 and item.GetProportion() > 0:
+                            item_vgrow = (growable_height * item.GetProportion()) / self.vgrow
+                        item_hgrow = growable_width
+                        
+                if growable_width > 0 and item.GetHGrow() > 0:
+                    item_hgrow = (growable_width * item.GetHGrow()) / 100
+                    print "hgrow = %d" % (item_hgrow)
+
+                if growable_height > 0 and item.GetVGrow() > 0:
+                    item_vgrow = (growable_height * item.GetVGrow()) / 100
+                    print "vgrow = %d" % (item_vgrow)
+                
+                grow_size = wx.Size(item_hgrow, item_vgrow)
+                size = item_minsize #wx.Size(item_minsize[0] + item_hgrow, item_minsize[1] + item_vgrow) 
+                if size[0] + grow_size[0] > row_widths[currentRow]:
+                    row_widths[currentRow] = size[0] + grow_size[0]
+                if size[1] + grow_size[1] > col_heights[currentCol]:
+                    col_heights[currentCol] = size[1] + grow_size[1] 
+                
+                grow_sizes[counter] = grow_size
+                item_sizes[counter] = size
+            
+            counter += 1
+        
+        counter = 0
+        for item in self.GetChildren():
+            if self.cols != 0:
+                currentRow, currentCol = divmod(counter, numcols)
+            else:
+                currentCol, currentRow = divmod(counter, numrows)
+            
+            itempos = self.GetPosition()
+            if item.IsShown():
+                rowstart = itempos[0] 
+                for row in range(0, currentRow):
+                    rowstart += row_widths[row]
+                    
+                colstart = itempos[1]
+                for col in range(0, currentCol):
+                    #print "numcols = %d, currentCol = %d, col = %d" % (numcols, currentCol, col)
+                    colstart += col_heights[col]
+                    
+                itempos[0] += rowstart
+                itempos[1] += colstart
+                
+                if item.GetFlag() & wx.ALIGN_RIGHT:
+                    itempos[0] += (row_widths[currentRow] - item_sizes[counter][0])
+                elif item.GetFlag() & (wx.ALIGN_CENTER | wx.ALIGN_CENTER_HORIZONTAL):
+                    itempos[0] += (row_widths[currentRow] - item_sizes[counter][0]) / 2
+                
+                if item.GetFlag() & wx.ALIGN_BOTTOM:
+                    itempos[1] += (col_heights[currentCol] - item_sizes[counter][1])
+                elif item.GetFlag() & (wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL):
+                    itempos[1] += (col_heights[currentCol] - item_sizes[counter][1]) / 2
+                
+                hgrowth =  (grow_sizes[counter][0] - itempos[0])
+                if hgrowth > 0:
+                    item_sizes[counter][0] += hgrowth
+                
+                vgrowth =  (grow_sizes[counter][1] - itempos[1])
+                if vgrowth > 0:
+                    item_sizes[counter][1] += vgrowth
+                #item_sizes[counter][1] -= itempos[1]
+                item.SetDimension(itempos, item_sizes[counter])
+                    
+            counter += 1
+
+def GetDefaultBorder(self):
+    border = 4
+    if wx.Platform == "__WXMAC__":
+        border = 6 
+    elif wx.Platform == "__WXMSW__":
+        # MSW HIGs use dialog units, not pixels
+        pnt = self.ConvertDialogPointToPixels(wx.Point(4, 4))
+        border = pnt[0] 
+    elif wx.Platform == "__WXGTK__":
+        border = 3 
+
+    return border
+
+def SetDefaultSizerProps(self):
+    item = self.GetParent().GetSizer().GetItem(self)    
+    item.SetProportion(0)
+    item.SetFlag(wx.ALL)
+    item.SetBorder(self.GetDefaultBorder())
+        
+def GetSizerProps(self):
+    """
+    Returns a dictionary of prop name + value
+    """
+    props = {}
+    item = self.GetParent().GetSizer().GetItem(self)
+    
+    props['proportion'] = item.GetProportion()
+    flags = item.GetFlag()
+
+    if flags & border['all'] == border['all']:
+        props['border'] = (['all'], item.GetBorder())
+    else:
+        borders = []
+        for key in border:
+            if flags & border[key]:
+                borders.append(key)      
+            
+        props['border'] = (borders, item.GetBorder())
+    
+    if flags & align['center'] == align['center']:
+        props['align'] = 'center'
+    else:
+        for key in halign:
+            if flags & halign[key]:
+                props['halign'] = key
+
+        for key in valign:
+            if flags & valign[key]:
+                props['valign'] = key
+    
+    for key in minsize:
+        if flags & minsize[key]:
+            props['minsize'] = key 
+    
+    for key in misc_flags:
+        if flags & misc_flags[key]:
+            props[key] = "true"
+            
+    return props
+
+def SetSizerProp(self, prop, value):
+    
+    lprop = prop.lower()
+    item = self.GetParent().GetSizer().GetItem(self) 
+    flag = item.GetFlag()
+    if lprop == "proportion":
+        item.SetProportion(int(value))
+    elif lprop == "hgrow":
+        item.SetHGrow(int(value))
+    elif lprop == "vgrow":
+        item.SetVGrow(int(value))
+    elif lprop == "align":
+        flag = flag | align[value]
+    elif lprop == "halign":
+        flag = flag | halign[value]
+    elif lprop == "valign":
+        flag = flag | valign[value]
+    elif lprop == "border":
+        # this arg takes a tuple (dir, pixels)
+        dirs, amount = value
+        if dirs == "all":
+            dirs = ["all"]
+        for dir in dirs:
+            flag = flag | border[dir]
+        item.SetBorder(amount)
+    elif lprop == "minsize":
+        flag = flag | minsize[value]
+    elif lprop in misc_flags:
+        if not value or str(value) == "" or str(value).lower() == "false":
+            flag = flag &~ misc_flags[lprop]
+        else:
+            flag = flag | misc_flags[lprop]
+
+    item.SetFlag(flag)
+
+def SetSizerProps(self, props={}, **kwargs):
+    allprops = {}
+    allprops.update(props)
+    allprops.update(kwargs)
+    
+    for prop in allprops:
+        self.SetSizerProp(prop, allprops[prop])
+        
+def GetDialogBorder(self):
+    border = 6
+    if wx.Platform == "__WXMAC__" or wx.Platform == "__WXGTK__":
+        border = 12
+    elif wx.Platform == "__WXMSW__":
+        pnt = self.ConvertDialogPointToPixels(wx.Point(7, 7))
+        border = pnt[0]
+    
+    return border
+
+def SetHGrow(self, proportion):
+    data = self.GetUserData()
+    if "HGrow" in data:
+        data["HGrow"] = proportion
+        self.SetUserData(data) 
+    
+def GetHGrow(self):
+    if self.GetUserData() and "HGrow" in self.GetUserData():
+        return self.GetUserData()["HGrow"]
+    else:
+        return 0
+        
+def SetVGrow(self, proportion):
+    data = self.GetUserData()
+    if "VGrow" in data:
+        data["VGrow"] = proportion
+        self.SetUserData(data) 
+    
+    
+def GetVGrow(self):
+    if self.GetUserData() and "VGrow" in self.GetUserData():
+        return self.GetUserData()["VGrow"]
+    else:
+        return 0
+
+def GetDefaultPanelBorder(self):
+    # child controls will handle their borders, so don't pad the panel.
+    return 0
+
+# Why, Python?! Why do you make it so easy?! ;-)    
+wx.Dialog.GetDialogBorder = GetDialogBorder
+wx.Panel.GetDefaultBorder = GetDefaultPanelBorder
+wx.Notebook.GetDefaultBorder = GetDefaultPanelBorder
+wx.SplitterWindow.GetDefaultBorder = GetDefaultPanelBorder
+
+wx.Window.GetDefaultBorder = GetDefaultBorder
+wx.Window.SetDefaultSizerProps = SetDefaultSizerProps
+wx.Window.SetSizerProp = SetSizerProp
+wx.Window.SetSizerProps = SetSizerProps
+wx.Window.GetSizerProps = GetSizerProps
+
+wx.SizerItem.SetHGrow = SetHGrow
+wx.SizerItem.GetHGrow = GetHGrow
+wx.SizerItem.SetVGrow = SetVGrow
+wx.SizerItem.GetVGrow = GetVGrow
+
+
+class SizedPanel(wx.PyPanel):
+    def __init__(self, *args, **kwargs):
+        wx.PyPanel.__init__(self, *args, **kwargs)
+        sizer = wx.BoxSizer(wx.VERTICAL) #TableSizer(1, 0)
+        self.SetSizer(sizer)
+        self.sizerType = "vertical"
+        
+    def AddChild(self, child):
+        wx.PyPanel.base_AddChild(self, child)
+        
+        sizer = self.GetSizer()
+        item = sizer.Add(child)
+        item.SetUserData({"HGrow":0, "VGrow":0})
+        
+        # Note: One problem is that the child class given to AddChild
+        # is the underlying wxWidgets control, not its Python subclass. So if 
+        # you derive your own class, and override that class' GetDefaultBorder(), 
+        # etc. methods, it will have no effect.  
+        child.SetDefaultSizerProps()
+        
+    def GetSizerType(self):
+        return self.sizerType
+    
+    def SetSizerType(self, type, options={}):
+        sizer = None
+        self.sizerType = type
+        if type == "horizontal":
+            sizer = wx.BoxSizer(wx.HORIZONTAL) # TableSizer(0, 1) 
+        
+        elif type == "vertical":
+            sizer = wx.BoxSizer(wx.VERTICAL) # TableSizer(1, 0)
+        
+        elif type == "form":
+            #sizer = TableSizer(2, 0)
+            sizer = wx.FlexGridSizer(0, 2, 0, 0)
+            sizer.AddGrowableCol(1)
+            
+        elif type == "table":
+            rows = cols = 0
+            if options.has_key('rows'):
+                rows = int(options['rows'])
+
+            if options.has_key('cols'):
+                cols = int(options['cols'])
+        
+            sizer = TableSizer(rows, cols)
+        
+        elif type == "grid":
+            sizer = wx.FlexGridSizer(0, 0, 0, 0)
+            if options.has_key('rows'):
+                sizer.SetRows(int(options['rows']))
+            else:
+                sizer.SetRows(0)
+            if options.has_key('cols'):
+                sizer.SetCols(int(options['cols']))
+            else:
+                sizer.SetCols(0)
+    
+            if options.has_key('growable_row'):
+                row, proportion = options['growable_row']
+                sizer.SetGrowableRow(row, proportion)
+            
+            if options.has_key('growable_col'):
+                col, proportion = options['growable_col']
+                sizer.SetGrowableCol(col, proportion)
+            
+            if options.has_key('hgap'):
+                sizer.SetHGap(options['hgap'])
+                
+            if options.has_key('vgap'):
+                sizer.SetVGap(options['vgap'])
+        if sizer:
+            self._SetNewSizer(sizer)
+                
+    def _SetNewSizer(self, sizer):
+        props = {}
+        for child in self.GetChildren():
+            props[child.GetId()] = child.GetSizerProps()
+            self.GetSizer().Detach(child)
+            
+        wx.PyPanel.SetSizer(self, sizer)
+        
+        for child in self.GetChildren():
+            self.GetSizer().Add(child)
+            child.SetSizerProps(props[child.GetId()])
+        
+class SizedDialog(wx.Dialog):
+    def __init__(self, *args, **kwargs):    
+        wx.Dialog.__init__(self, *args, **kwargs)
+        
+        self.borderLen = 12
+        self.mainPanel = SizedPanel(self, -1)
+        
+        mysizer = wx.BoxSizer(wx.VERTICAL)
+        mysizer.Add(self.mainPanel, 1, wx.EXPAND | wx.ALL, self.GetDialogBorder())
+        self.SetSizer(mysizer)
+        
+        self.SetAutoLayout(True)
+        
+    def GetContentsPane(self):
+        return self.mainPanel
+        
+    def SetButtonSizer(self, sizer):
+        self.GetSizer().Add(sizer, 0, wx.EXPAND | wx.BOTTOM | wx.RIGHT, self.GetDialogBorder())
+        
+        # Temporary hack to fix button ordering problems.
+        cancel = self.FindWindowById(wx.ID_CANCEL)
+        no = self.FindWindowById(wx.ID_NO)
+        if no and cancel:
+            cancel.MoveAfterInTabOrder(no)
+            
+class SizedFrame(wx.Frame):
+    def __init__(self, *args, **kwargs):    
+        wx.Frame.__init__(self, *args, **kwargs)
+        
+        self.borderLen = 12
+        # this probably isn't needed, but I thought it would help to make it consistent
+        # with SizedDialog, and creating a panel to hold things is often good practice.
+        self.mainPanel = SizedPanel(self, -1)
+        
+        mysizer = wx.BoxSizer(wx.VERTICAL)
+        mysizer.Add(self.mainPanel, 1, wx.EXPAND)
+        self.SetSizer(mysizer)
+        
+        self.SetAutoLayout(True)
+        
+    def GetContentsPane(self):
+        return self.mainPanel
\ No newline at end of file