1 # --------------------------------------------------------------------------- # 
   2 # FOLDPANELBAR wxPython IMPLEMENTATION 
   3 # Ported From Jorgen Bodde & Julian Smart (Extended Demo) C++ Code By: 
   5 # Andrea Gavana, @ 23 Mar 2005 
   6 # Latest Revision: 28 Mar 2005, 22.30 CET 
  11 # All The C++ TODOs Are Still Alive. I Am Not Able to Read Jorges's Mind 
  12 # So I Don't Really Know What Will Be The New Features/Additions He Will 
  13 # Make On His Code. At The Moment They Are: 
  15 # 1. OnPaint Function In CaptionBar Class: 
  16 # TODO: Maybe First A Memory Dc Should Draw All, And Then Paint It On The 
  17 # Caption. This Way A Flickering Arrow During Resize Is Not Visible. 
  19 # 2. OnChar Function In CaptionBar Class: 
  20 # TODO: This Is Easy To Do But I Don't Have Any Useful Idea On Which Kind 
  21 # Of Features To Add. Does Anyone Have An Intelligent Idea? 
  23 # 3. AddFoldPanelWindow Function In FoldPanelBar Class: 
  24 # TODO: Take Old And New Heights, And If Difference, Reposition All The 
  25 # Lower Panels. This Is Because The User Can Add New wxWindow Controls 
  26 # Somewhere In Between When Other Panels Are Already Present. 
  27 # Don't Know What It Means. Probably Is My Poor English... 
  29 # 4. OnSizePanel Function In FoldPanelBar Class: 
  30 # TODO: A Smart Way To Check Wether The Old - New Width Of The 
  31 # Panel Changed, If So No Need To Resize The Fold Panel Items 
  33 # 5. Implementing Styles Like FPB_SINGLE_FOLD and FPB_EXCLUSIVE_FOLD 
  34 # TODO: Jorgen Has Left Undone These Jobs. I Don't Really Get What They 
  35 # Should Supposed To Do, So If Someone Could Enlight Me, Please Let Me Know. 
  38 # For The Original TODO List From Jorgen, Please Refer To: 
  39 # http://www.solidsteel.nl/jorg/components/foldpanel/wxFoldPanelBar.php#todo_list 
  43 # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please 
  46 # andrea.gavana@agip.it 
  49 # Or, Obviously, To The wxPython Mailing List!!! 
  53 # --------------------------------------------------------------------------- # 
  57 The `FoldPanelBar` is a control that contains multiple panels (of type 
  58 `FoldPanelItem`) that can be expanded or collapsed. The captionbar of 
  59 the FoldPanel can be customized by setting it to a horizontal gradient 
  60 style, vertical gradient style, a single color, a rectangle or filled 
  61 rectangle. The FoldPanel items can be collapsed in place or to the 
  62 bottom of the control. `wx.Window` derived controls can be added 
  63 dynamically, and separated by separator lines.  FoldPanelBar is 
  64 freeware and distributed under the wxPython license. 
  70 The internals of the FoldPanelBar is a list of FoldPanelItem objects. Through 
  71 the reference of FoldPanel these panels can be controlled by adding new controls 
  72 to a FoldPanel or adding new FoldPanels to the FoldPanelBar.  
  73 The CaptionBar fires events to the parent (container of all panel items) when a 
  74 sub-panel needs resizing (either folding or expanding). The fold or expand process 
  75 is simply a resize of the panel so it looks like all controls on it are gone. All 
  76 controls are still child of the FoldPanel they are located on. If they don't 
  77 handle the event (and they won't) then the owner of the FoldPanelBar gets the 
  78 events. This is what you need to handle the controls. There isn't much to it just 
  79 a lot of calculations to see what panel belongs where. There are no sizers 
  80 involved in the panels, everything is purely x-y positioning.  
  83 What can it do and what not? 
  84 ---------------------------- 
  87         * Run-time addition of panels (no deletion just yet)  
  88         * Run time addition of controls to the panel (it will be resized accordingly) 
  89         * Creating panels in collapsed mode or expanded mode  
  90         * Various modes of caption behaviour and filling to make it more appealing  
  91         * Panels can be folded and collapsed (or all of them) to allow more space 
  95         * Selection of a panel like in a list ctrl  
  96         * Dragging and dropping the panels  
  97         * Re-ordering the panels (not yet)   
 103 FoldPanelBar is supported on the following platforms:  
 104   * Windows (Verified on Windows XP, 2000) 
 105   * Linux/Unix (GTK2) (Thanks To Toni Brkic And Robin Dunn) 
 106   * Mac OSX (Thanks To Robin Dunn For The CaptionBar Size Patch) 
 109 Latest Revision: Andrea Gavana @ 30 Mar 2005, 22.30 CET 
 115 #---------------------------------------------------------------------- 
 116 # Collapsed And Expanded Bitmap Images 
 117 # Created With img2py.py  
 118 #---------------------------------------------------------------------- 
 120 def GetCollapsedIconData(): 
 122 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ 
 123 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ 
 124 \x00\x007IDAT8\x8dcddbf\xa0\x040Q\xa4{\xf0\x1b\xf0\xff\xdf\xdf\xff\x03\xe7\ 
 125 \x02\x98\xed\x84\\A\x1b\x17\xa0\xdb\x8a\xcf\x15\xd4w\x01.\xdbp\x89S\xec\x02\ 
 126 \xc6\xd1\xbc\xc0\x00\x00\x9a\xf5\x1b\xfa\xf9m$?\x00\x00\x00\x00IEND\xaeB`\ 
 129 def GetCollapsedIconBitmap(): 
 130     return wx
.BitmapFromImage(GetCollapsedIconImage()) 
 132 def GetCollapsedIconImage(): 
 134     stream 
= cStringIO
.StringIO(GetCollapsedIconData()) 
 135     return wx
.ImageFromStream(stream
) 
 137 #---------------------------------------------------------------------- 
 138 def GetExpandedIconData(): 
 140 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ 
 141 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ 
 142 \x00\x00BIDAT8\x8dcddbf\xa0\x040Q\xa4{P\x18\xc0\x82.\xf0\xff\xdf\xdf\xff\xb8\ 
 143 \x143213R\xdd\x05\x18\x06`\xb3\x05\x9f8m\x02\x11\xdd6\\\xb6\xd3\xce\x05\xc8\ 
 144 \xb6\xe2\xb3\x9d*.`\x1c\xcd\x0b\x0c\x00\x9e\xbc\x04W\x19\xcfa\xb5\x00\x00\ 
 145 \x00\x00IEND\xaeB`\x82'  
 147 def GetExpandedIconBitmap(): 
 148     return wx
.BitmapFromImage(GetExpandedIconImage()) 
 150 def GetExpandedIconImage(): 
 152     stream 
= cStringIO
.StringIO(GetExpandedIconData()) 
 153     return wx
.ImageFromStream(stream
) 
 155 #---------------------------------------------------------------------- 
 157 #---------------------------------------------------------------------- 
 158 # FOLDPANELBAR Starts Here 
 159 #---------------------------------------------------------------------- 
 163 #- CAPTIONBAR_GRADIENT_V: Draws a vertical gradient from top to bottom 
 164 #- CAPTIONBAR_GRADIENT_H: Draws a horizontal gradient from left to right 
 165 #- CAPTIONBAR_SINGLE: Draws a single filled rectangle to draw the caption 
 166 #- CAPTIONBAR_RECTANGLE: Draws a single colour with a rectangle around the caption 
 167 #- CAPTIONBAR_FILLED_RECTANGLE: Draws a filled rectangle and a border around it 
 169 CAPTIONBAR_NOSTYLE            
= 0 
 170 CAPTIONBAR_GRADIENT_V         
= 1 
 171 CAPTIONBAR_GRADIENT_H         
= 2 
 172 CAPTIONBAR_SINGLE             
= 3 
 173 CAPTIONBAR_RECTANGLE          
= 4 
 174 CAPTIONBAR_FILLED_RECTANGLE   
= 5 
 179 # pixels of the bmp to be aligned from the right filled with space 
 180 FPB_BMP_RIGHTSPACE 
= 2 
 182 # Not yet supported but added for future reference. Single fold forces 
 183 # other panels to close when they are open, and only opens the current panel. 
 184 # This will allow the open panel to gain the full size left in the client area 
 185 FPB_SINGLE_FOLD 
= 0x0001 
 187 # All panels are stacked to the bottom. When they are expanded again they 
 189 FPB_COLLAPSE_TO_BOTTOM 
= 0x0002 
 191 # Not yet supported, but added for future reference. Single fold plus panels 
 192 # will be stacked at the bottom 
 193 FPB_EXCLUSIVE_FOLD 
= FPB_SINGLE_FOLD | FPB_COLLAPSE_TO_BOTTOM
 
 196 FPB_HORIZONTAL 
= wx
.HORIZONTAL
 
 197 FPB_VERTICAL 
= wx
.VERTICAL  
 
 199 # Default Extrastyle of the FoldPanelBar  
 200 FPB_DEFAULT_EXTRASTYLE 
= 0 
 201 # Default style of the FoldPanelBar  
 202 FPB_DEFAULT_STYLE 
= wx
.TAB_TRAVERSAL | wx
.NO_BORDER
 
 204 # FoldPanelItem default settings 
 208 FPB_DEFAULT_LEFTSPACING 
= 5 
 209 FPB_DEFAULT_RIGHTSPACING 
= 10 
 210 FPB_DEFAULT_SPACING 
= 8 
 212 FPB_DEFAULT_LEFTLINESPACING 
= 2 
 213 FPB_DEFAULT_RIGHTLINESPACING 
= 2 
 216 # ------------------------------------------------------------------------------ # 
 217 # class CaptionBarStyle 
 218 # ------------------------------------------------------------------------------ # 
 220 class CaptionBarStyle
: 
 222     This class encapsulates the styles you wish to set for the 
 223     `CaptionBar` (this is the part of the FoldPanel where the caption 
 224     is displayed). It can either be applied at creation time be 
 225     reapplied when styles need to be changed. 
 227     At construction time, all styles are set to their default 
 228     transparency.  This means none of the styles will be applied to 
 229     the `CaptionBar` in question, meaning it will be created using the 
 230     default internals. When setting i.e the color, font or panel 
 231     style, these styles become active to be used. 
 236         """ Default constructor for this class.""" 
 241     def ResetDefaults(self
): 
 242         """ Resets default CaptionBarStyle."""         
 243         self
._firstColourUsed 
= False 
 244         self
._secondColourUsed 
= False 
 245         self
._textColourUsed 
= False 
 246         self
._captionFontUsed 
= False 
 247         self
._captionStyleUsed 
= False 
 248         self
._captionStyle 
= CAPTIONBAR_GRADIENT_V
 
 251     # ------- CaptionBar Font ------- 
 253     def SetCaptionFont(self
, font
): 
 255         Sets font for the caption bar. 
 257         If this is not set, the font property is undefined and will 
 258         not be used. Use `CaptionFontUsed` to check if this style is 
 261         self
._captionFont 
= font
 
 262         self
._captionFontUsed 
= True 
 265     def CaptionFontUsed(self
): 
 266         """ Checks if the caption bar font is set. """         
 267         return self
._captionFontUsed
 
 270     def GetCaptionFont(self
): 
 272         Returns the font for the caption bar. 
 274         Please be warned this will result in an assertion failure when 
 275         this property is not previously set. 
 277         :see: `SetCaptionFont`, `CaptionFontUsed` 
 279         return self
._captionFont
 
 282     # ------- First Colour ------- 
 284     def SetFirstColour(self
, colour
): 
 286         Sets first colour for the caption bar. 
 288         If this is not set, the colour property is undefined and will 
 289         not be used. Use `FirstColourUsed` to check if this style is 
 292         self
._firstColour 
= colour
 
 293         self
._firstColourUsed 
= True 
 296     def FirstColourUsed(self
): 
 297         """ Checks if the first colour of the caption bar is set."""         
 298         return self
._firstColourUsed
 
 301     def GetFirstColour(self
): 
 303         Returns the first colour for the caption bar. 
 305         Please be warned this will result in an assertion failure when 
 306         this property is not previously set. 
 308         :see: `SetFirstColour`, `FirstColourUsed` 
 310         return self
._firstColour
 
 313     # ------- Second Colour ------- 
 315     def SetSecondColour(self
, colour
): 
 317         Sets second colour for the caption bar. 
 319         If this is not set, the colour property is undefined and will 
 320         not be used. Use `SecondColourUsed` to check if this style is 
 323         self
._secondColour 
= colour
 
 324         self
._secondColourUsed 
= True 
 327     def SecondColourUsed(self
): 
 328         """ Checks if the second colour of the caption bar is set."""         
 329         return self
._secondColourUsed
 
 332     def GetSecondColour(self
): 
 334         Returns the second colour for the caption bar. 
 336         Please be warned this will result in an assertion failure when 
 337         this property is not previously set. 
 339         :see: `SetSecondColour`, `SecondColourUsed` 
 341         return self
._secondColour
 
 344     # ------- Caption Text Colour ------- 
 346     def SetCaptionColour(self
, colour
): 
 348         Sets caption colour for the caption bar. 
 350         If this is not set, the colour property is undefined and will 
 351         not be used. Use `CaptionColourUsed` to check if this style is 
 354         self
._textColour 
= colour
 
 355         self
._textColourUsed 
= True 
 358     def CaptionColourUsed(self
): 
 359         """ Checks if the caption colour of the caption bar is set."""         
 360         return self
._textColourUsed
 
 363     def GetCaptionColour(self
): 
 365         Returns the caption colour for the caption bar. 
 367         Please be warned this will result in an assertion failure 
 368         when this property is not previously set. 
 369         See also SetCaptionColour(), CaptionColourUsed() 
 371         return self
._textColour
 
 374     # ------- CaptionStyle  ------- 
 376     def SetCaptionStyle(self
, style
): 
 378         Sets caption style for the caption bar. 
 380         If this is not set, the property is undefined and will not be 
 381         used. Use CaptionStyleUsed() to check if this style is used. 
 382         The following styles can be applied: 
 384             * CAPTIONBAR_GRADIENT_V: Draws a vertical gradient from top to bottom 
 386             * CAPTIONBAR_GRADIENT_H: Draws a horizontal gradient from 
 389             * CAPTIONBAR_SINGLE: Draws a single filled rectangle to 
 392             * CAPTIONBAR_RECTANGLE: Draws a single colour with a 
 393               rectangle around the caption 
 395             * CAPTIONBAR_FILLED_RECTANGLE: Draws a filled rectangle 
 396               and a border around it 
 399         self
._captionStyle 
= style
 
 400         self
._captionStyleUsed 
= True 
 403     def CaptionStyleUsed(self
): 
 404         """ Checks if the caption style of the caption bar is set."""         
 405         return self
._captionStyleUsed
 
 408     def GetCaptionStyle(self
): 
 410         Returns the caption style for the caption bar. 
 412         Please be warned this will result in an assertion failure 
 413         when this property is not previously set. 
 415         :see: `SetCaptionStyle`, `CaptionStyleUsed` 
 417         return self
._captionStyle
 
 420 #-----------------------------------# 
 422 #-----------------------------------# 
 423 wxEVT_CAPTIONBAR 
= wx
.NewEventType() 
 424 EVT_CAPTIONBAR 
= wx
.PyEventBinder(wxEVT_CAPTIONBAR
, 0) 
 427 # ---------------------------------------------------------------------------- # 
 428 # class CaptionBarEvent 
 429 # ---------------------------------------------------------------------------- # 
 431 class CaptionBarEvent(wx
.PyCommandEvent
): 
 433     This event will be sent when a EVT_CAPTIONBAR is mapped in the parent. 
 434     It is to notify the parent that the bar is now in collapsed or expanded 
 435     state. The parent should re-arrange the associated windows accordingly 
 437     def __init__(self
, evtType
): 
 438         """ Default Constructor For This Class."""         
 439         wx
.PyCommandEvent
.__init
__(self
, evtType
) 
 442     def GetFoldStatus(self
): 
 444         Returns whether the bar is expanded or collapsed. True means 
 447         return not self
._bar
.IsCollapsed() 
 451         """ Returns The CaptionBar Selected."""         
 455     def SetTag(self
, tag
): 
 456         """ Assign A Tag To The Selected CaptionBar."""         
 461         """ Returns The Tag Assigned To The Selected CaptionBar."""         
 465     def SetBar(self
, bar
): 
 467         Sets the bar associated with this event. 
 469         Should not used by any other then the originator of the event. 
 474 # -------------------------------------------------------------------------------- # 
 476 # -------------------------------------------------------------------------------- # 
 478 class CaptionBar(wx
.Window
): 
 480     This class is a graphical caption component that consists of a 
 481     caption and a clickable arrow. 
 483     The CaptionBar fires an event EVT_CAPTIONBAR which is a 
 484     `CaptionBarEvent`.  This event can be caught and the parent window 
 485     can act upon the collapsed or expanded state of the bar (which is 
 486     actually just the icon which changed).  The parent panel can 
 487     reduce size or expand again. 
 490     # Define Empty CaptionBar Style 
 491     EmptyCaptionBarStyle 
= CaptionBarStyle() 
 493     def __init__(self
, parent
, id, pos
, size
, caption
="", 
 494                  foldIcons
=None, cbstyle
=EmptyCaptionBarStyle
, 
 495                  rightIndent
=FPB_BMP_RIGHTSPACE
, 
 496                  iconWidth
=16, iconHeight
=16, collapsed
=False): 
 497         """ Default Class Constructor.""" 
 499         wx
.Window
.__init
__(self
, parent
, wx
.ID_ANY
, pos
=wx
.DefaultPosition
, 
 500                            size
=(20,20), style
=wx
.NO_BORDER
) 
 502         self
._controlCreated 
= False 
 503         self
._collapsed 
= collapsed
 
 504         self
.ApplyCaptionStyle(cbstyle
, True) 
 506         if foldIcons 
is None: 
 507             foldIcons 
= wx
.ImageList(16, 16) 
 509             bmp 
= GetExpandedIconBitmap() 
 511             bmp 
= GetCollapsedIconBitmap() 
 516             assert foldIcons
.GetImageCount() > 1 
 517             iconWidth
, iconHeight 
= foldIcons
.GetSize(0) 
 519         self
._caption 
= caption
 
 520         self
._foldIcons 
= foldIcons
 
 521         self
._style 
= cbstyle
 
 522         self
._rightIndent 
= rightIndent
 
 523         self
._iconWidth 
= iconWidth
 
 524         self
._iconHeight 
= iconHeight
 
 525         self
._oldSize 
= wx
.Size(20,20) 
 527         self
._controlCreated 
= True 
 529         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 530         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 531         self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouseEvent
) 
 532         self
.Bind(wx
.EVT_CHAR
, self
.OnChar
) 
 535     def ApplyCaptionStyle(self
, cbstyle
=EmptyCaptionBarStyle
, applyDefault
=True): 
 536         """ Applies the style defined in cbstyle to the CaptionBar.""" 
 542             # get first colour from style or make it default 
 543             if not newstyle
.FirstColourUsed(): 
 544                 newstyle
.SetFirstColour(wx
.WHITE
) 
 546             # get second colour from style or make it default 
 547             if not newstyle
.SecondColourUsed(): 
 548                 # make the second colour slightly darker then the background 
 549                 color 
= self
.GetParent().GetBackgroundColour() 
 550                 r
, g
, b 
= int(color
.Red()), int(color
.Green()), int(color
.Blue()) 
 551                 color 
= ((r 
>> 1) + 20, (g 
>> 1) + 20, (b 
>> 1) + 20) 
 552                 newstyle
.SetSecondColour(wx
.Colour(color
[0], color
[1], color
[2])) 
 555             if not newstyle
.CaptionColourUsed(): 
 556                 newstyle
.SetCaptionColour(wx
.BLACK
) 
 559             if not newstyle
.CaptionFontUsed(): 
 560                 newstyle
.SetCaptionFont(self
.GetParent().GetFont()) 
 562             # apply caption style 
 563             if not newstyle
.CaptionStyleUsed(): 
 564                 newstyle
.SetCaptionStyle(CAPTIONBAR_GRADIENT_V
) 
 566         self
._style 
= newstyle
 
 569     def SetCaptionStyle(self
, cbstyle
=EmptyCaptionBarStyle
, applyDefault
=True): 
 571         Sets CaptionBar styles with CapionBarStyle class. 
 573         All styles that are actually set, are applied. If you set 
 574         applyDefault to True, all other (not defined) styles will be 
 575         set to default. If it is False, the styles which are not set 
 576         in the CaptionBarStyle will be ignored. 
 578         self
.ApplyCaptionStyle(cbstyle
, applyDefault
) 
 582     def GetCaptionStyle(self
): 
 584         Returns the current style of the captionbar in a 
 585         `CaptionBarStyle` class. 
 587         This can be used to change and set back the changes. 
 592     def IsCollapsed(self
): 
 594         Returns wether the status of the bar is expanded or collapsed. 
 596         return self
._collapsed
 
 599     def SetRightIndent(self
, pixels
): 
 601         Sets the amount of pixels on the right from which the bitmap 
 604         If this is 0, it will be drawn all the way to the right, 
 605         default is equal to FPB_BMP_RIGHTSPACE. Assign this before 
 606         assigning an image list to prevent a redraw. 
 609         self
._rightIndent 
= pixels
 
 616         This sets the internal state / representation to collapsed. 
 618         This does not trigger a `CaptionBarEvent` to be sent to the 
 621         self
._collapsed 
= True 
 622         self
.RedrawIconBitmap() 
 627         This sets the internal state / representation to expanded. 
 629         This does not trigger a `CaptionBarEvent` to be sent to the 
 632         self
._collapsed 
= False 
 633         self
.RedrawIconBitmap() 
 636     def SetBoldFont(self
): 
 637         """ Sets the CaptionBarFont weight to BOLD.""" 
 639         self
.GetFont().SetWeight(wx
.BOLD
) 
 642     def SetNormalFont(self
): 
 643         """ Sets the CaptionBarFont weight to NORMAL.""" 
 645         self
.GetFont().SetWeight(wx
.NORMAL
) 
 648     def IsVertical(self
): 
 650         Returns wether the CaptionBar Has Default Orientation Or Not. 
 655         fld 
= self
.GetParent().GetGrandParent() 
 656         if isinstance(fld
, FoldPanelBar
): 
 657             return self
.GetParent().GetGrandParent().IsVertical() 
 659             raise "ERROR: Wrong Parent " + repr(fld
) 
 662     def OnPaint(self
, event
): 
 663         """ The paint event for flat or gradient fill. """ 
 665         if not self
._controlCreated
: 
 669         dc 
= wx
.PaintDC(self
) 
 670         wndRect 
= self
.GetRect() 
 671         vertical 
= self
.IsVertical() 
 673         # TODO: Maybe first a memory DC should draw all, and then paint it on 
 674         # the caption. This way a flickering arrow during resize is not visible 
 676         self
.FillCaptionBackground(dc
) 
 677         dc
.SetFont(self
._style
.GetCaptionFont()) 
 680             dc
.DrawText(self
._caption
, 4, FPB_EXTRA_Y
/2) 
 682             dc
.DrawRotatedText(self
._caption
, FPB_EXTRA_Y
/2, 
 683                                wndRect
.GetBottom() - 4, 90) 
 685         # draw small icon, either collapsed or expanded 
 686         # based on the state of the bar. If we have any bmp's 
 690             index 
= self
._collapsed
 
 693                 drw 
= wndRect
.GetRight() - self
._iconWidth 
- self
._rightIndent
 
 694                 self
._foldIcons
.Draw(index
, dc
, drw
, 
 695                                      (wndRect
.GetHeight() - self
._iconHeight
)/2, 
 696                                      wx
.IMAGELIST_DRAW_TRANSPARENT
) 
 698                 self
._foldIcons
.Draw(index
, dc
, 
 699                                      (wndRect
.GetWidth() - self
._iconWidth
)/2, 
 700                                      self
._rightIndent
, wx
.IMAGELIST_DRAW_TRANSPARENT
) 
 705     def FillCaptionBackground(self
, dc
): 
 707         Fills the background of the caption with either a gradient or 
 711         style 
= self
._style
.GetCaptionStyle() 
 713         if style 
== CAPTIONBAR_GRADIENT_V
: 
 714             if self
.IsVertical(): 
 715                 self
.DrawVerticalGradient(dc
, self
.GetRect()) 
 717                 self
.DrawHorizontalGradient(dc
, self
.GetRect()) 
 719         elif style 
== CAPTIONBAR_GRADIENT_H
: 
 720             if self
.IsVertical(): 
 721                 self
.DrawHorizontalGradient(dc
, self
.GetRect()) 
 723                 self
.DrawVerticalGradient(dc
, self
.GetRect()) 
 725         elif style 
== CAPTIONBAR_SINGLE
: 
 726             self
.DrawSingleColour(dc
, self
.GetRect()) 
 727         elif style 
== CAPTIONBAR_RECTANGLE 
or style 
== CAPTIONBAR_FILLED_RECTANGLE
: 
 728             self
.DrawSingleRectangle(dc
, self
.GetRect()) 
 730             raise "STYLE Error: Undefined Style Selected: " + repr(style
) 
 733     def OnMouseEvent(self
, event
): 
 735         Catches the mouse click-double click. 
 737         If clicked on the arrow (single) or double on the caption we 
 738         change state and an event must be fired to let this panel 
 744         if event
.LeftDown() and self
._foldIcons
: 
 746             pt 
= event
.GetPosition() 
 747             rect 
= self
.GetRect() 
 748             vertical 
= self
.IsVertical() 
 750             drw 
= (rect
.GetWidth() - self
._iconWidth 
- self
._rightIndent
) 
 751             if vertical 
and pt
.x 
> drw 
or not vertical 
and \
 
 752                pt
.y 
< (self
._iconHeight 
+ self
._rightIndent
): 
 755         elif event
.LeftDClick(): 
 758         # send the collapse, expand event to the parent 
 761             event 
= CaptionBarEvent(wxEVT_CAPTIONBAR
) 
 762             event
.SetId(self
.GetId()) 
 763             event
.SetEventObject(self
) 
 765             self
.GetEventHandler().ProcessEvent(event
) 
 768     def OnChar(self
, event
): 
 769         """ Unused Methods. Any Ideas?!?""" 
 770         # TODO: Anything here? 
 774     def DoGetBestSize(self
): 
 776         Returns the best size for this panel, based upon the font 
 777         assigned to this window, and the caption string 
 780         if self
.IsVertical(): 
 781             x
, y 
= self
.GetTextExtent(self
._caption
) 
 783             y
, x 
= self
.GetTextExtent(self
._caption
) 
 785         if x 
< self
._iconWidth
: 
 788         if y 
< self
._iconHeight
: 
 791         # TODO: The extra FPB_EXTRA_X constants should be adjustable as well 
 793         return wx
.Size(x 
+ FPB_EXTRA_X
, y 
+ FPB_EXTRA_Y
) 
 796     def DrawVerticalGradient(self
, dc
, rect
): 
 797         """ Gradient fill from colour 1 to colour 2 with top to bottom. """ 
 799         if  rect
.height 
< 1 or rect
.width 
< 1: 
 802         dc
.SetPen(wx
.TRANSPARENT_PEN
) 
 804         # calculate gradient coefficients 
 805         col2 
= self
._style
.GetSecondColour() 
 806         col1 
= self
._style
.GetFirstColour() 
 808         r1
, g1
, b1 
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue()) 
 809         r2
