]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wxaddons/sized_controls.py
   1 #---------------------------------------------------------------------- 
   2 # Name:        sized_controls.py 
   3 # Purpose:     Implements default, HIG-compliant sizers under the hood 
   4 #              and provides a simple interface for customizing those sizers. 
   6 # Author:      Kevin Ollivier 
   9 # Copyright:   (c) 2006 Kevin Ollivier 
  10 # Licence:     wxWindows license 
  11 #---------------------------------------------------------------------- 
  15 # For HIG info: links to all the HIGs can be found here:  
  16 # http://en.wikipedia.org/wiki/Human_Interface_Guidelines 
  19 # useful defines for sizer prop values 
  21 halign 
= {  "left": wx
.ALIGN_LEFT
, 
  22             "center": wx
.ALIGN_CENTER_HORIZONTAL
, 
  23             "centre": wx
.ALIGN_CENTRE_HORIZONTAL
, 
  24             "right": wx
.ALIGN_RIGHT
, 
  27 valign 
= {  "top": wx
.ALIGN_TOP
, 
  28             "bottom": wx
.ALIGN_BOTTOM
, 
  29             "center": wx
.ALIGN_CENTER_VERTICAL
, 
  30             "centre": wx
.ALIGN_CENTRE_VERTICAL
, 
  33 align 
= {   "center": wx
.ALIGN_CENTER
, 
  34             "centre": wx
.ALIGN_CENTRE
,  
  37 border 
= {  "left": wx
.LEFT
, 
  44 minsize 
= {   "fixed":    wx
.FIXED_MINSIZE
, 
  45               "adjust":   wx
.ADJUST_MINSIZE
, 
  48 misc_flags 
= {   "expand": wx.EXPAND, }
 
  51 # My attempt at creating a more intuitive replacement for nesting box sizers 
  52 class TableSizer(wx
.PySizer
): 
  53     def __init__(self
, rows
=0, cols
=0): 
  54         wx
.PySizer
.__init
__(self
) 
  65         # allow us to use 'old-style' proportions when emulating box sizers 
  66         self
.isHorizontal 
= (self
.rows 
== 1 and self
.cols 
== 0) 
  67         self
.isVertical 
= (self
.cols 
== 1 and self
.rows 
== 0) 
  69     def CalcNumRowsCols(self
): 
  72         numchild 
= len(self
.GetChildren()) 
  74         if numrows 
== 0 and numcols 
== 0: 
  78             rows
, mod 
= divmod(numchild
, self
.cols
) 
  84             cols
, mod 
= divmod(numchild
, self
.rows
) 
  89         return numrows
, numcols
 
  92         numrows
, numcols 
= self
.CalcNumRowsCols()  
  93         numchild 
= len(self
.GetChildren()) 
  96             return wx
.Size(10, 10) 
  98         if numrows 
== 0 and numcols 
== 0: 
  99             print "TableSizer must have the number of rows or columns set. Cannot continue." 
 100             return wx
.Size(10, 10) 
 102         self
.row_widths 
= [0 for x 
in range(0, numrows
)] 
 103         self
.col_heights 
= [0 for x 
in range(0, numcols
)] 
 110         # get the max row width and max column height 
 111         for item 
in self
.GetChildren():             
 113                 currentRow
, currentCol 
= divmod(counter
, numcols
) 
 115                 currentCol
, currentRow 
= divmod(counter
, numrows
) 
 118                 width
, height 
= item
.CalcMin() 
 120                 if self
.isVertical 
and item
.GetProportion() > 0: 
 121                     self
.hgrow 
+= item
.GetProportion() 
 122                 elif self
.isHorizontal 
and item
.GetProportion() > 0: 
 123                     self
.vgrow 
+= item
.GetProportion() 
 125                 if width 
> self
.row_widths
[currentRow
]: 
 126                     self
.row_widths
[currentRow
] = width
 
 128                 if height 
> self
.col_heights
[currentCol
]: 
 129                     self
.col_heights
[currentCol
] = height
 
 134         for row_width 
in self
.row_widths
: 
 135             minwidth 
+= row_width
 
 138         for col_height 
in self
.col_heights
: 
 139             minheight 
+= col_height
 
 141         self
.fixed_width 
= minwidth
 
 142         self
.fixed_height 
= minheight
 
 144         return wx
.Size(minwidth
, minheight
) 
 146     def RecalcSizes(self
): 
 147         numrows
, numcols 
= self
.CalcNumRowsCols() 
 148         numchild 
= len(self
.GetChildren()) 
 156         print "cols %d, rows %d" % (self
.cols
, self
.rows
) 
 157         print "fixed_height %d, fixed_width %d" % (self
.fixed_height
, self
.fixed_width
) 
 158         #print "self.GetSize() = " + `self.GetSize()` 
 160         row_widths 
= [0 for x 
in range(0, numrows
)] 
 161         col_heights 
= [0 for x 
in range(0, numcols
)] 
 162         item_sizes 
= [0 for x 
in range(0, len(self
.GetChildren()))] 
 163         grow_sizes 
= [0 for x 
in range(0, len(self
.GetChildren()))] 
 169         # first, we set sizes for all children, and while doing so, calc 
 170         # the maximum row heights and col widths. Then, afterwards we handle 
 171         # the positioning of the controls                 
 173         for item 
in self
.GetChildren(): 
 175                 currentRow
, currentCol 
= divmod(counter
, numcols
) 
 177                 currentCol
, currentRow 
= divmod(counter
, numrows
) 
 179                 item_minsize 
= item
.GetMinSizeWithBorder() 
 180                 width 
= item_minsize
[0] 
 181                 height 
= item_minsize
[1] 
 183                 print "row_height %d, row_width %d" % (self
.col_heights
[currentCol
], self
.row_widths
[currentRow
]) 
 184                 growable_width 
= (self
.GetSize()[0]) - width
 
 185                 growable_height 
= (self
.GetSize()[1]) - height
 
 187                 #if not self.isVertical and not self.isHorizontal: 
 188                 #    growable_width = self.GetSize()[0] - self.row_widths[currentRow] 
 189                 #    growable_height = self.GetSize()[1] - self.col_heights[currentCol] 
 191                 #print "grow_height %d, grow_width %d" % (growable_height, growable_width) 
 195                 # support wx.EXPAND for box sizers to be compatible 
 196                 if item
.GetFlag() & wx
.EXPAND
: 
 198                         if self
.hgrow 
> 0 and item
.GetProportion() > 0: 
 199                             item_hgrow 
= (growable_width 
* item
.GetProportion()) / self
.hgrow
 
 200                         item_vgrow 
= growable_height 
 
 202                     elif self
.isHorizontal
:  
 203                         if self
.vgrow 
> 0 and item
.GetProportion() > 0: 
 204                             item_vgrow 
= (growable_height 
* item
.GetProportion()) / self
.vgrow
 
 205                         item_hgrow 
= growable_width
 
 207                 if growable_width 
> 0 and item
.GetHGrow() > 0: 
 208                     item_hgrow 
= (growable_width 
* item
.GetHGrow()) / 100 
 209                     print "hgrow = %d" % (item_hgrow
) 
 211                 if growable_height 
> 0 and item
.GetVGrow() > 0: 
 212                     item_vgrow 
= (growable_height 
* item
.GetVGrow()) / 100 
 213                     print "vgrow = %d" % (item_vgrow
) 
 215                 grow_size 
= wx
.Size(item_hgrow
, item_vgrow
) 
 216                 size 
= item_minsize 
#wx.Size(item_minsize[0] + item_hgrow, item_minsize[1] + item_vgrow)  
 217                 if size
[0] + grow_size
[0] > row_widths
[currentRow
]: 
 218                     row_widths
[currentRow
] = size
[0] + grow_size
[0] 
 219                 if size
[1] + grow_size
[1] > col_heights
[currentCol
]: 
 220                     col_heights
[currentCol
] = size
[1] + grow_size
[1]  
 222                 grow_sizes
[counter
] = grow_size
 
 223                 item_sizes
[counter
] = size
 
 228         for item 
in self
.GetChildren(): 
 230                 currentRow
, currentCol 
= divmod(counter
, numcols
) 
 232                 currentCol
, currentRow 
= divmod(counter
, numrows
) 
 234             itempos 
= self
.GetPosition() 
 236                 rowstart 
= itempos
[0]  
 237                 for row 
in range(0, currentRow
): 
 238                     rowstart 
+= row_widths
[row
] 
 240                 colstart 
= itempos
[1] 
 241                 for col 
in range(0, currentCol
): 
 242                     #print "numcols = %d, currentCol = %d, col = %d" % (numcols, currentCol, col) 
 243                     colstart 
+= col_heights
[col
] 
 245                 itempos
[0] += rowstart
 
 246                 itempos
[1] += colstart
 
 248                 if item
.GetFlag() & wx
.ALIGN_RIGHT
: 
 249                     itempos
[0] += (row_widths
[currentRow
] - item_sizes
[counter
][0]) 
 250                 elif item
.GetFlag() & (wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_HORIZONTAL
): 
 251                     itempos
[0] += (row_widths
[currentRow
] - item_sizes
[counter
][0]) / 2 
 253                 if item
.GetFlag() & wx
.ALIGN_BOTTOM
: 
 254                     itempos
[1] += (col_heights
[currentCol
] - item_sizes
[counter
][1]) 
 255                 elif item
.GetFlag() & (wx
.ALIGN_CENTER | wx
.ALIGN_CENTER_VERTICAL
): 
 256                     itempos
[1] += (col_heights
[currentCol
] - item_sizes
[counter
][1]) / 2 
 258                 hgrowth 
=  (grow_sizes
[counter
][0] - itempos
[0]) 
 260                     item_sizes
[counter
][0] += hgrowth
 
 262                 vgrowth 
=  (grow_sizes
[counter
][1] - itempos
[1]) 
 264                     item_sizes
[counter
][1] += vgrowth
 
 265                 #item_sizes[counter][1] -= itempos[1] 
 266                 item
.SetDimension(itempos
, item_sizes
[counter
]) 
 270 def GetDefaultBorder(self
): 
 272     if wx
.Platform 
== "__WXMAC__": 
 274     elif wx
.Platform 
== "__WXMSW__": 
 275         # MSW HIGs use dialog units, not pixels 
 276         pnt 
= self
.ConvertDialogPointToPixels(wx
.Point(4, 4)) 
 278     elif wx
.Platform 
== "__WXGTK__": 
 283 def SetDefaultSizerProps(self
): 
 284     item 
= self
.GetParent().GetSizer().GetItem(self
)     
 285     item
.SetProportion(0) 
 287     item
.SetBorder(self
.GetDefaultBorder()) 
 289 def GetSizerProps(self
): 
 291     Returns a dictionary of prop name + value 
 294     item 
= self
.GetParent().GetSizer().GetItem(self
) 
 296     props
['proportion'] = item
.GetProportion() 
 297     flags 
= item
.GetFlag() 
 299     if flags 
& border
['all'] == border
['all']: 
 300         props
['border'] = (['all'], item
.GetBorder()) 
 304             if flags 
& border
[key
]: 
 307         props
['border'] = (borders
, item
.GetBorder()) 
 309     if flags 
& align
['center'] == align
['center']: 
 310         props
['align'] = 'center' 
 313             if flags 
& halign
[key
]: 
 314                 props
['halign'] = key
 
 317             if flags 
& valign
[key
]: 
 318                 props
['valign'] = key
 
 321         if flags 
& minsize
[key
]: 
 322             props
['minsize'] = key 
 
 324     for key 
in misc_flags
: 
 325         if flags 
& misc_flags
[key
]: 
 330 def SetSizerProp(self
, prop
, value
): 
 333     sizer 
= self
.GetParent().GetSizer() 
 334     item 
= sizer
.GetItem(self
)  
 335     flag 
= item
.GetFlag() 
 336     if lprop 
== "proportion": 
 337         item
.SetProportion(int(value
)) 
 338     elif lprop 
== "hgrow": 
 339         item
.SetHGrow(int(value
)) 
 340     elif lprop 
== "vgrow": 
 341         item
.SetVGrow(int(value
)) 
 342     elif lprop 
== "align": 
 343         flag 
= flag | align
[value
] 
 344     elif lprop 
== "halign": 
 345         flag 
= flag | halign
[value
] 
 346     elif lprop 
== "valign": 
 347         flag 
= flag | valign
[value
] 
 348     elif lprop 
== "border": 
 349         # this arg takes a tuple (dir, pixels) 
 354             flag 
= flag | border
[dir] 
 355         item
.SetBorder(amount
) 
 356     elif lprop 
== "minsize": 
 357         flag 
= flag | minsize
[value
] 
 358     elif lprop 
in misc_flags
: 
 359         if not value 
or str(value
) == "" or str(value
).lower() == "false": 
 360             flag 
= flag 
&~ misc_flags
[lprop
] 
 362             flag 
= flag | misc_flags
[lprop
] 
 364     # auto-adjust growable rows/columns if expand or proportion is set 
 365     # on a sizer item in a FlexGridSizer 
 366     if lprop 
in ["expand", "proportion"] and isinstance(sizer
, wx
.FlexGridSizer
): 
 367         cols 
= sizer
.GetCols() 
 368         rows 
= sizer
.GetRows() 
 369         # FIXME: I'd like to get the item index in the sizer instead, but 
 370         # doing sizer.GetChildren.index(item) always gives an error 
 371         itemnum 
= self
.GetParent().GetChildren().index(self
) 
 376             col
, row 
= divmod( itemnum
, rows 
) 
 378             row
, col 
= divmod( itemnum
, cols 
) 
 380         if lprop 
== "expand": 
 381             sizer
.AddGrowableCol(col
) 
 382         elif lprop 
== "proportion" and int(value
) != 0: 
 383             sizer
.AddGrowableRow(row
) 
 387 def SetSizerProps(self
, props
={}, **kwargs
): 
 389     allprops
.update(props
) 
 390     allprops
.update(kwargs
) 
 392     for prop 
in allprops
: 
 393         self
.SetSizerProp(prop
, allprops
[prop
]) 
 395 def GetDialogBorder(self
): 
 397     if wx
.Platform 
== "__WXMAC__" or wx
.Platform 
== "__WXGTK__": 
 399     elif wx
.Platform 
== "__WXMSW__": 
 400         pnt 
= self
.ConvertDialogPointToPixels(wx
.Point(7, 7)) 
 405 def SetHGrow(self
, proportion
): 
 406     data 
= self
.GetUserData() 
 408         data
["HGrow"] = proportion
 
 409         self
.SetUserData(data
)  
 412     if self
.GetUserData() and "HGrow" in self
.GetUserData(): 
 413         return self
.GetUserData()["HGrow"] 
 417 def SetVGrow(self
, proportion
): 
 418     data 
= self
.GetUserData() 
 420         data
["VGrow"] = proportion
 
 421         self
.SetUserData(data
)  
 425     if self
.GetUserData() and "VGrow" in self
.GetUserData(): 
 426         return self
.GetUserData()["VGrow"] 
 430 def GetDefaultPanelBorder(self
): 
 431     # child controls will handle their borders, so don't pad the panel. 
 434 # Why, Python?! Why do you make it so easy?! ;-)     
 435 wx
.Dialog
.GetDialogBorder 
= GetDialogBorder
 
 436 wx
.Panel
.GetDefaultBorder 
= GetDefaultPanelBorder
 
 437 wx
.Notebook
.GetDefaultBorder 
= GetDefaultPanelBorder
 
 438 wx
.SplitterWindow
.GetDefaultBorder 
= GetDefaultPanelBorder
 
 440 wx
.Window
.GetDefaultBorder 
= GetDefaultBorder
 
 441 wx
.Window
.SetDefaultSizerProps 
= SetDefaultSizerProps
 
 442 wx
.Window
.SetSizerProp 
= SetSizerProp
 
 443 wx
.Window
.SetSizerProps 
= SetSizerProps
 
 444 wx
.Window
.GetSizerProps 
= GetSizerProps
 
 446 wx
.SizerItem
.SetHGrow 
= SetHGrow
 
 447 wx
.SizerItem
.GetHGrow 
= GetHGrow
 
 448 wx
.SizerItem
.SetVGrow 
= SetVGrow
 
 449 wx
.SizerItem
.GetVGrow 
= GetVGrow
 
 452 class SizedPanel(wx
.PyPanel
): 
 453     def __init__(self
, *args
, **kwargs
): 
 454         wx
.PyPanel
.__init
__(self
, *args
, **kwargs
) 
 455         sizer 
= wx
.BoxSizer(wx
.VERTICAL
) #TableSizer(1, 0) 
 457         self
.sizerType 
= "vertical" 
 459     def AddChild(self
, child
): 
 460         wx
.PyPanel
.base_AddChild(self
, child
) 
 462         sizer 
= self
.GetSizer() 
 463         item 
= sizer
.Add(child
) 
 464         item
.SetUserData({"HGrow":0, "VGrow":0}
) 
 466         # Note: One problem is that the child class given to AddChild 
 467         # is the underlying wxWidgets control, not its Python subclass. So if  
 468         # you derive your own class, and override that class' GetDefaultBorder(),  
 469         # etc. methods, it will have no effect.   
 470         child
.SetDefaultSizerProps() 
 472     def GetSizerType(self
): 
 473         return self
.sizerType
 
 475     def SetSizerType(self
, type, options
={}): 
 477         self
.sizerType 
= type 
 478         if type == "horizontal": 
 479             sizer 
= wx
.BoxSizer(wx
.HORIZONTAL
) # TableSizer(0, 1)  
 481         elif type == "vertical": 
 482             sizer 
= wx
.BoxSizer(wx
.VERTICAL
) # TableSizer(1, 0) 
 485             #sizer = TableSizer(2, 0) 
 486             sizer 
= wx
.FlexGridSizer(0, 2, 0, 0) 
 487             #sizer.AddGrowableCol(1) 
 489         elif type == "table": 
 491             if options
.has_key('rows'): 
 492                 rows 
= int(options
['rows']) 
 494             if options
.has_key('cols'): 
 495                 cols 
= int(options
['cols']) 
 497             sizer 
= TableSizer(rows
, cols
) 
 500             sizer 
= wx
.FlexGridSizer(0, 0, 0, 0) 
 501             if options
.has_key('rows'): 
 502                 sizer
.SetRows(int(options
['rows'])) 
 505             if options
.has_key('cols'): 
 506                 sizer
.SetCols(int(options
['cols'])) 
 510             if options
.has_key('growable_row'): 
 511                 row
, proportion 
= options
['growable_row'] 
 512                 sizer
.SetGrowableRow(row
, proportion
) 
 514             if options
.has_key('growable_col'): 
 515                 col
, proportion 
= options
['growable_col'] 
 516                 sizer
.SetGrowableCol(col
, proportion
) 
 518             if options
.has_key('hgap'): 
 519                 sizer
.SetHGap(options
['hgap']) 
 521             if options
.has_key('vgap'): 
 522                 sizer
.SetVGap(options
['vgap']) 
 524             self
._SetNewSizer
(sizer
) 
 526     def _SetNewSizer(self
, sizer
): 
 528         for child 
in self
.GetChildren(): 
 529             props
[child
.GetId()] = child
.GetSizerProps() 
 530             self
.GetSizer().Detach(child
) 
 532         wx
.PyPanel
.SetSizer(self
, sizer
) 
 534         for child 
in self
.GetChildren(): 
 535             self
.GetSizer().Add(child
) 
 536             child
.SetSizerProps(props
[child
.GetId()]) 
 538 class SizedDialog(wx
.Dialog
): 
 539     def __init__(self
, *args
, **kwargs
):     
 540         wx
.Dialog
.__init
__(self
, *args
, **kwargs
) 
 543         self
.mainPanel 
= SizedPanel(self
, -1) 
 545         mysizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 546         mysizer
.Add(self
.mainPanel
, 1, wx
.EXPAND | wx
.ALL
, self
.GetDialogBorder()) 
 547         self
.SetSizer(mysizer
) 
 549         self
.SetAutoLayout(True) 
 551     def GetContentsPane(self
): 
 552         return self
.mainPanel
 
 554     def SetButtonSizer(self
, sizer
): 
 555         self
.GetSizer().Add(sizer
, 0, wx
.EXPAND | wx
.BOTTOM | wx
.RIGHT
, self
.GetDialogBorder()) 
 557         # Temporary hack to fix button ordering problems. 
 558         cancel 
= self
.FindWindowById(wx
.ID_CANCEL
) 
 559         no 
= self
.FindWindowById(wx
.ID_NO
) 
 561             cancel
.MoveAfterInTabOrder(no
) 
 563 class SizedFrame(wx
.Frame
): 
 564     def __init__(self
, *args
, **kwargs
):     
 565         wx
.Frame
.__init
__(self
, *args
, **kwargs
) 
 568         # this probably isn't needed, but I thought it would help to make it consistent 
 569         # with SizedDialog, and creating a panel to hold things is often good practice. 
 570         self
.mainPanel 
= SizedPanel(self
, -1) 
 572         mysizer 
= wx
.BoxSizer(wx
.VERTICAL
) 
 573         mysizer
.Add(self
.mainPanel
, 1, wx
.EXPAND
) 
 574         self
.SetSizer(mysizer
) 
 576         self
.SetAutoLayout(True) 
 578     def GetContentsPane(self
): 
 579         return self
.mainPanel