1 # --------------------------------------------------------------------------- #
2 # FANCYBUTTONPANEL Widget wxPython IMPLEMENTATION
4 # Original C++ Code From Eran. You Can Find It At:
6 # http://wxforum.shadonet.com/viewtopic.php?t=6619
8 # License: wxWidgets license
13 # Andrea Gavana, @ 02 Oct 2006
14 # Latest Revision: 16 Oct 2006, 17.00 GMT
17 # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
20 # andrea.gavana@gmail.com
23 # Or, Obviously, To The wxPython Mailing List!!!
27 # --------------------------------------------------------------------------- #
30 With `ButtonPanel` class you have a panel with gradient coloring
31 on it and with the possibility to place some buttons on it. Using a
32 standard panel with normal wx.Buttons leads to an ugly result: the
33 buttons are placed correctly on the panel - but with grey area around
34 them. Gradient coloring is kept behind the images - this was achieved
35 due to the PNG format and the transparency of the bitmaps.
37 The image are functioning like a buttons and can be caught in your
38 code using the usual self.Bind(wx.EVT_BUTTON, self.OnButton) method.
40 The control is generic, and support theming (well, I tested it under
41 Windows with the three defauls themes: grey, blue, silver and the
48 The following example shows a simple implementation that uses ButtonPanel
49 inside a very simple frame::
51 class MyFrame(wx.Frame):
53 def __init__(self, parent, id=-1, title="ButtonPanel", pos=wx.DefaultPosition,
54 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE):
56 wx.Frame.__init__(self, parent, id, title, pos, size, style)
58 mainPanel = wx.Panel(self, -1)
59 self.logtext = wx.TextCtrl(mainPanel, -1, "", style=wx.TE_MULTILINE)
61 vSizer = wx.BoxSizer(wx.VERTICAL)
62 mainPanel.SetSizer(vSizer)
64 alignment = BP_ALIGN_RIGHT
66 titleBar = ButtonPanel(mainPanel, -1, "A Simple Test & Demo")
68 btn1 = ButtonInfo(wx.NewId(), wx.Bitmap("png4.png", wx.BITMAP_TYPE_PNG))
69 titleBar.AddButton(btn1)
70 self.Bind(wx.EVT_BUTTON, self.OnButton, btn1)
72 btn2 = ButtonInfo(wx.NewId(), wx.Bitmap("png3.png", wx.BITMAP_TYPE_PNG))
73 titleBar.AddButton(btn2)
74 self.Bind(wx.EVT_BUTTON, self.OnButton, btn2)
76 btn3 = ButtonInfo(wx.NewId(), wx.Bitmap("png2.png", wx.BITMAP_TYPE_PNG))
77 titleBar.AddButton(btn3)
78 self.Bind(wx.EVT_BUTTON, self.OnButton, btn3)
80 btn4 = ButtonInfo(wx.NewId(), wx.Bitmap("png1.png", wx.BITMAP_TYPE_PNG))
81 titleBar.AddButton(btn4)
82 self.Bind(wx.EVT_BUTTON, self.OnButton, btn4)
84 vSizer.Add(titleBar, 0, wx.EXPAND)
86 vSizer.Add(self.logtext, 1, wx.EXPAND|wx.ALL, 5)
90 # our normal wxApp-derived class, as usual
92 app = wx.PySimpleApp()
95 app.SetTopWindow(frame)
103 ButtonPanel Is Freeware And Distributed Under The wxPython License.
105 Latest Revision: Andrea Gavana @ 12 Oct 2006, 17.00 GMT
113 # Some constants to tune the BPArt class
114 BP_BACKGROUND_COLOR
= 0
115 """ Background brush colour when no gradient shading exists. """
116 BP_GRADIENT_COLOR_FROM
= 1
117 """ Starting gradient colour, used only when BP_USE_GRADIENT style is applied. """
118 BP_GRADIENT_COLOR_TO
= 2
119 """ Ending gradient colour, used only when BP_USE_GRADIENT style is applied. """
121 """ Pen colour to paint the border of ButtonPanel. """
123 """ Main ButtonPanel caption colour. """
124 BP_BUTTONTEXT_COLOR
= 5
125 """ Text colour for buttons with text. """
126 BP_BUTTONTEXT_INACTIVE_COLOR
= 6
127 """ Text colour for inactive buttons with text. """
128 BP_SELECTION_BRUSH_COLOR
= 7
129 """ Brush colour to be used when hovering or selecting a button. """
130 BP_SELECTION_PEN_COLOR
= 8
131 """ Pen colour to be used when hovering or selecting a button. """
132 BP_SEPARATOR_COLOR
= 9
133 """ Pen colour used to paint the separators. """
135 """ Font of the ButtonPanel main caption. """
136 BP_BUTTONTEXT_FONT
= 11
137 """ Text font for the buttons with text. """
139 BP_BUTTONTEXT_ALIGN_BOTTOM
= 12
140 """ Flag that indicates the image and text in buttons is stacked. """
141 BP_BUTTONTEXT_ALIGN_RIGHT
= 13
142 """ Flag that indicates the text is shown alongside the image in buttons with text. """
144 BP_SEPARATOR_SIZE
= 14
146 Separator size. NB: This is not the line width, but the sum of the space before
147 and after the separator line plus the width of the line.
151 Size of the left/right margins in ButtonPanel (top/bottom for vertically
152 aligned ButtonPanels).
155 """ Size of the border. """
157 """ Inter-tool separator size. """
159 # Caption Gradient Type
161 """ No gradient shading should be used to paint the background. """
162 BP_GRADIENT_VERTICAL
= 1
163 """ Vertical gradient shading should be used to paint the background. """
164 BP_GRADIENT_HORIZONTAL
= 2
165 """ Horizontal gradient shading should be used to paint the background. """
167 # Flags for HitTest() method
171 # Alignment of buttons in the panel
182 # Check for the new method in 2.7 (not present in 2.6.3.3)
183 if wx
.VERSION_STRING
< "2.7":
184 wx
.Rect
.Contains
= lambda self
, point
: wx
.Rect
.Inside(self
, point
)
187 def BrightenColour(color
, factor
):
188 """ Bright the input colour by a factor."""
190 val
= color
.Red()*factor
196 val
= color
.Green()*factor
202 val
= color
.Blue()*factor
208 return wx
.Color(red
, green
, blue
)
211 def GrayOut(anImage
):
213 Convert the given image (in place) to a grayed-out version,
214 appropriate for a 'Disabled' appearance.
217 factor
= 0.7 # 0 < f < 1. Higher Is Grayer
219 anImage
= anImage
.ConvertToImage()
220 if anImage
.HasAlpha():
221 anImage
.ConvertAlphaToMask(1)
223 if anImage
.HasMask():
224 maskColor
= (anImage
.GetMaskRed(), anImage
.GetMaskGreen(), anImage
.GetMaskBlue())
228 data
= map(ord, list(anImage
.GetData()))
230 for i
in range(0, len(data
), 3):
232 pixel
= (data
[i
], data
[i
+1], data
[i
+2])
233 pixel
= MakeGray(pixel
, factor
, maskColor
)
238 anImage
.SetData(''.join(map(chr, data
)))
240 anImage
= anImage
.ConvertToBitmap()
245 def MakeGray((r
,g
,b
), factor
, maskColor
):
247 Make a pixel grayed-out. If the pixel matches the maskColor, it won't be
251 if (r
,g
,b
) != maskColor
:
252 return map(lambda x
: int((230 - x
) * factor
) + x
, (r
,g
,b
))
257 # ---------------------------------------------------------------------------- #
259 # Handles all the drawings for buttons, separators and text and allows the
260 # programmer to set colours, sizes and gradient shadings for ButtonPanel
261 # ---------------------------------------------------------------------------- #
265 BPArt is an art provider class which does all of the drawing for ButtonPanel.
266 This allows the library caller to customize the BPArt or to completely replace
267 all drawing with custom BPArts.
270 def __init__(self
, parentStyle
):
271 """ Default class constructor. """
273 base_color
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_3DFACE
)
275 self
._background
_brush
= wx
.Brush(base_color
, wx
.SOLID
)
276 self
._gradient
_color
_to
= wx
.WHITE
277 self
._gradient
_color
_from
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_ACTIVECAPTION
)
279 if parentStyle
& BP_USE_GRADIENT
:
280 self
._border
_pen
= wx
.Pen(wx
.WHITE
, 3)
281 self
._caption
_text
_color
= wx
.WHITE
282 self
._buttontext
_color
= wx
.Colour(70, 143, 255)
283 self
._separator
_pen
= wx
.Pen(BrightenColour(self
._gradient
_color
_from
, 1.4))
284 self
._gradient
_type
= BP_GRADIENT_VERTICAL
286 self
._border
_pen
= wx
.Pen(BrightenColour(base_color
, 0.9), 3)
287 self
._caption
_text
_color
= wx
.BLACK
288 self
._buttontext
_color
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNTEXT
)
289 self
._separator
_pen
= wx
.Pen(BrightenColour(base_color
, 0.9))
290 self
._gradient
_type
= BP_GRADIENT_NONE
292 self
._buttontext
_inactive
_color
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_GRAYTEXT
)
293 self
._selection
_brush
= wx
.Brush(wx
.Color(225, 225, 255))
294 self
._selection
_pen
= wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_ACTIVECAPTION
))
296 sysfont
= wx
.SystemSettings
.GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
297 self
._caption
_font
= wx
.Font(sysfont
.GetPointSize(), wx
.DEFAULT
, wx
.NORMAL
, wx
.BOLD
,
298 False, sysfont
.GetFaceName())
299 self
._buttontext
_font
= wx
.Font(sysfont
.GetPointSize(), wx
.DEFAULT
, wx
.NORMAL
, wx
.NORMAL
,
300 False, sysfont
.GetFaceName())
302 self
._separator
_size
= 7
303 self
._margins
_size
= wx
.Size(6, 6)
304 self
._caption
_border
_size
= 3
305 self
._padding
_size
= wx
.Size(6, 6)
308 def GetMetric(self
, id):
309 """ Returns sizes of customizable options. """
311 if id == BP_SEPARATOR_SIZE
:
312 return self
._separator
_size
313 elif id == BP_MARGINS_SIZE
:
314 return self
._margins
_size
315 elif id == BP_BORDER_SIZE
:
316 return self
._caption
_border
_size
317 elif id == BP_PADDING_SIZE
:
318 return self
._padding
_size
320 raise "\nERROR: Invalid Metric Ordinal. "
323 def SetMetric(self
, id, new_val
):
324 """ Sets sizes for customizable options. """
326 if id == BP_SEPARATOR_SIZE
:
327 self
._separator
_size
= new_val
328 elif id == BP_MARGINS_SIZE
:
329 self
._margins
_size
= new_val
330 elif id == BP_BORDER_SIZE
:
331 self
._caption
_border
_size
= new_val
332 self
._border
_pen
.SetWidth(new_val
)
333 elif id == BP_PADDING_SIZE
:
334 self
._padding
_size
= new_val
336 raise "\nERROR: Invalid Metric Ordinal. "
339 def GetColor(self
, id):
340 """ Returns colours of customizable options. """
342 if id == BP_BACKGROUND_COLOR
:
343 return self
._background
_brush
.GetColour()
344 elif id == BP_GRADIENT_COLOR_FROM
:
345 return self
._gradient
_color
_from
346 elif id == BP_GRADIENT_COLOR_TO
:
347 return self
._gradient
_color
_to
348 elif id == BP_BORDER_COLOR
:
349 return self
._border
_pen
.GetColour()
350 elif id == BP_TEXT_COLOR
:
351 return self
._caption
_text
_color
352 elif id == BP_BUTTONTEXT_COLOR
:
353 return self
._buttontext
_color
354 elif id == BP_BUTTONTEXT_INACTIVE_COLOR
:
355 return self
._buttontext
_inactive
_color
356 elif id == BP_SELECTION_BRUSH_COLOR
:
357 return self
._selection
_brush
.GetColour()
358 elif id == BP_SELECTION_PEN_COLOR
:
359 return self
._selection
_pen
.GetColour()
360 elif id == BP_SEPARATOR_COLOR
:
361 return self
._separator
_pen
.GetColour()
363 raise "\nERROR: Invalid Colour Ordinal. "
366 def SetColor(self
, id, colour
):
367 """ Sets colours for customizable options. """
369 if id == BP_BACKGROUND_COLOR
:
370 self
._background
_brush
.SetColour(colour
)
371 elif id == BP_GRADIENT_COLOR_FROM
:
372 self
._gradient
_color
_from
= colour
373 elif id == BP_GRADIENT_COLOR_TO
:
374 self
._gradient
_color
_to
= colour
375 elif id == BP_BORDER_COLOR
:
376 self
._border
_pen
.SetColour(colour
)
377 elif id == BP_TEXT_COLOR
:
378 self
._caption
_text
_color
= colour
379 elif id == BP_BUTTONTEXT_COLOR
:
380 self
._buttontext
_color
= colour
381 elif id == BP_BUTTONTEXT_INACTIVE_COLOR
:
382 self
._buttontext
_inactive
_color
= colour
383 elif id == BP_SELECTION_BRUSH_COLOR
:
384 self
._selection
_brush
.SetColour(colour
)
385 elif id == BP_SELECTION_PEN_COLOR
:
386 self
._selection
_pen
.SetColour(colour
)
387 elif id == BP_SEPARATOR_COLOR
:
388 self
._separator
_pen
.SetColour(colour
)
390 raise "\nERROR: Invalid Colour Ordinal. "
397 def SetFont(self
, id, font
):
398 """ Sets font for customizable options. """
400 if id == BP_TEXT_FONT
:
401 self
._caption
_font
= font
402 elif id == BP_BUTTONTEXT_FONT
:
403 self
._buttontext
_font
= font
406 def GetFont(self
, id):
407 """ Returns font of customizable options. """
409 if id == BP_TEXT_FONT
:
410 return self
._caption
_font
411 elif id == BP_BUTTONTEXT_FONT
:
412 return self
._buttontext
_font
417 def SetGradientType(self
, gradient
):
418 """ Sets the gradient type for BPArt drawings. """
420 self
._gradient
_type
= gradient
423 def GetGradientType(self
):
424 """ Returns the gradient type for BPArt drawings. """
426 return self
._gradient
_type
429 def DrawSeparator(self
, dc
, rect
, isVertical
):
430 """ Draws a separator in ButtonPanel. """
432 dc
.SetPen(self
._separator
_pen
)
435 ystart
= yend
= rect
.y
+ rect
.height
/2
436 xstart
= int(rect
.x
+ 1.5*self
._caption
_border
_size
)
437 xend
= int(rect
.x
+ rect
.width
- 1.5*self
._caption
_border
_size
)
438 dc
.DrawLine(xstart
, ystart
, xend
, yend
)
440 xstart
= xend
= rect
.x
+ rect
.width
/2
441 ystart
= int(rect
.y
+ 1.5*self
._caption
_border
_size
)
442 yend
= int(rect
.y
+ rect
.height
- 1.5*self
._caption
_border
_size
)
443 dc
.DrawLine(xstart
, ystart
, xend
, yend
)
446 def DrawCaption(self
, dc
, rect
, captionText
):
447 """ Draws the main caption text in ButtonPanel. """
449 textColour
= self
._caption
_text
_color
450 textFont
= self
._caption
_font
451 padding
= self
._padding
_size
453 dc
.SetTextForeground(textColour
)
456 dc
.DrawText(captionText
, rect
.x
+ padding
.x
, rect
.y
+padding
.y
)
459 def DrawButton(self
, dc
, rect
, parentSize
, buttonBitmap
, isVertical
,
460 buttonStatus
, isToggled
, textAlignment
, text
=""):
461 """ Draws a button in ButtonPanel, together with its text (if any). """
463 bmpxsize
, bmpysize
= buttonBitmap
.GetWidth(), buttonBitmap
.GetHeight()
466 borderw
= self
._caption
_border
_size
467 padding
= self
._padding
_size
469 buttonFont
= self
._buttontext
_font
470 dc
.SetFont(buttonFont
)
474 rect
= wx
.Rect(borderw
, rect
.y
, rect
.width
-2*borderw
, rect
.height
)
478 textW
, textH
= dc
.GetTextExtent(text
)
480 if textAlignment
== BP_BUTTONTEXT_ALIGN_RIGHT
:
481 fullExtent
= bmpxsize
+ padding
.x
/2 + textW
482 bmpypos
= rect
.y
+ (rect
.height
- bmpysize
)/2
483 bmpxpos
= rect
.x
+ (rect
.width
- fullExtent
)/2
484 textxpos
= bmpxpos
+ padding
.x
/2 + bmpxsize
485 textypos
= bmpypos
+ (bmpysize
- textH
)/2
487 bmpxpos
= rect
.x
+ (rect
.width
- bmpxsize
)/2
488 bmpypos
= rect
.y
+ padding
.y
489 textxpos
= rect
.x
+ (rect
.width
- textW
)/2
490 textypos
= bmpypos
+ bmpysize
+ padding
.y
/2
492 bmpxpos
= rect
.x
+ (rect
.width
- bmpxsize
)/2
493 bmpypos
= rect
.y
+ (rect
.height
- bmpysize
)/2
498 rect
= wx
.Rect(rect
.x
, borderw
, rect
.width
, rect
.height
-2*borderw
)
502 textW
, textH
= dc
.GetTextExtent(text
)
504 if textAlignment
== BP_BUTTONTEXT_ALIGN_RIGHT
:
505 fullExtent
= bmpxsize
+ padding
.x
/2 + textW
506 bmpypos
= rect
.y
+ (rect
.height
- bmpysize
)/2
507 bmpxpos
= rect
.x
+ (rect
.width
- fullExtent
)/2
508 textxpos
= bmpxpos
+ padding
.x
/2 + bmpxsize
509 textypos
= bmpypos
+ (bmpysize
- textH
)/2
511 fullExtent
= bmpysize
+ padding
.y
/2 + textH
512 bmpxpos
= rect
.x
+ (rect
.width
- bmpxsize
)/2
513 bmpypos
= rect
.y
+ (rect
.height
- fullExtent
)/2
514 textxpos
= rect
.x
+ (rect
.width
- textW
)/2
515 textypos
= bmpypos
+ bmpysize
+ padding
.y
/2
517 bmpxpos
= rect
.x
+ (rect
.width
- bmpxsize
)/2
518 bmpypos
= rect
.y
+ (rect
.height
- bmpysize
)/2
521 # [ Padding | Text | .. Buttons .. | Padding ]
523 if buttonStatus
in ["Pressed", "Toggled", "Hover"]:
524 dc
.SetBrush(self
._selection
_brush
)
525 dc
.SetPen(self
._selection
_pen
)
526 dc
.DrawRoundedRectangleRect(rect
, 4)
528 if buttonStatus
== "Pressed" or isToggled
:
531 dc
.DrawBitmap(buttonBitmap
, bmpxpos
+dx
, bmpypos
+dy
, True)
534 isEnabled
= buttonStatus
!= "Disabled"
535 self
.DrawLabel(dc
, text
, isEnabled
, textxpos
+dx
, textypos
+dy
)
538 def DrawLabel(self
, dc
, text
, isEnabled
, xpos
, ypos
):
539 """ Draws the label for a button. """
542 dc
.SetTextForeground(self
._buttontext
_inactive
_color
)
544 dc
.SetTextForeground(self
._buttontext
_color
)
546 dc
.DrawText(text
, xpos
, ypos
)
549 def DrawButtonPanel(self
, dc
, rect
, style
):
550 """ Paint the ButtonPanel's background. """
552 if style
& BP_USE_GRADIENT
:
553 # Draw gradient color in the backgroud of the panel
554 self
.FillGradientColor(dc
, rect
)
556 # Draw a rectangle around the panel
557 backBrush
= (style
& BP_USE_GRADIENT
and [wx
.TRANSPARENT_BRUSH
] or \
558 [self
._background
_brush
])[0]
560 dc
.SetBrush(backBrush
)
561 dc
.SetPen(self
._border
_pen
)
562 dc
.DrawRectangleRect(rect
)
565 def FillGradientColor(self
, dc
, rect
):
566 """ Gradient fill from colour 1 to colour 2 with top to bottom or left to right. """
568 if rect
.height
< 1 or rect
.width
< 1:
571 isVertical
= self
._gradient
_type
== BP_GRADIENT_VERTICAL
572 size
= (isVertical
and [rect
.height
] or [rect
.width
])[0]
573 start
= (isVertical
and [rect
.y
] or [rect
.x
])[0]
575 # calculate gradient coefficients
577 col2
= self
._gradient
_color
_from
578 col1
= self
._gradient
_color
_to
581 rstep
= float((col2
.Red() - col1
.Red()))/float(size
)
582 gstep
= float((col2
.Green() - col1
.Green()))/float(size
)
583 bstep
= float((col2
.Blue() - col1
.Blue()))/float(size
)
585 for coord
in xrange(start
, start
+ size
):
587 currCol
= wx
.Colour(col1
.Red() + rf
, col1
.Green() + gf
, col1
.Blue() + bf
)
588 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
589 dc
.SetPen(wx
.Pen(currCol
))
591 dc
.DrawLine(rect
.x
, coord
, rect
.x
+ rect
.width
, coord
)
593 dc
.DrawLine(coord
, rect
.y
, coord
, rect
.y
+ rect
.width
)
600 class Control(wx
.EvtHandler
):
602 def __init__(self
, parent
, size
=wx
.Size(-1, -1)):
604 Default class constructor.
606 Base class for all pseudo controls
607 parent = parent object
608 size = (width, height)
611 wx
.EvtHandler
.__init
__(self
)
613 self
._parent
= parent
614 self
._id
= wx
.NewId()
620 def Show(self
, show
=True):
621 """ Shows or hide the control. """
627 """ Hides the control. """
633 """ Returns whether the control is shown or not. """
639 """ Returns the control id. """
644 def GetBestSize(self
):
645 """ Returns the control best size. """
651 """ Disables the control. """
656 def Enable(self
, value
=True):
657 """ Enables or disables the control. """
659 self
.disabled
= not value
662 def SetFocus(self
, focus
=True):
663 """ Sets or kills the focus on the control. """
669 """ Returns whether the control has the focus or not. """
674 def OnMouseEvent(self
, x
, y
, event
):
677 def Draw(self
, rect
):
686 This is a mix-in class to add pseudo support to a wx sizer. Just create
687 a new class that derives from this class and the wx sizer and intercepts
688 any methods that add to the wx sizer.
691 self
.children
= [] # list of child Pseudo Controls
693 # Sizer doesn't use the x1,y1,x2,y2 so allow it to
694 # be called with or without the coordinates
695 def Draw(self
, dc
, x1
=0, y1
=0, x2
=0, y2
=0):
696 for item
in self
.children
:
697 # use sizer coordinates rather than
699 c
= item
.GetUserData()
700 c
.Draw(dc
, item
.GetRect())
702 def GetBestSize(self
):
703 # this should be handled by the wx.Sizer based class
704 return self
.GetMinSize()
708 class BoxSizer(Sizer
, wx
.BoxSizer
):
709 def __init__(self
, orient
=wx
.HORIZONTAL
):
710 wx
.BoxSizer
.__init
__(self
, orient
)
713 #-------------------------------------------
714 # sizer overrides (only called from Python)
715 #-------------------------------------------
716 # no support for user data if it's a pseudocontrol
717 # since that is already used
718 def Add(self
, item
, proportion
=0, flag
=0, border
=0, userData
=None):
719 # check to see if it's a pseudo object or sizer
720 if isinstance(item
, Sizer
):
721 szitem
= wx
.BoxSizer
.Add(self
, item
, proportion
, flag
, border
, item
)
722 self
.children
.append(szitem
)
723 elif isinstance(item
, Control
): # Control should be what ever class your controls come from
724 sz
= item
.GetBestSize()
725 # add a spacer to track this object
726 szitem
= wx
.BoxSizer
.Add(self
, sz
, proportion
, flag
, border
, item
)
727 self
.children
.append(szitem
)
729 wx
.BoxSizer
.Add(self
, item
, proportion
, flag
, border
, userData
)
731 def Prepend(self
, item
, proportion
=0, flag
=0, border
=0, userData
=None):
732 # check to see if it's a pseudo object or sizer
733 if isinstance(item
, Sizer
):
734 szitem
= wx
.BoxSizer
.Prepend(self
, item
, proportion
, flag
, border
, item
)
735 self
.children
.append(szitem
)
736 elif isinstance(item
, Control
): # Control should be what ever class your controls come from
737 sz
= item
.GetBestSize()
738 # add a spacer to track this object
739 szitem
= wx
.BoxSizer
.Prepend(self
, sz
, proportion
, flag
, border
, item
)
740 self
.children
.insert(0,szitem
)
742 wx
.BoxSizer
.Prepend(self
, item
, proportion
, flag
, border
, userData
)
744 def Insert(self
, before
, item
, proportion
=0, flag
=0, border
=0, userData
=None, realIndex
=None):
745 # check to see if it's a pseudo object or sizer
746 if isinstance(item
, Sizer
):
747 szitem
= wx
.BoxSizer
.Insert(self
, before
, item
, proportion
, flag
, border
, item
)
748 self
.children
.append(szitem
)
749 elif isinstance(item
, Control
): # Control should be what ever class your controls come from
750 sz
= item
.GetBestSize()
751 # add a spacer to track this object
752 szitem
= wx
.BoxSizer
.Insert(self
, before
, sz
, proportion
, flag
, border
, item
)
753 if realIndex
is not None:
754 self
.children
.insert(realIndex
,szitem
)
756 self
.children
.insert(before
,szitem
)
759 wx
.BoxSizer
.Insert(self
, before
, item
, proportion
, flag
, border
, userData
)
762 def Remove(self
, indx
, pop
=-1):
765 self
.children
.pop(pop
)
767 wx
.BoxSizer
.Remove(self
, indx
)
772 for ii
, child
in enumerate(self
.GetChildren()):
773 item
= child
.GetUserData()
774 if item
and child
.IsShown():
775 self
.SetItemMinSize(ii
, *item
.GetBestSize())
777 wx
.BoxSizer
.Layout(self
)
780 def Show(self
, item
, show
=True):
782 child
= self
.GetChildren()[item
]
783 if child
and child
.GetUserData():
784 child
.GetUserData().Show(show
)
786 wx
.BoxSizer
.Show(self
, item
, show
)
789 # ---------------------------------------------------------------------------- #
791 # This class holds all the information to size and draw a separator inside
793 # ---------------------------------------------------------------------------- #
795 class Separator(Control
):
797 def __init__(self
, parent
):
798 """ Default class constructor. """
801 self
._parent
= parent
802 Control
.__init
__(self
, parent
)
805 def GetBestSize(self
):
806 """ Returns the separator best size. """
808 # 10 is completely arbitrary, but it works anyhow
809 if self
._parent
.IsVertical():
810 return wx
.Size(10, self
._parent
._art
.GetMetric(BP_SEPARATOR_SIZE
))
812 return wx
.Size(self
._parent
._art
.GetMetric(BP_SEPARATOR_SIZE
), 10)
815 def Draw(self
, dc
, rect
):
816 """ Draws the separator. Actually the drawing is done in BPArt. """
818 if not self
.IsShown():
821 isVertical
= self
._parent
.IsVertical()
822 self
._parent
._art
.DrawSeparator(dc
, rect
, isVertical
)
825 # ---------------------------------------------------------------------------- #
826 # Class ButtonPanelText
827 # This class is used to hold data about the main caption in ButtonPanel
828 # ---------------------------------------------------------------------------- #
830 class ButtonPanelText(Control
):
832 def __init__(self
, parent
, text
=""):
833 """ Default class constructor. """
837 self
._parent
= parent
839 Control
.__init
__(self
, parent
)
843 """ Returns the caption text. """
848 def SetText(self
, text
=""):
849 """ Sets the caption text. """
855 """ Convenience function to create a DC. """
857 dc
= wx
.ClientDC(self
._parent
)
858 textFont
= self
._parent
._art
.GetFont(BP_TEXT_FONT
)
864 def GetBestSize(self
):
865 """ Returns the best size for the main caption in ButtonPanel. """
871 rect
= self
._parent
.GetClientRect()
873 tw
, th
= dc
.GetTextExtent(self
._text
)
874 padding
= self
._parent
._art
.GetMetric(BP_PADDING_SIZE
)
875 self
._size
= wx
.Size(tw
+2*padding
.x
, th
+2*padding
.y
)
880 def Draw(self
, dc
, rect
):
881 """ Draws the main caption. Actually the drawing is done in BPArt. """
883 if not self
.IsShown():
886 captionText
= self
.GetText()
887 self
._parent
._art
.DrawCaption(dc
, rect
, captionText
)
890 # -- ButtonInfo class implementation ----------------------------------------
891 # This class holds information about every button that is added to
892 # ButtonPanel. It is an auxiliary class that you should use
893 # every time you add a button.
895 class ButtonInfo(Control
):
897 def __init__(self
, parent
, id=wx
.ID_ANY
, bmp
=wx
.NullBitmap
,
898 status
="Normal", text
="", kind
=wx
.ITEM_NORMAL
):
900 Default class constructor.
903 - parent: the parent window (ButtonPanel);
905 - bmp: the associated bitmap;
906 - status: button status (pressed, hovered, None).
907 - text to be displayed either below of to the right of the button
913 self
._status
= status
914 self
._rect
= wx
.Rect()
918 self
._textAlignment
= BP_BUTTONTEXT_ALIGN_BOTTOM
920 disabledbmp
= GrayOut(bmp
)
922 self
._bitmaps
= {"Normal": bmp
, "Toggled": None, "Disabled": disabledbmp
,
923 "Hover": None, "Pressed": None}
925 Control
.__init
__(self
, parent
)
928 def GetBestSize(self
):
929 """ Returns the best size for the button. """
931 xsize
= self
.GetBitmap().GetWidth()
932 ysize
= self
.GetBitmap().GetHeight()
935 # We have text in the button
936 dc
= wx
.ClientDC(self
._parent
)
937 normalFont
= self
._parent
._art
.GetFont(BP_BUTTONTEXT_FONT
)
938 dc
.SetFont(normalFont
)
939 tw
, th
= dc
.GetTextExtent(self
.GetText())
941 if self
.GetTextAlignment() == BP_BUTTONTEXT_ALIGN_BOTTOM
:
942 xsize
= max(xsize
, tw
)
946 ysize
= max(ysize
, th
)
948 border
= self
._parent
._art
.GetMetric(BP_BORDER_SIZE
)
949 padding
= self
._parent
._art
.GetMetric(BP_PADDING_SIZE
)
951 if self
._parent
.IsVertical():
952 xsize
= xsize
+ 2*border
954 ysize
= ysize
+ 2*border
956 self
._size
= wx
.Size(xsize
+2*padding
.x
, ysize
+2*padding
.y
)
961 def Draw(self
, dc
, rect
):
962 """ Draws the button on ButtonPanel. Actually the drawing is done in BPArt. """
964 if not self
.IsShown():
967 buttonBitmap
= self
.GetBitmap()
968 isVertical
= self
._parent
.IsVertical()
969 text
= self
.GetText()
970 parentSize
= self
._parent
.GetSize()[not isVertical
]
971 buttonStatus
= self
.GetStatus()
972 isToggled
= self
.GetToggled()
973 textAlignment
= self
.GetTextAlignment()
975 self
._parent
._art
.DrawButton(dc
, rect
, parentSize
, buttonBitmap
, isVertical
,
976 buttonStatus
, isToggled
, textAlignment
, text
)
981 def CheckRefresh(self
, status
):
982 """ Checks whether a ButtonPanel repaint is needed or not. Convenience function. """
984 if status
== self
._status
:
985 self
._parent
.Refresh()
988 def SetBitmap(self
, bmp
, status
="Normal"):
989 """ Sets the associated bitmap. """
991 self
._bitmaps
[status
] = bmp
992 self
.CheckRefresh(status
)
995 def GetBitmap(self
, status
=None):
996 """ Returns the associated bitmap. """
999 status
= self
._status
1001 if not self
.IsEnabled():
1004 if self
._bitmaps
[status
] is None:
1005 return self
._bitmaps
["Normal"]
1007 return self
._bitmaps
[status
]
1011 """ Returns the button rect. """
1016 def GetStatus(self
):
1017 """ Returns the button status. """
1023 """ Returns the button id. """
1028 def SetRect(self
, rect
):
1029 """ Sets the button rect. """
1034 def SetStatus(self
, status
):
1035 """ Sets the button status. """
1037 if status
== self
._status
:
1040 if self
.GetToggled() and status
== "Normal":
1043 self
._status
= status
1044 self
._parent
.Refresh()
1047 def GetTextAlignment(self
):
1048 """ Returns the text alignment in the button (bottom or right). """
1050 return self
._textAlignment
1053 def SetTextAlignment(self
, alignment
):
1054 """ Sets the text alignment in the button (bottom or right). """
1056 if alignment
== self
._textAlignment
:
1059 self
._alignment
= alignment
1062 def GetToggled(self
):
1063 """ Returns whether a wx.ITEM_CHECK button is toggled or not. """
1065 if self
._kind
== wx
.ITEM_NORMAL
:
1071 def SetToggled(self
, toggle
=True):
1072 """ Sets a wx.ITEM_CHECK button toggled/not toggled. """
1074 if self
._kind
== wx
.ITEM_NORMAL
:
1077 self
._toggle
= toggle
1080 def SetId(self
, id):
1081 """ Sets the button id. """
1086 def AddStatus(self
, name
="Custom", bmp
=wx
.NullBitmap
):
1088 Add a programmer-defined status in addition to the 5 default status:
1096 self
._bitmaps
.update({name: bmp}
)
1099 def Enable(self
, enable
=True):
1102 self
._status
= "Normal"
1104 self
._status
= "Disabled"
1107 def IsEnabled(self
):
1109 return self
._status
!= "Disabled"
1112 def SetText(self
, text
=""):
1113 """ Sets the text of the button. """
1119 """ Returns the text associated to the button. """
1125 """ Returns whether the button has text or not. """
1127 return self
._text
!= ""
1130 def SetKind(self
, kind
=wx
.ITEM_NORMAL
):
1131 """ Sets the button type (standard or toggle). """
1137 """ Returns the button type (standard or toggle). """
1142 Bitmap
= property(GetBitmap
, SetBitmap
)
1143 Id
= property(GetId
, SetId
)
1144 Rect
= property(GetRect
, SetRect
)
1145 Status
= property(GetStatus
, SetStatus
)
1148 # -- ButtonPanel class implementation ----------------------------------
1149 # This is the main class.
1151 class ButtonPanel(wx
.PyPanel
):
1153 def __init__(self
, parent
, id=wx
.ID_ANY
, text
="", style
=BP_DEFAULT_STYLE
,
1154 alignment
=BP_ALIGN_LEFT
, name
="buttonPanel"):
1156 Default class constructor.
1158 - parent: parent window
1160 - text: text to draw
1161 - style: window style
1162 - alignment: alignment of buttons (left or right)
1163 - name: window class name
1166 wx
.PyPanel
.__init
__(self
, parent
, id, wx
.DefaultPosition
, wx
.DefaultSize
,
1167 wx
.NO_BORDER
, name
=name
)
1170 self
._vSeparators
= []
1172 self
._nStyle
= style
1173 self
._alignment
= alignment
1175 self
._art
= BPArt(style
)
1177 self
._controlCreated
= False
1179 direction
= (self
.IsVertical() and [wx
.VERTICAL
] or [wx
.HORIZONTAL
])[0]
1180 self
._mainsizer
= BoxSizer(direction
)
1181 self
.SetSizer(self
._mainsizer
)
1183 margins
= self
._art
.GetMetric(BP_MARGINS_SIZE
)
1185 # First spacer to create some room before the first text/button/control
1186 self
._mainsizer
.Add((margins
.x
, margins
.y
), 0)
1188 # Last spacer to create some room before the last text/button/control
1189 self
._mainsizer
.Add((margins
.x
, margins
.y
), 0)
1191 self
.Bind(wx
.EVT_SIZE
, self
.OnSize
)
1192 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1193 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
1194 self
.Bind(wx
.EVT_MOTION
, self
.OnMouseMove
)
1195 self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnLeftDown
)
1196 self
.Bind(wx
.EVT_LEFT_UP
, self
.OnLeftUp
)
1197 self
.Bind(wx
.EVT_LEAVE_WINDOW
, self
.OnMouseLeave
)
1198 self
.Bind(wx
.EVT_ENTER_WINDOW
, self
.OnMouseEnterWindow
)
1200 self
.SetBarText(text
)
1204 def SetBarText(self
, text
):
1205 """ Sets the main caption text (leave text="" for no text). """
1211 if self
._controlCreated
:
1214 self
._text
= ButtonPanelText(self
, text
)
1215 lenChildren
= len(self
._mainsizer
.GetChildren())
1218 # Even if we have no text, we insert it an empty spacer anyway
1219 # it is easier to handle if you have to recreate the sizer after.
1220 if self
.IsStandard():
1221 self
._mainsizer
.Insert(1, self
._text
, 0, wx
.ALIGN_CENTER
,
1222 userData
=self
._text
, realIndex
=0)
1224 self
._mainsizer
.Insert(lenChildren
-1, self
._text
, 0, wx
.ALIGN_CENTER
,
1225 userData
=self
._text
, realIndex
=lenChildren
)
1229 # We have text, so insert the text and an expandable spacer
1230 # alongside it. "Standard" ButtonPanel are left or top aligned.
1231 if self
.IsStandard():
1232 self
._mainsizer
.Insert(1, self
._text
, 0, wx
.ALIGN_CENTER
,
1233 userData
=self
._text
, realIndex
=0)
1234 self
._mainsizer
.Insert(2, (0, 0), 1, wx
.EXPAND
)
1237 self
._mainsizer
.Insert(lenChildren
-1, self
._text
, 0, wx
.ALIGN_CENTER
,
1238 userData
=self
._text
, realIndex
=lenChildren
)
1239 self
._mainsizer
.Insert(lenChildren
-1, (0, 0), 1, wx
.EXPAND
)
1242 def RemoveText(self
):
1243 """ Removes the main caption text. """
1245 lenChildren
= len(self
._mainsizer
.GetChildren())
1246 lenCustom
= len(self
._vButtons
) + len(self
._vSeparators
) + 1
1248 if self
.IsStandard():
1250 self
._mainsizer
.Remove(1, 0)
1251 if self
.HasBarText():
1252 # Detach the expandable spacer
1253 self
._mainsizer
.Remove(1, -1)
1256 self
._mainsizer
.Remove(lenChildren
-2, lenCustom
-1)
1257 if self
.HasBarText():
1258 # Detach the expandable spacer
1259 self
._mainsizer
.Remove(lenChildren
-3, -1)
1262 def GetBarText(self
):
1263 """ Returns the main caption text. """
1265 return self
._text
.GetText()
1268 def HasBarText(self
):
1269 """ Returns whether ButtonPanel has a main caption text or not. """
1271 return hasattr(self
, "_text") and self
._text
.GetText() != ""
1274 def AddButton(self
, btnInfo
):
1276 Adds a button to ButtonPanel. Remember to pass a ButtonInfo instance to
1277 this method. See the demo for details.
1280 lenChildren
= len(self
._mainsizer
.GetChildren())
1281 self
._mainsizer
.Insert(lenChildren
-1, btnInfo
, 0, wx
.ALIGN_CENTER|wx
.EXPAND
, userData
=btnInfo
)
1283 self
._vButtons
.append(btnInfo
)
1286 def AddSpacer(self
, size
=(0, 0), proportion
=1, flag
=wx
.EXPAND
):
1287 """ Adds a spacer (stretchable or fixed-size) to ButtonPanel. """
1289 lenChildren
= len(self
._mainsizer
.GetChildren())
1290 self
._mainsizer
.Insert(lenChildren
-1, size
, proportion
, flag
)
1293 def AddControl(self
, control
, proportion
=0, flag
=wx
.ALIGN_CENTER|wx
.ALL
, border
=None):
1294 """ Adds a wxPython control to ButtonPanel. """
1296 lenChildren
= len(self
._mainsizer
.GetChildren())
1299 border
= self
._art
.GetMetric(BP_PADDING_SIZE
)
1300 border
= max(border
.x
, border
.y
)
1302 self
._mainsizer
.Insert(lenChildren
-1, control
, proportion
, flag
, border
)
1305 def AddSeparator(self
):
1306 """ Adds a separator line to ButtonPanel. """
1308 lenChildren
= len(self
._mainsizer
.GetChildren())
1309 separator
= Separator(self
)
1311 self
._mainsizer
.Insert(lenChildren
-1, separator
, 0, wx
.EXPAND
)
1312 self
._vSeparators
.append(separator
)
1315 def RemoveAllButtons(self
):
1316 """ Remove all the buttons from ButtonPanel. """
1321 def RemoveAllSeparators(self
):
1322 """ Remove all the separators from ButtonPanel. """
1324 self
._vSeparators
= []
1327 def GetAlignment(self
):
1328 """ Returns the button alignment (left, right, top, bottom). """
1330 return self
._alignment
1333 def SetAlignment(self
, alignment
):
1334 """ Sets the button alignment (left, right, top, bottom). """
1336 if alignment
== self
._alignment
:
1341 text
= self
.GetBarText()
1343 # Remove the text in any case
1346 # Remove the first and last spacers
1347 self
._mainsizer
.Remove(0, -1)
1348 self
._mainsizer
.Remove(len(self
._mainsizer
.GetChildren())-1, -1)
1350 self
._alignment
= alignment
1352 # Recreate the sizer accordingly to the new alignment
1353 self
.ReCreateSizer(text
)
1356 def IsVertical(self
):
1357 """ Returns whether ButtonPanel is vertically aligned or not. """
1359 return self
._alignment
not in [BP_ALIGN_RIGHT
, BP_ALIGN_LEFT
]
1362 def IsStandard(self
):
1363 """ Returns whether ButtonPanel is aligned "Standard" (left/top) or not. """
1365 return self
._alignment
in [BP_ALIGN_LEFT
, BP_ALIGN_TOP
]
1370 Do the Layout for ButtonPanel.
1371 NB: Call this method every time you make a modification to the layout
1372 or to the customizable sizes of the pseudo controls.
1375 margins
= self
._art
.GetMetric(BP_MARGINS_SIZE
)
1376 lenChildren
= len(self
._mainsizer
.GetChildren())
1378 self
._mainsizer
.SetItemMinSize(0, (margins
.x
, margins
.y
))
1379 self
._mainsizer
.SetItemMinSize(lenChildren
-1, (margins
.x
, margins
.y
))
1381 self
._controlCreated
= True
1384 # *VERY* WEIRD: the sizer seems not to respond to any layout until I
1385 # change the ButtonPanel size and restore it back
1386 size
= self
.GetSize()
1387 self
.SetSize((size
.x
+1, size
.y
+1))
1388 self
.SetSize((size
.x
, size
.y
))
1394 def ReCreateSizer(self
, text
):
1395 """ Recreates the ButtonPanel sizer accordingly to the alignment specified. """
1397 children
= self
._mainsizer
.GetChildren()
1398 self
.RemoveAllButtons()
1399 self
.RemoveAllSeparators()
1401 # Create a new sizer depending on the alignment chosen
1402 direction
= (self
.IsVertical() and [wx
.VERTICAL
] or [wx
.HORIZONTAL
])[0]
1403 self
._mainsizer
= BoxSizer(direction
)
1405 margins
= self
._art
.GetMetric(BP_MARGINS_SIZE
)
1406 # First spacer to create some room before the first text/button/control
1407 self
._mainsizer
.Add((margins
.x
, margins
.y
), 0)
1409 # Last spacer to create some room before the last text/button/control
1410 self
._mainsizer
.Add((margins
.x
, margins
.y
), 0)
1412 # This is needed otherwise SetBarText goes mad
1413 self
._controlCreated
= False
1415 for child
in children
:
1416 userData
= child
.GetUserData()
1418 if isinstance(userData
, ButtonInfo
):
1419 # It is a ButtonInfo, can't be anything else
1420 self
.AddButton(child
.GetUserData())
1421 elif isinstance(userData
, Separator
):
1425 if child
.IsSpacer():
1426 # This is a spacer, expandable or not
1427 self
.AddSpacer(child
.GetSize(), child
.GetProportion(),
1430 # This is a wxPython control
1431 self
.AddControl(child
.GetWindow(), child
.GetProportion(),
1432 child
.GetFlag(), child
.GetBorder())
1434 self
.SetSizer(self
._mainsizer
)
1436 # Now add the text. It doesn't matter if there is no text
1437 self
.SetBarText(text
)
1444 def DoGetBestSize(self
):
1445 """ Returns the best size of ButtonPanel. """
1447 w
= h
= btnWidth
= btnHeight
= 0
1448 isVertical
= self
.IsVertical()
1450 padding
= self
._art
.GetMetric(BP_PADDING_SIZE
)
1451 border
= self
._art
.GetMetric(BP_BORDER_SIZE
)
1452 margins
= self
._art
.GetMetric(BP_MARGINS_SIZE
)
1453 separator_size
= self
._art
.GetMetric(BP_SEPARATOR_SIZE
)
1455 # Add the space required for the main caption
1456 if self
.HasBarText():
1457 w
, h
= self
._text
.GetBestSize()
1465 # Add the button's sizes
1466 for btn
in self
._vButtons
:
1468 bw
, bh
= btn
.GetBestSize()
1469 btnWidth
= max(btnWidth
, bw
)
1470 btnHeight
= max(btnHeight
, bh
)
1473 w
= max(w
, btnWidth
)
1476 h
= max(h
, btnHeight
)
1479 # Add the control's sizes
1480 for control
in self
.GetControls():
1481 cw
, ch
= control
.GetSize()
1489 # Add the separator's sizes and the 2 SizerItems at the beginning
1491 if self
.IsVertical():
1492 h
+= 2*margins
.y
+ len(self
._vSeparators
)*separator_size
1494 w
+= 2*margins
.x
+ len(self
._vSeparators
)*separator_size
1496 return wx
.Size(w
, h
)
1499 def OnPaint(self
, event
):
1500 """ Handles the wx.EVT_PAINT event for ButtonPanel. """
1502 dc
= wx
.BufferedPaintDC(self
)
1503 rect
= self
.GetClientRect()
1505 self
._art
.DrawButtonPanel(dc
, rect
, self
._nStyle
)
1506 self
._mainsizer
.Draw(dc
)
1509 def OnEraseBackground(self
, event
):
1510 """ Handles the wx.EVT_ERASE_BACKGROUND event for ButtonPanel (does nothing). """
1515 def OnSize(self
, event
):
1516 """ Handles the wx.EVT_SIZE event for ButtonPanel. """
1518 # NOTE: It seems like LayoutItems number of calls can be optimized in some way.
1519 # Currently every DoLayout (or every parent Layout()) calls about 3 times
1520 # the LayoutItems method. Any idea on how to improve it?
1527 def LayoutItems(self
):
1529 Layout the items using a different algorithm depending on the existance
1530 of the main caption.
1533 nonspacers
, allchildren
= self
.GetNonFlexibleChildren()
1535 if self
.HasBarText():
1536 self
.FlexibleLayout(nonspacers
, allchildren
)
1538 self
.SizeLayout(nonspacers
, allchildren
)
1540 self
._mainsizer
.Layout()
1543 def SizeLayout(self
, nonspacers
, children
):
1544 """ Layout the items when no main caption exists. """
1546 size
= self
.GetSize()
1547 isVertical
= self
.IsVertical()
1550 indx1
= len(nonspacers
)
1552 for item
in nonspacers
:
1553 corner
+= self
.GetItemSize(item
, isVertical
)
1554 if corner
> size
[isVertical
]:
1555 indx1
= nonspacers
.index(item
)
1558 # Leave out the last spacer, it has to be there always
1559 for ii
in xrange(len(nonspacers
)-1):
1560 indx
= children
.index(nonspacers
[ii
])
1561 self
._mainsizer
.Show(indx
, ii
< indx1
)
1564 def GetItemSize(self
, item
, isVertical
):
1565 """ Returns the size of an item in the main ButtonPanel sizer. """
1567 if item
.GetUserData():
1568 return item
.GetUserData().GetBestSize()[isVertical
]
1570 return item
.GetSize()[isVertical
]
1573 def FlexibleLayout(self
, nonspacers
, allchildren
):
1574 """ Layout the items when the main caption exists. """
1576 if len(nonspacers
) < 2:
1579 isVertical
= self
.IsVertical()
1580 isStandard
= self
.IsStandard()
1582 size
= self
.GetSize()[isVertical
]
1583 padding
= self
._art
.GetMetric(BP_PADDING_SIZE
)
1585 fixed
= (isStandard
and [nonspacers
[1]] or [nonspacers
[-2]])[0]
1588 nonspacers
.reverse()
1589 leftendx
= fixed
.GetSize()[isVertical
] + padding
.x
1591 rightstartx
= size
- fixed
.GetSize()[isVertical
]
1594 count
= lennonspacers
= len(nonspacers
)
1596 for item
in nonspacers
:
1598 size
-= self
.GetItemSize(item
, isVertical
)
1602 size
+= self
.GetItemSize(item
, isVertical
)
1603 if size
> rightstartx
:
1608 nonspacers
.reverse()
1610 for jj
in xrange(2, lennonspacers
):
1611 indx
= allchildren
.index(nonspacers
[jj
])
1612 self
._mainsizer
.Show(indx
, jj
>= count
)
1615 def GetNonFlexibleChildren(self
):
1617 Returns all the ButtonPanel main sizer's children that are not
1622 children2
= self
._mainsizer
.GetChildren()
1624 for child
in children2
:
1625 if child
.IsSpacer():
1626 if child
.GetUserData() or child
.GetProportion() == 0:
1627 children1
.append(child
)
1629 children1
.append(child
)
1631 return children1
, children2
1634 def GetControls(self
):
1635 """ Returns the wxPython controls that belongs to ButtonPanel. """
1637 children2
= self
._mainsizer
.GetChildren()
1638 children1
= [child
for child
in children2
if not child
.IsSpacer()]
1643 def SetStyle(self
, style
):
1644 """ Sets ButtonPanel style. """
1646 if style
== self
._nStyle
:
1649 self
._nStyle
= style
1654 """ Returns the ButtonPanel style. """
1659 def OnMouseMove(self
, event
):
1660 """ Handles the wx.EVT_MOTION event for ButtonPanel. """
1662 # Check to see if we are hovering a button
1663 for btn
in self
._vButtons
:
1665 if not btn
.IsEnabled():
1668 if btn
.GetRect().Contains(event
.GetPosition()):
1669 btn
.SetStatus("Hover")
1671 btn
.SetStatus("Normal")
1677 def OnLeftDown(self
, event
):
1678 """ Handles the wx.EVT_LEFT_DOWN event for ButtonPanel. """
1680 tabId
, hit
= self
.HitTest(event
.GetPosition())
1682 if hit
== BP_HT_BUTTON
and self
._vButtons
[tabId
].IsEnabled():
1684 self
._vButtons
[tabId
].SetStatus("Pressed")
1688 def OnLeftUp(self
, event
):
1689 """ Handles the wx.EVT_LEFT_UP event for ButtonPanel. """
1691 tabId
, flags
= self
.HitTest(event
.GetPosition())
1692 hit
= self
._vButtons
[tabId
]
1694 if flags
== BP_HT_BUTTON
:
1696 if hit
.GetStatus() == "Disabled":
1699 for btn
in self
._vButtons
:
1703 if hit
.GetStatus() == "Pressed":
1704 # Fire a button click event
1705 btnEvent
= wx
.CommandEvent(wx
.wxEVT_COMMAND_BUTTON_CLICKED
, hit
.GetId())
1706 self
.GetEventHandler().ProcessEvent(btnEvent
)
1708 hit
.SetToggled(not hit
.GetToggled())
1710 # Update the button status to be hovered
1711 hit
.SetStatus("Hover")
1717 def OnMouseLeave(self
, event
):
1718 """ Handles the wx.EVT_LEAVE_WINDOW event for ButtonPanel. """
1720 # Reset all buttons statuses
1721 for btn
in self
._vButtons
:
1722 if not btn
.IsEnabled():
1724 btn
.SetStatus("Normal")
1730 def OnMouseEnterWindow(self
, event
):
1731 """ Handles the wx.EVT_ENTER_WINDOW event for ButtonPanel. """
1736 def HitTest(self
, pt
):
1738 HitTest method for ButtonPanel. Returns the button (if any) and
1744 for ii
in xrange(len(self
._vButtons
)):
1745 if not self
._vButtons
[ii
].IsEnabled():
1747 if self
._vButtons
[ii
].GetRect().Contains(pt
):
1748 return ii
, BP_HT_BUTTON
1750 return -1, BP_HT_NONE
1754 """ Returns the associated BPArt art provider. """