]>
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 item
= self
.GetParent().GetSizer().GetItem(self
)
334 flag
= item
.GetFlag()
335 if lprop
== "proportion":
336 item
.SetProportion(int(value
))
337 elif lprop
== "hgrow":
338 item
.SetHGrow(int(value
))
339 elif lprop
== "vgrow":
340 item
.SetVGrow(int(value
))
341 elif lprop
== "align":
342 flag
= flag | align
[value
]
343 elif lprop
== "halign":
344 flag
= flag | halign
[value
]
345 elif lprop
== "valign":
346 flag
= flag | valign
[value
]
347 elif lprop
== "border":
348 # this arg takes a tuple (dir, pixels)
353 flag
= flag | border
[dir]
354 item
.SetBorder(amount
)
355 elif lprop
== "minsize":
356 flag
= flag | minsize
[value
]
357 elif lprop
in misc_flags
:
358 if not value
or str(value
) == "" or str(value
).lower() == "false":
359 flag
= flag
&~ misc_flags
[lprop
]
361 flag
= flag | misc_flags
[lprop
]
365 def SetSizerProps(self
, props
={}, **kwargs
):
367 allprops
.update(props
)
368 allprops
.update(kwargs
)
370 for prop
in allprops
:
371 self
.SetSizerProp(prop
, allprops
[prop
])
373 def GetDialogBorder(self
):
375 if wx
.Platform
== "__WXMAC__" or wx
.Platform
== "__WXGTK__":
377 elif wx
.Platform
== "__WXMSW__":
378 pnt
= self
.ConvertDialogPointToPixels(wx
.Point(7, 7))
383 def SetHGrow(self
, proportion
):
384 data
= self
.GetUserData()
386 data
["HGrow"] = proportion
387 self
.SetUserData(data
)
390 if self
.GetUserData() and "HGrow" in self
.GetUserData():
391 return self
.GetUserData()["HGrow"]
395 def SetVGrow(self
, proportion
):
396 data
= self
.GetUserData()
398 data
["VGrow"] = proportion
399 self
.SetUserData(data
)
403 if self
.GetUserData() and "VGrow" in self
.GetUserData():
404 return self
.GetUserData()["VGrow"]
408 def GetDefaultPanelBorder(self
):
409 # child controls will handle their borders, so don't pad the panel.
412 # Why, Python?! Why do you make it so easy?! ;-)
413 wx
.Dialog
.GetDialogBorder
= GetDialogBorder
414 wx
.Panel
.GetDefaultBorder
= GetDefaultPanelBorder
415 wx
.Notebook
.GetDefaultBorder
= GetDefaultPanelBorder
416 wx
.SplitterWindow
.GetDefaultBorder
= GetDefaultPanelBorder
418 wx
.Window
.GetDefaultBorder
= GetDefaultBorder
419 wx
.Window
.SetDefaultSizerProps
= SetDefaultSizerProps
420 wx
.Window
.SetSizerProp
= SetSizerProp
421 wx
.Window
.SetSizerProps
= SetSizerProps
422 wx
.Window
.GetSizerProps
= GetSizerProps
424 wx
.SizerItem
.SetHGrow
= SetHGrow
425 wx
.SizerItem
.GetHGrow
= GetHGrow
426 wx
.SizerItem
.SetVGrow
= SetVGrow
427 wx
.SizerItem
.GetVGrow
= GetVGrow
430 class SizedPanel(wx
.PyPanel
):
431 def __init__(self
, *args
, **kwargs
):
432 wx
.PyPanel
.__init
__(self
, *args
, **kwargs
)
433 sizer
= wx
.BoxSizer(wx
.VERTICAL
) #TableSizer(1, 0)
435 self
.sizerType
= "vertical"
437 def AddChild(self
, child
):
438 wx
.PyPanel
.base_AddChild(self
, child
)
440 sizer
= self
.GetSizer()
441 item
= sizer
.Add(child
)
442 item
.SetUserData({"HGrow":0, "VGrow":0}
)
444 # Note: One problem is that the child class given to AddChild
445 # is the underlying wxWidgets control, not its Python subclass. So if
446 # you derive your own class, and override that class' GetDefaultBorder(),
447 # etc. methods, it will have no effect.
448 child
.SetDefaultSizerProps()
450 def GetSizerType(self
):
451 return self
.sizerType
453 def SetSizerType(self
, type, options
={}):
455 self
.sizerType
= type
456 if type == "horizontal":
457 sizer
= wx
.BoxSizer(wx
.HORIZONTAL
) # TableSizer(0, 1)
459 elif type == "vertical":
460 sizer
= wx
.BoxSizer(wx
.VERTICAL
) # TableSizer(1, 0)
463 #sizer = TableSizer(2, 0)
464 sizer
= wx
.FlexGridSizer(0, 2, 0, 0)
465 sizer
.AddGrowableCol(1)
467 elif type == "table":
469 if options
.has_key('rows'):
470 rows
= int(options
['rows'])
472 if options
.has_key('cols'):
473 cols
= int(options
['cols'])
475 sizer
= TableSizer(rows
, cols
)
478 sizer
= wx
.FlexGridSizer(0, 0, 0, 0)
479 if options
.has_key('rows'):
480 sizer
.SetRows(int(options
['rows']))
483 if options
.has_key('cols'):
484 sizer
.SetCols(int(options
['cols']))
488 if options
.has_key('growable_row'):
489 row
, proportion
= options
['growable_row']
490 sizer
.SetGrowableRow(row
, proportion
)
492 if options
.has_key('growable_col'):
493 col
, proportion
= options
['growable_col']
494 sizer
.SetGrowableCol(col
, proportion
)
496 if options
.has_key('hgap'):
497 sizer
.SetHGap(options
['hgap'])
499 if options
.has_key('vgap'):
500 sizer
.SetVGap(options
['vgap'])
502 self
._SetNewSizer
(sizer
)
504 def _SetNewSizer(self
, sizer
):
506 for child
in self
.GetChildren():
507 props
[child
.GetId()] = child
.GetSizerProps()
508 self
.GetSizer().Detach(child
)
510 wx
.PyPanel
.SetSizer(self
, sizer
)
512 for child
in self
.GetChildren():
513 self
.GetSizer().Add(child
)
514 child
.SetSizerProps(props
[child
.GetId()])
516 class SizedDialog(wx
.Dialog
):
517 def __init__(self
, *args
, **kwargs
):
518 wx
.Dialog
.__init
__(self
, *args
, **kwargs
)
521 self
.mainPanel
= SizedPanel(self
, -1)
523 mysizer
= wx
.BoxSizer(wx
.VERTICAL
)
524 mysizer
.Add(self
.mainPanel
, 1, wx
.EXPAND | wx
.ALL
, self
.GetDialogBorder())
525 self
.SetSizer(mysizer
)
527 self
.SetAutoLayout(True)
529 def GetContentsPane(self
):
530 return self
.mainPanel
532 def SetButtonSizer(self
, sizer
):
533 self
.GetSizer().Add(sizer
, 0, wx
.EXPAND | wx
.BOTTOM | wx
.RIGHT
, self
.GetDialogBorder())
535 # Temporary hack to fix button ordering problems.
536 cancel
= self
.FindWindowById(wx
.ID_CANCEL
)
537 no
= self
.FindWindowById(wx
.ID_NO
)
539 cancel
.MoveAfterInTabOrder(no
)
541 class SizedFrame(wx
.Frame
):
542 def __init__(self
, *args
, **kwargs
):
543 wx
.Frame
.__init
__(self
, *args
, **kwargs
)
546 # this probably isn't needed, but I thought it would help to make it consistent
547 # with SizedDialog, and creating a panel to hold things is often good practice.
548 self
.mainPanel
= SizedPanel(self
, -1)
550 mysizer
= wx
.BoxSizer(wx
.VERTICAL
)
551 mysizer
.Add(self
.mainPanel
, 1, wx
.EXPAND
)
552 self
.SetSizer(mysizer
)
554 self
.SetAutoLayout(True)
556 def GetContentsPane(self
):
557 return self
.mainPanel