]>
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