, g2
, b2 
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue()) 
 811         flrect 
= float(rect
.height
) 
 813         rstep 
= float((r2 
- r1
)) / flrect
 
 814         gstep 
= float((g2 
- g1
)) / flrect
 
 815         bstep 
= float((b2 
- b1
)) / flrect
 
 819         for y 
in range(rect
.y
, rect
.y 
+ rect
.height
): 
 820             currCol 
= (r1 
+ rf
, g1 
+ gf
, b1 
+ bf
) 
 822             dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
)) 
 823             dc
.DrawRectangle(rect
.x
, rect
.y 
+ (y 
- rect
.y
), rect
.width
, rect
.height
) 
 829     def DrawHorizontalGradient(self
, dc
, rect
): 
 830         """ Gradient fill from colour 1 to colour 2 with left to right. """ 
 832         if rect
.height 
< 1 or rect
.width 
< 1: 
 835         dc
.SetPen(wx
.TRANSPARENT_PEN
) 
 837         # calculate gradient coefficients 
 838         col2 
= self
._style
.GetSecondColour() 
 839         col1 
= self
._style
.GetFirstColour() 
 841         r1
, g1
, b1 
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue()) 
 842         r2
, g2
, b2 
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue()) 
 844         flrect 
= float(rect
.width
) 
 846         rstep 
= float((r2 
- r1
)) / flrect
 
 847         gstep 
= float((g2 
- g1
)) / flrect
 
 848         bstep 
= float((b2 
- b1
)) / flrect
 
 852         for x 
in range(rect
.x
, rect
.x 
+ rect
.width
): 
 853             currCol 
= (r1 
+ rf
, g1 
+ gf
, b1 
+ bf
) 
 855             dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
)) 
 856             dc
.DrawRectangle(rect
.x 
+ (x 
- rect
.x
), rect
.y
, 1, rect
.height
) 
 862     def DrawSingleColour(self
, dc
, rect
): 
 863         """ Single colour fill. This is the most easy one to find. """ 
 865         if rect
.height 
< 1 or rect
.width 
< 1: 
 868         dc
.SetPen(wx
.TRANSPARENT_PEN
) 
 870         # draw simple rectangle 
 871         dc
.SetBrush(wx
.Brush(self
._style
.GetFirstColour(), wx
.SOLID
)) 
 872         dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, rect
.height
) 
 875     def DrawSingleRectangle(self
, dc
, rect
): 
 876         """ Single rectangle. This is the most easy one to find. """ 
 878         if rect
.height 
< 2 or rect
.width 
< 1: 
 881         # single frame, set up internal fill colour 
 883         if self
._style
.GetCaptionStyle() == CAPTIONBAR_RECTANGLE
: 
 884             color 
= self
.GetParent().GetBackgroundColour() 
 885             br 
= wx
.Brush(color
, wx
.SOLID
) 
 887             color 
= self
._style
.GetFirstColour() 
 888             br 
= wx
.Brush(color
, wx
.SOLID
) 
 890         # setup the pen frame 
 892         pen 
= wx
.Pen(self
._style
.GetSecondColour()) 
 895         dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, rect
.height 
- 1) 
 897         bgpen 
= wx
.Pen(self
.GetParent().GetBackgroundColour()) 
 899         dc
.DrawLine(rect
.x
, rect
.y 
+ rect
.height 
- 1, rect
.x 
+ rect
.width
, 
 900                     rect
.y 
+ rect
.height 
- 1) 
 903     def OnSize(self
, event
): 
 904         """ Handles the size events for the CaptionBar.""" 
 906         if not self
._controlCreated
: 
 910         size 
= event
.GetSize() 
 914             # What I am doing here is simply invalidating the part of the window 
 915             # exposed. So when I make a rect with as width the newly exposed part, 
 916             # and the x,y of the old window size origin, I don't need a bitmap 
 917             # calculation in it, or do I ? The bitmap needs redrawing anyway.  
 918             # Leave it like this until I figured it out. 
 920             # set rect to redraw as old bitmap area which is entitled to redraw 
 922             rect 
= wx
.Rect(size
.GetWidth() - self
._iconWidth 
- self
._rightIndent
, 0, 
 923                            self
._iconWidth 
+ self
._rightIndent
, 
 924                            self
._iconWidth 
+ self
._rightIndent
) 
 926             # adjust rectangle when more is slided so we need to redraw all 
 927             # the old stuff but not all (ugly flickering) 
 929             diffX 
= size
.GetWidth() - self
._oldSize
.GetWidth() 
 933                 # adjust the rect with all the crap to redraw 
 935                 rect
.SetWidth(rect
.GetWidth() + diffX 
+ 10) 
 936                 rect
.SetX(rect
.GetX() - diffX 
- 10) 
 938             self
.RefreshRect(rect
) 
 942             rect 
= self
.GetRect() 
 943             self
.RefreshRect(rect
) 
 948     def RedrawIconBitmap(self
): 
 949         """ Redraws the icons (if they exists). """ 
 953             # invalidate the bitmap area and force a redraw 
 955             rect 
= self
.GetRect() 
 957             rect
.SetX(rect
.GetWidth() - self
._iconWidth 
- self
._rightIndent
) 
 958             rect
.SetWidth(self
._iconWidth 
+ self
._rightIndent
) 
 959             self
.RefreshRect(rect
) 
 962 # ---------------------------------------------------------------------------------- # 
 964 # ---------------------------------------------------------------------------------- # 
 966 class FoldPanelBar(wx
.Panel
): 
 968     The FoldPanelBar is a class which can maintain a list of 
 969     collapsable panels.  Once a panel is collapsed, only it's caption 
 970     bar is visible to the user. This will provide more space for the 
 971     other panels, or allow the user to close panels which are not used 
 972     often to get the most out of the work area. 
 974     This control is easy to use. Simply create it as a child for a 
 975     panel or sash window, and populate panels with 
 976     `AddFoldPanel`. Then use the AdddFoldPanelWindow` to add 
 977     `wx.Window` derived controls to the current fold panel. Use 
 978     `AddFoldPanelSeparator` to put separators between the groups of 
 979     controls that need a visual separator to group them 
 980     together. After all is constructed, the user can fold the panels 
 981     by doubleclicking on the bar or single click on the arrow, which 
 982     will indicate the collapsed or expanded state. 
 984     # Define Empty CaptionBar Style 
 985     EmptyCaptionBarStyle 
= CaptionBarStyle() 
 987     def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
, 
 988                  style
=FPB_DEFAULT_STYLE
, extraStyle
=FPB_DEFAULT_EXTRASTYLE
):  
 989         """ Default Class Constructor. """ 
 991         self
._controlCreated 
= False 
 992         self
._extraStyle 
= extraStyle
 
 994         # make sure there is any orientation 
 995         if style 
& FPB_HORIZONTAL 
!= FPB_HORIZONTAL
: 
 996             style 
= style | FPB_VERTICAL
 
 998         if style 
& FPB_HORIZONTAL 
== 4: 
 999             self
._isVertical 
= False 
1001             self
._isVertical 
= True 
1004         # create the panel (duh!). This causes a size event, which we are going 
1005         # to skip when we are not initialised 
1007         wx
.Panel
.__init
__(self
, parent
, id, pos
, size
, style
) 
1009         # the fold panel area 
1011         self
._foldPanel 
= wx
.Panel(self
, wx
.ID_ANY
, pos
, size
, 
1012                                    wx
.NO_BORDER | wx
.TAB_TRAVERSAL
) 
1014         self
._controlCreated 
= True 
1017         self
.Bind(EVT_CAPTIONBAR
, self
.OnPressCaption
) 
1018         self
.Bind(wx
.EVT_SIZE
, self
.OnSizePanel
) 
1021     def AddFoldPanel(self
, caption
="", collapsed
=False, foldIcons
=None, 
1022                      cbstyle
=EmptyCaptionBarStyle
): 
1024         Adds a fold panel to the list of panels. 
1026         If the flag collapsed is set to True, the panel is collapsed 
1027         initially.  The FoldPanel item which is returned, can be used 
1028         as a reference to perform actions upon the fold panel like 
1029         collapsing it, expanding it, or deleting it from the list. 
1031         Use this foldpanel to add windows to it. Please consult 
1032         `AddFoldPanelWindow` and `AddFoldPanelSeparator` to know how 
1033         to add items derived from `wx.Window` to the panels. 
1036         # create a fold panel item, which is first only the caption. 
1037         # the user can now add a panel area which will be folded in 
1040         if foldIcons 
is None: 
1041             foldIcons 
= wx
.ImageList(16, 16) 
1043             bmp 
= GetExpandedIconBitmap() 
1045             bmp 
= GetCollapsedIconBitmap() 
1048         item 
= FoldPanelItem(self
._foldPanel
, -1, caption
=caption
, 
1049                              foldIcons
=foldIcons
, 
1050                              collapsed
=collapsed
, cbstyle
=cbstyle
) 
1053         if len(self
._panels
) > 0: 
1054             pos 
= self
._panels
[-1].GetItemPos() + self
._panels
[-1].GetPanelLength() 
1056         item
.Reposition(pos
) 
1057         self
._panels
.append(item
) 
1062     def AddFoldPanelWindow(self
, panel
, window
, flags
=FPB_ALIGN_WIDTH
, 
1063                            Spacing
=FPB_DEFAULT_SPACING
, 
1064                            leftSpacing
=FPB_DEFAULT_LEFTLINESPACING
, 
1065                            rightSpacing
=FPB_DEFAULT_RIGHTLINESPACING
): 
1067         Adds a `wx.Window` derived instance to the referenced 
1070         IMPORTANT: Make the window be a child of the FoldPanel. See 
1071         example that follows. The flags to be used are: 
1073             * FPB_ALIGN_WIDTH: Which means the wxWindow to be added 
1074               will be aligned to fit the width of the FoldPanel when 
1075               it is resized.  Very handy for sizer items, buttons and 
1078             * FPB_ALIGN_LEFT: Aligns left instead of fitting the 
1079               width of the child window to be added. Use either this 
1080               one or FPB_ALIGN_WIDTH. 
1082         The wx.Window to be added can be slightly indented from left 
1083         and right so it is more visibly placed in the FoldPanel. Use 
1084         Spacing > 0 to give the control an y offset from the previous 
1085         wx.Window added, use leftSpacing to give it a slight indent 
1086         from the left, and rightSpacing also reserves a little space 
1087         on the right so the wxWindow can be properly placed in the 
1090         The following example adds a FoldPanel to the FoldPanelBar and 
1091         adds two wx.Window derived controls to the FoldPanel:: 
1093             # create the FoldPanelBar 
1094             >>> m_pnl = FoldPanelBar(self, wx.ID_ANY, wx.DefaultPosition, 
1095                                      wx.DefaultSize, FPB_DEFAULT_STYLE, 
1096                                      FPB_COLLAPSE_TO_BOTTOM) 
1098             # add a foldpanel to the control. "Test me" is the caption and it is 
1099             # initially not collapsed. 
1100             >>> item = m_pnl.AddFoldPanel("Test me", False) 
1102             # now add a button to the fold panel. Mind that the button should be 
1103             # made child of the FoldPanel and not of the main form. 
1104             >>> m_pnl.AddFoldPanelWindow(item, wx.Button(item, ID_COLLAPSEME, 
1107             # add a separator between the two controls. This is purely a visual 
1108             # line that can have a certain color and also the indents and width 
1109             # aligning like a control. 
1110             >>> m_pnl.AddFoldPanelSeparator(item) 
1112             # now add a text ctrl. Also very easy. Align this on width so that 
1113             # when the control gets wider the text control also sizes along. 
1114             >>> m_pnl.AddFoldPanelWindow(item, wx.TextCtrl(item, wx.ID_ANY, "Comment"), 
1115                                          FPB_ALIGN_WIDTH, FPB_DEFAULT_SPACING, 20) 
1120             item 
= self
._panels
.index(panel
) 
1122             raise "ERROR: Invalid Panel Passed To AddFoldPanelWindow: " + repr(panel
) 
1124         panel
.AddWindow(window
, flags
, Spacing
, leftSpacing
, rightSpacing
) 
1126         # TODO: Take old and new height, and if difference, reposition all the lower 
1127         # panels this is because the user can add new wxWindow controls somewhere in 
1128         # between when other panels are already present. 
1133     def AddFoldPanelSeparator(self
, panel
, colour
=wx
.BLACK
, 
1134                               Spacing
=FPB_DEFAULT_SPACING
, 
1135                               leftSpacing
=FPB_DEFAULT_LEFTLINESPACING
, 
1136                               rightSpacing
=FPB_DEFAULT_RIGHTLINESPACING
): 
1138         Adds a separator line to the current FoldPanel. 
1140         The seperator is a simple line which is drawn and is no real 
1141         component.  It can be used to separate groups of controls 
1142         which belong to each other.  The colour is adjustable, and it 
1143         takes the same Spacing, leftSpacing and rightSpacing as 
1144         `AddFoldPanelWindow`. 
1148             item 
= self
._panels
.index(panel
) 
1150             raise "ERROR: Invalid Panel Passed To AddFoldPanelSeparator: " + repr(panel
) 
1152         panel
.AddSeparator(colour
, Spacing
, leftSpacing
, rightSpacing
) 
1156     def OnSizePanel(self
, event
): 
1157         """ Handles the EVT_SIZE event for the FoldPanelBar. """ 
1159         # skip all stuff when we are not initialised yet 
1161         if not self
._controlCreated
: 
1165         foldrect 
= self
.GetRect() 
1167         # fold panel itself. If too little space, 
1173         self
._foldPanel
.SetSize(foldrect
[2:]) 
1175         if self
._extraStyle 
& FPB_COLLAPSE_TO_BOTTOM
: 
1176             rect 
= self
.RepositionCollapsedToBottom() 
1177             vertical 
= self
.IsVertical() 
1178             if vertical 
and rect
.GetHeight() > 0 or not vertical 
and rect
.GetWidth() > 0: 
1179                 self
.RefreshRect(rect
) 
1181         # TODO: A smart way to check wether the old - new width of the 
1182         # panel changed, if so no need to resize the fold panel items 
1184         self
.RedisplayFoldPanelItems() 
1187     def OnPressCaption(self
, event
): 
1188         """ Handles the EVT_CAPTIONBAR event in the FoldPanelBar. """ 
1190         # act upon the folding or expanding status of the bar 
1191         # to expand or collapse the panel(s) 
1193         if event
.GetFoldStatus(): 
1194             self
.Collapse(event
.GetTag()) 
1196             self
.Expand(event
.GetTag()) 
1201     def RefreshPanelsFrom(self
, item
): 
1202         """ Refreshes all the panels from given index down to last one. """ 
1205             i 
= self
._panels
.index(item
) 
1207             raise "ERROR: Invalid Panel Passed To RefreshPanelsFrom: " + repr(item
) 
1211         # if collapse to bottom is on, the panels that are not expanded 
1212         # should be drawn at the bottom. All panels that are expanded 
1213         # are drawn on top. The last expanded panel gets all the extra space 
1215         if self
._extraStyle 
& FPB_COLLAPSE_TO_BOTTOM
: 
1219             for panels 
in self
._panels
: 
1221                 if panels
.IsExpanded(): 
1222                     offset 
= offset 
+ panels
.Reposition(offset
) 
1224             # put all non collapsed panels at the bottom where there is space,  
1225             # else put them right behind the expanded ones 
1227             self
.RepositionCollapsedToBottom() 
1231             pos 
= self
._panels
[i
].GetItemPos() + self
._panels
[i
].GetPanelLength() 
1232             for j 
in range(i
+1, len(self
._panels
)): 
1233                 pos 
= pos 
+ self
._panels
[j
].Reposition(pos
) 
1238     def RedisplayFoldPanelItems(self
): 
1239         """ Resizes the fold panels so they match the width. """ 
1240         # resize them all. No need to reposition 
1241         for panels 
in self
._panels
: 
1242             panels
.ResizePanel() 
1246     def RepositionCollapsedToBottom(self
): 
1248         Repositions all the collapsed panels to the bottom. 
1250         When it is not possible to align them to the bottom, stick 
1251         them behind the visible panels. The Rect holds the slack area 
1252         left between last repositioned panel and the bottom 
1253         panels. This needs to get a refresh. 
1256         value 
= wx
.Rect(0,0,0,0) 
1257         vertical 
= self
.IsVertical() 
1259         # determine wether the number of panels left 
1260         # times the size of their captions is enough 
1261         # to be placed in the left over space 
1265         collapsed
, expanded
, values 
= self
.GetPanelsLength(collapsed
, expanded
) 
1267         # if no room stick them behind the normal ones, else 
1270         if (vertical 
and [self
.GetSize().GetHeight()] or \
 
1271             [self
.GetSize().GetWidth()])[0] - expanded 
- collapsed 
< 0: 
1275             # value is the region which is left unpainted 
1276             # I will send it back as 'slack' so it does not need to 
1279             value
.SetHeight(self
.GetSize().GetHeight()) 
1280             value
.SetWidth(self
.GetSize().GetWidth()) 
1283                 value
.SetY(expanded
) 
1284                 value
.SetHeight(value
.GetHeight() - expanded
) 
1286                 value
.SetX(expanded
) 
1287                 value
.SetWidth(value
.GetWidth() - expanded
) 
1289             offset 
= (vertical 
and [self
.GetSize().GetHeight()] or \
 
1290                       [self
.GetSize().GetWidth()])[0] - collapsed
 
1295         for panels 
in self
._panels
: 
1296             if not panels
.IsExpanded(): 
1297                 offset 
= offset 
+ panels
.Reposition(offset
) 
1302     def GetPanelsLength(self
, collapsed
, expanded
): 
1304         Returns the length of the panels that are expanded and 
1307         This is useful to determine quickly what size is used to 
1308         display, and what is left at the bottom (right) to align the 
1314         # assumed here that all the panels that are expanded 
1315         # are positioned after each other from 0,0 to end. 
1317         for j 
in range(0, len(self
._panels
)): 
1318             offset 
= self
._panels
[j
].GetPanelLength() 
1319             value 
= value 
+ offset
 
1320             if self
._panels
[j
].IsExpanded(): 
1321                 expanded 
= expanded 
+ offset
 
1323                 collapsed 
= collapsed 
+ offset
 
1325         return collapsed
, expanded
, value
 
1328     def Collapse(self
, foldpanel
): 
1330         Collapses the given FoldPanel reference, and updates the 
1333         In the FPB_COLLAPSE_TO_BOTTOM style, all collapsed captions 
1334         are put at the bottom of the control. In the normal mode, they 
1335         stay where they are. 
1339             item 
= self
._panels
.index(foldpanel
) 
1341             raise "ERROR: Invalid Panel Passed To Collapse: " + repr(foldpanel
) 
1343         foldpanel
.Collapse() 
1344         self
.RefreshPanelsFrom(foldpanel
) 
1347     def Expand(self
, foldpanel
): 
1349         Expands the given FoldPanel reference, and updates the 
1352         In the FPB_COLLAPSE_TO_BOTTOM style, they will be removed from 
1353         the bottom and the order where the panel originally was placed 
1358         self
.RefreshPanelsFrom(foldpanel
) 
1361     def ApplyCaptionStyle(self
, foldpanel
, cbstyle
): 
1363         Sets the style of the caption bar (`CaptionBar`) of the 
1366         The changes are applied immediately. All styles not set in the 
1367         CaptionBarStyle class are not applied. Use the CaptionBar 
1368         reference to indicate what captionbar you want to apply the 
1369         style to. To apply one style to all CaptionBar items, use 
1370         `ApplyCaptionStyleAll` 
1372         foldpanel
.ApplyCaptionStyle(cbstyle
) 
1375     def ApplyCaptionStyleAll(self
, cbstyle
): 
1377         Sets the style of all the caption bars of the FoldPanel. 
1379         The changes are applied immediately. 
1381         for panels 
in self
._panels
: 
1382             self
.ApplyCaptionStyle(panels
, cbstyle
) 
1385     def GetCaptionStyle(self
, foldpanel
): 
1387         Returns the currently used caption style for the FoldPanel. 
1389         It is returned as a CaptionBarStyle class. After modifying it, 
1390         it can be set again. 
1392         return foldpanel
.GetCaptionStyle() 
1395     def IsVertical(self
): 
1397         Returns whether the CaptionBar has default orientation or not. 
1399         Default is vertical. 
1401         return self
._isVertical
 
1404     def GetFoldPanel(self
, item
): 
1406         Returns the panel associated with the index "item". 
1408         See the example at the bottom of the module, especially the events 
1409         for the "Collapse Me" and "Expand Me" buttons. 
1412             ind 
= self
._panels
[item
] 
1413             return self
._panels
[item
] 
1415             raise "ERROR: List Index Out Of Range Or Bad Item Passed: " + repr(item
) + \
 
1416                   ". Item Should Be An Integer Between " + repr(0) + " And " + \
 
1417                   repr(len(self
._panels
)) 
1421         """ Returns the number of panels in the FoldPanelBar. """ 
1424             return len(self
._panels
) 
1426             raise "ERROR: No Panels Have Been Added To FoldPanelBar" 
1430 # --------------------------------------------------------------------------------- # 
1431 # class FoldPanelItem 
1432 # --------------------------------------------------------------------------------- # 
1434 class FoldPanelItem(wx
.Panel
): 
1436     This class is a child sibling of the `FoldPanelBar` class. It will 
1437     contain a `CaptionBar` class for receiving of events, and a the 
1438     rest of the area can be populated by a `wx.Panel` derived class. 
1440     # Define Empty CaptionBar Style 
1441     EmptyCaptionBarStyle 
= CaptionBarStyle() 
1443     def __init__(self
, parent
, id=wx
.ID_ANY
, caption
="", foldIcons
=None, 
1444                  collapsed
=False, cbstyle
=EmptyCaptionBarStyle
): 
1445         """ Default Class Constructor. """ 
1447         wx
.Panel
.__init
__(self
, parent
, id, style
=wx
.CLIP_CHILDREN
) 
1448         self
._controlCreated 
= False 
1451         self
._LastInsertPos 
= 0 
1453         self
._userSized 
= False 
1455         if foldIcons 
is None: 
1456             foldIcons 
= wx
.ImageList(16, 16) 
1458             bmp 
= GetExpandedIconBitmap() 
1460             bmp 
= GetCollapsedIconBitmap() 
1463         self
._foldIcons 
= foldIcons
 
1465         # create the caption bar, in collapsed or expanded state 
1467         self
._captionBar 
= CaptionBar(self
, wx
.ID_ANY
, wx
.Point(0,0), 
1468                                       size
=wx
.DefaultSize
, caption
=caption
, 
1469                                       foldIcons
=foldIcons
, cbstyle
=cbstyle
) 
1472             self
._captionBar
.Collapse() 
1474         self
._controlCreated 
= True 
1476         # make initial size for component, if collapsed, the 
1477         # size is determined on the panel height and won't change 
1479         size 
= self
._captionBar
.GetSize() 
1481         self
._PanelSize 
= (self
.IsVertical() and [size
.GetHeight()] or \
 
1482                            [size
.GetWidth()])[0] 
1484         self
._LastInsertPos 
= self
._PanelSize
 
1487         self
.Bind(EVT_CAPTIONBAR
, self
.OnPressCaption
) 
1488         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
1491     def AddWindow(self
, window
, flags
=FPB_ALIGN_WIDTH
, Spacing
=FPB_DEFAULT_SPACING
, 
1492                   leftSpacing
=FPB_DEFAULT_LEFTLINESPACING
, 
1493                   rightSpacing
=FPB_DEFAULT_RIGHTLINESPACING
): 
1495         Adds a window item to the list of items on this panel. 
1497         The flags are FPB_ALIGN_LEFT for a non sizing window element, 
1498         and FPB_ALIGN_WIDTH for a width aligned item. The Spacing 
1499         parameter reserves a number of pixels before the window 
1500         element, and leftSpacing is an indent.  rightSpacing is only 
1501         relevant when the style FPB_ALIGN_WIDTH is chosen. 
1504         wi 
= FoldWindowItem(self
, window
, Type
="WINDOW", flags
=flags
, Spacing
=Spacing
, 
1505                             leftSpacing
=leftSpacing
, rightSpacing
=rightSpacing
) 
1507         self
._items
.append(wi
) 
1509         vertical 
= self
.IsVertical() 
1511         self
._Spacing 
= Spacing
 
1512         self
._leftSpacing 
= leftSpacing
 
1513         self
._rightSpacing 
= rightSpacing
 
1515         xpos 
= (vertical 
and [leftSpacing
] or [self
._LastInsertPos 
+ Spacing
])[0] 
1516         ypos 
= (vertical 
and [self
._LastInsertPos 
+ Spacing
] or [leftSpacing
])[0] 
1518         window
.SetDimensions(xpos
, ypos
, -1, -1, wx
.SIZE_USE_EXISTING
) 
1520         self
._LastInsertPos 
= self
._LastInsertPos 
+ wi
.GetWindowLength(vertical
) 
1524     def AddSeparator(self
, colour
=wx
.BLACK
, Spacing
=FPB_DEFAULT_SPACING
, 
1525                      leftSpacing
=FPB_DEFAULT_LEFTSPACING
, 
1526                      rightSpacing
=FPB_DEFAULT_RIGHTSPACING
): 
1528         Adds a separator item to the list of items on this panel. """ 
1530         wi 
= FoldWindowItem(self
, window
=None, Type
="SEPARATOR", 
1531                             flags
=FPB_ALIGN_WIDTH
, y
=self
._LastInsertPos
, 
1532                             colour
=colour
, Spacing
=Spacing
, leftSpacing
=leftSpacing
, 
1533                             rightSpacing
=rightSpacing
) 
1535         self
._items
.append(wi
) 
1536         self
._LastInsertPos 
= self
._LastInsertPos 
+ \
 
1537                               wi
.GetWindowLength(self
.IsVertical()) 
1542     def Reposition(self
, pos
): 
1544         Repositions this FoldPanelBar and reports the length occupied 
1545         for the next FoldPanelBar in the list. 
1547         # NOTE: Call Resize before Reposition when an item is added, because the new 
1548         # size needed will be calculated by Resize. Of course the relative position 
1549         # of the controls have to be correct in respect to the caption bar 
1553         vertical 
= self
.IsVertical() 
1554         xpos 
= (vertical 
and [-1] or [pos
])[0] 
1555         ypos 
= (vertical 
and [pos
] or [-1])[0] 
1557         self
.SetDimensions(xpos
, ypos
, -1, -1, wx
.SIZE_USE_EXISTING
) 
1562         return self
.GetPanelLength() 
1565     def OnPressCaption(self
, event
): 
1566         """ Handles the EVT_CAPTIONBAR event in the FoldPanelItem. """ 
1568         # tell the upper container we are responsible 
1569         # for this event, so it can fold the panel item 
1576     def ResizePanel(self
): 
1577         """ Resizes the panel. """ 
1579         # prevent unnecessary updates by blocking repaints for a sec 
1583         vertical 
= self
.IsVertical() 
1584         # force this panel to take the width of the parent panel and the y of the 
1585         # user or calculated width (which will be recalculated by the contents here) 
1588         if self
._captionBar
.IsCollapsed(): 
1589             size 
= self
._captionBar
.GetSize() 
1590             self
._PanelSize 
= (vertical 
and [size
.GetHeight()] or [size
.GetWidth()])[0] 
1592             size 
= self
.GetBestSize() 
1593             self
._PanelSize 
= (vertical 
and [size
.GetHeight()] or [size
.GetWidth()])[0] 
1597                     size
.SetHeight(self
._UserSize
) 
1599                     size
.SetWidth(self
._UserSize
) 
1601         pnlsize 
= self
.GetParent().GetSize() 
1604             size
.SetWidth(pnlsize
.GetWidth()) 
1606             size
.SetHeight(pnlsize
.GetHeight()) 
1608         # resize caption bar 
1609         xsize 
= (vertical 
and [size
.GetWidth()] or [-1])[0] 
1610         ysize 
= (vertical 
and [-1] or [size
.GetHeight()])[0] 
1612         self
._captionBar
.SetSize((xsize
, ysize
)) 
1617         # go by all the controls and call Layout 
1619         for items 
in self
._items
: 
1620             items
.ResizeItem((vertical 
and [size
.GetWidth()] or \
 
1621                               [size
.GetHeight()])[0], vertical
) 
1626     def OnPaint(self
, event
): 
1627         """ Handles the EVT_PAINT event in the FoldPanelItem. """ 
1629         # draw all the items that are lines 
1631         dc 
= wx
.PaintDC(self
) 
1632         vertical 
= self
.IsVertical() 
1634         for item 
in self
._items
: 
1636             if item
.GetType() == "SEPARATOR": 
1637                 pen 
= wx
.Pen(item
.GetLineColour(), 1, wx
.SOLID
) 
1639                 a 
= item
.GetLeftSpacing() 
1640                 b 
= item
.GetLineY() + item
.GetSpacing() 
1641                 c 
= item
.GetLineLength() 
1645                     dc
.DrawLine(a
, b
, d
, b
) 
1647                     dc
.DrawLine(b
, a
, b
, d
) 
1652     def IsVertical(self
): 
1654         Returns wether the CaptionBar Has Default Orientation Or Not. 
1656         Default is vertical. 
1659         # grandparent of FoldPanelItem is FoldPanelBar 
1660         # default is vertical 
1662         if isinstance(self
.GetGrandParent(), FoldPanelBar
): 
1663             return self
.GetGrandParent().IsVertical() 
1665             raise "ERROR: Wrong Parent " + repr(self
.GetGrandParent()) 
1668     def IsExpanded(self
): 
1670         Returns expanded or collapsed status.  If the panel is 
1671         expanded, True is returned. 
1674         return not self
._captionBar
.IsCollapsed() 
1677     def GetItemPos(self
): 
1678         """ Returns item's position. """ 
1680         return self
._itemPos
 
1684         # this should not be called by the user, because it doesn't trigger the 
1685         # parent  to tell it that we are collapsed or expanded, it only changes 
1688         self
._captionBar
.Collapse() 
1693         # this should not be called by the user, because it doesn't trigger the 
1694         # parent to tell it that we are collapsed or expanded, it only changes 
1697         self
._captionBar
.Expand() 
1701     def GetPanelLength(self
): 
1702         """ Returns size of panel. """ 
1704         if self
._captionBar
.IsCollapsed(): 
1705             return self
.GetCaptionLength() 
1706         elif self
._userSized
: 
1707             return self
._UserSize
 
1709         return self
._PanelSize
 
1712     def GetCaptionLength(self
): 
1714         Returns height of caption only. This is for folding 
1715         calculation purposes. 
1718         size 
= self
._captionBar
.GetSize() 
1719         return (self
.IsVertical() and [size
.GetHeight()] or [size
.GetWidth()])[0] 
1722     def ApplyCaptionStyle(self
, cbstyle
): 
1723         """ Applies the style defined in cbstyle to the CaptionBar.""" 
1725         self
._captionBar
.SetCaptionStyle(cbstyle
) 
1728     def GetCaptionStyle(self
): 
1730         Returns the current style of the captionbar in a 
1731         CaptionBarStyle class. 
1733         This can be used to change and set back the changes. 
1736         return self
._captionBar
.GetCaptionStyle() 
1739 # ----------------------------------------------------------------------------------- # 
1740 # class FoldWindowItem 
1741 # ----------------------------------------------------------------------------------- # 
1743 class FoldWindowItem
: 
1745     This class is a child sibling of the `FoldPanelItem` class. It 
1746     will contain wx.Window that can be either a separator (a colored 
1747     line simulated by a wx.Window) or a wxPython controls (such as a 
1748     wx.Button, a wx.ListCtrl etc...). 
1750     def __init__(self
, parent
, window
=None, **kw
): 
1752         Default Class Constructor 
1756             Type = "WINDOW", flags = FPB_ALIGN_WIDTH, 
1757             Spacing = FPB_DEFAULT_SPACING, 
1758             leftSpacing = FPB_DEFAULT_LEFTSPACING, 
1759             rightSpacing = FPB_DEFAULT_RIGHTSPACING 
1764             y, lineColor = wx.BLACK, 
1765             flags = FPB_ALIGN_WIDTH, 
1766             Spacing = FPB_DEFAULT_SPACING, 
1767             leftSpacing = FPB_DEFAULT_LEFTLINESPACING, 
1768             rightSpacing = FPB_DEFAULT_RIGHTLINESPACING 
1772         if not kw
.has_key("Type"): 
1773             raise 'ERROR: Missing Window Type Information. This Should Be "WINDOW" Or "SEPARATOR"' 
1775         if kw
.get("Type") == "WINDOW": 
1776             # Window constructor. This initialises the class as a wx.Window Type 
1778             if kw
.has_key("flags"): 
1779                 self
._flags 
= kw
.get("flags") 
1781                 self
._flags 
= FPB_ALIGN_WIDTH
 
1782             if kw
.has_key("Spacing"): 
1783                 self
._Spacing 
= kw
.get("Spacing") 
1785                 self
._Spacing 
= FPB_DEFAULT_SPACING
 
1786             if kw
.has_key("leftSpacing"): 
1787                 self
._leftSpacing 
= kw
.get("leftSpacing") 
1789                 self
._leftSpacing 
= FPB_DEFAULT_LEFTSPACING
 
1790             if kw
.has_key("rightSpacing"): 
1791                 self
._rightSpacing 
= kw
.get("rightSpacing") 
1793                 self
._rightSpacing 
= FPB_DEFAULT_RIGHTSPACING
 
1796             self
._sepLineColour 
= None 
1800         elif kw
.get("Type") == "SEPARATOR": 
1801             # separator constructor. This initialises the class as a separator type 
1804                 self
._lineY 
= kw
.get("y") 
1806                 raise "ERROR: Undefined Y Position For The Separator" 
1807             if kw
.has_key("lineColour"): 
1808                 self
._sepLineColour 
= kw
.get("lineColour") 
1810                 self
._sepLineColour 
= wx
.BLACK
 
1811             if kw
.has_key("flags"): 
1812                 self
._flags 
= kw
.get("flags") 
1814                 self
._flags 
= FPB_ALIGN_WIDTH
 
1815             if kw
.has_key("Spacing"): 
1816                 self
._Spacing 
= kw
.get("Spacing") 
1818                 self
._Spacing 
= FPB_DEFAULT_SPACING
 
1819             if kw
.has_key("leftSpacing"): 
1820                 self
._leftSpacing 
= kw
.get("leftSpacing") 
1822                 self
._leftSpacing 
= FPB_DEFAULT_LEFTSPACING
 
1823             if kw
.has_key("rightSpacing"): 
1824                 self
._rightSpacing 
= kw
.get("rightSpacing") 
1826                 self
._rightSpacing 
= FPB_DEFAULT_RIGHTSPACING
 
1831             raise "ERROR: Undefined Window Type Selected: " + repr(kw
.get("Type")) 
1833         self
._type 
= kw
.get("Type") 
1834         self
._lineLength 
= 0 
1843     def GetLineLength(self
): 
1844         return self
._lineLength
 
1846     def GetLineColour(self
): 
1847         return self
._sepLineColour
 
1849     def GetLeftSpacing(self
): 
1850         return self
._leftSpacing
 
1852     def GetRightSpacing(self
): 
1853         return self
._rightSpacing
 
1855     def GetSpacing(self
): 
1856         return self
._Spacing
 
1859     def GetWindowLength(self
, vertical
=True): 
1861         Returns space needed by the window if type is FoldWindowItem 
1862         "WINDOW" and returns the total size plus the extra spacing. 
1866         if self
._type 
== "WINDOW": 
1867             size 
= self
._wnd
.GetSize() 
1868             value 
= (vertical 
and [size
.GetHeight()] or [size
.GetWidth()])[0] + \
 
1871         elif self
._type 
== "SEPARATOR": 
1872             value 
= 1 + self
._Spacing
 
1877     def ResizeItem(self
, size
, vertical
=True): 
1879         Resizes the element, whatever it is. 
1881         A separator or line will be always aligned by width or height 
1882         depending on orientation of the whole panel. 
1885         if self
._flags 
& FPB_ALIGN_WIDTH
: 
1886             # align by taking full width 
1887             mySize 
= size 
- self
._leftSpacing 
- self
._rightSpacing
 
1890                 mySize 
= 10  # can't have negative width 
1892             if self
._type 
== "SEPARATOR": 
1893                 self
._lineLength 
= mySize
 
1895                 xsize 
= (vertical 
and [mySize
] or [-1])[0] 
1896                 ysize 
= (vertical 
and [-1] or [mySize
])[0] 
1898                 self
._wnd
.SetSize((xsize
, ysize
))