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: 05 Nov 2005, 23.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 
  36 # 1. Implemented Styles Like FPB_SINGLE_FOLD and FPB_EXCLUSIVE_FOLD 
  37 # Thanks To E. A. Tacao For His Nice Suggestions. 
  39 # 2. Added Some Maquillage To FoldPanelBar: When The Mouse Enters The Icon 
  40 # Region, It Is Changed To wx.CURSOR_HAND. 
  43 # For The Original TODO List From Jorgen, Please Refer To: 
  44 # http://www.solidsteel.nl/jorg/components/foldpanel/wxFoldPanelBar.php#todo_list 
  48 # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please 
  51 # andrea.gavana@agip.it 
  54 # Or, Obviously, To The wxPython Mailing List!!! 
  58 # --------------------------------------------------------------------------- # 
  62 The `FoldPanelBar` is a control that contains multiple panels (of type 
  63 `FoldPanelItem`) that can be expanded or collapsed. The captionbar of 
  64 the FoldPanel can be customized by setting it to a horizontal gradient 
  65 style, vertical gradient style, a single color, a rectangle or filled 
  66 rectangle. The FoldPanel items can be collapsed in place or to the 
  67 bottom of the control. `wx.Window` derived controls can be added 
  68 dynamically, and separated by separator lines.  FoldPanelBar is 
  69 freeware and distributed under the wxPython license. 
  75 The internals of the FoldPanelBar is a list of FoldPanelItem objects. Through 
  76 the reference of FoldPanel these panels can be controlled by adding new controls 
  77 to a FoldPanel or adding new FoldPanels to the FoldPanelBar.  
  78 The CaptionBar fires events to the parent (container of all panel items) when a 
  79 sub-panel needs resizing (either folding or expanding). The fold or expand process 
  80 is simply a resize of the panel so it looks like all controls on it are gone. All 
  81 controls are still child of the FoldPanel they are located on. If they don't 
  82 handle the event (and they won't) then the owner of the FoldPanelBar gets the 
  83 events. This is what you need to handle the controls. There isn't much to it just 
  84 a lot of calculations to see what panel belongs where. There are no sizers 
  85 involved in the panels, everything is purely x-y positioning.  
  88 What can it do and what not? 
  89 ---------------------------- 
  92         * Run-time addition of panels (no deletion just yet)  
  93         * Run time addition of controls to the panel (it will be resized accordingly) 
  94         * Creating panels in collapsed mode or expanded mode  
  95         * Various modes of caption behaviour and filling to make it more appealing  
  96         * Panels can be folded and collapsed (or all of them) to allow more space 
 100         * Selection of a panel like in a list ctrl  
 101         * Dragging and dropping the panels  
 102         * Re-ordering the panels (not yet)   
 108 FoldPanelBar is supported on the following platforms:  
 109   * Windows (Verified on Windows XP, 2000) 
 110   * Linux/Unix (GTK2) (Thanks To Toni Brkic And Robin Dunn) 
 111   * Mac OSX (Thanks To Robin Dunn For The CaptionBar Size Patch) 
 114 FoldPanelBar is based upon Jorgen Bodde's C++ implementation. 
 115 Latest Revision: Andrea Gavana @ 05 Nov 2005, 23.30 CET 
 121 #---------------------------------------------------------------------- 
 122 # Collapsed And Expanded Bitmap Images 
 123 # Created With img2py.py  
 124 #---------------------------------------------------------------------- 
 126 def GetCollapsedIconData(): 
 128 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ 
 129 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ 
 130 \x00\x007IDAT8\x8dcddbf\xa0\x040Q\xa4{\xf0\x1b\xf0\xff\xdf\xdf\xff\x03\xe7\ 
 131 \x02\x98\xed\x84\\A\x1b\x17\xa0\xdb\x8a\xcf\x15\xd4w\x01.\xdbp\x89S\xec\x02\ 
 132 \xc6\xd1\xbc\xc0\x00\x00\x9a\xf5\x1b\xfa\xf9m$?\x00\x00\x00\x00IEND\xaeB`\ 
 135 def GetCollapsedIconBitmap(): 
 136     return wx
.BitmapFromImage(GetCollapsedIconImage()) 
 138 def GetCollapsedIconImage(): 
 140     stream 
= cStringIO
.StringIO(GetCollapsedIconData()) 
 141     return wx
.ImageFromStream(stream
) 
 143 #---------------------------------------------------------------------- 
 144 def GetExpandedIconData(): 
 146 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ 
 147 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ 
 148 \x00\x00BIDAT8\x8dcddbf\xa0\x040Q\xa4{P\x18\xc0\x82.\xf0\xff\xdf\xdf\xff\xb8\ 
 149 \x143213R\xdd\x05\x18\x06`\xb3\x05\x9f8m\x02\x11\xdd6\\\xb6\xd3\xce\x05\xc8\ 
 150 \xb6\xe2\xb3\x9d*.`\x1c\xcd\x0b\x0c\x00\x9e\xbc\x04W\x19\xcfa\xb5\x00\x00\ 
 151 \x00\x00IEND\xaeB`\x82'  
 153 def GetExpandedIconBitmap(): 
 154     return wx
.BitmapFromImage(GetExpandedIconImage()) 
 156 def GetExpandedIconImage(): 
 158     stream 
= cStringIO
.StringIO(GetExpandedIconData()) 
 159     return wx
.ImageFromStream(stream
) 
 161 #---------------------------------------------------------------------- 
 163 #---------------------------------------------------------------------- 
 164 # FOLDPANELBAR Starts Here 
 165 #---------------------------------------------------------------------- 
 169 #- CAPTIONBAR_GRADIENT_V: Draws a vertical gradient from top to bottom 
 170 #- CAPTIONBAR_GRADIENT_H: Draws a horizontal gradient from left to right 
 171 #- CAPTIONBAR_SINGLE: Draws a single filled rectangle to draw the caption 
 172 #- CAPTIONBAR_RECTANGLE: Draws a single colour with a rectangle around the caption 
 173 #- CAPTIONBAR_FILLED_RECTANGLE: Draws a filled rectangle and a border around it 
 175 CAPTIONBAR_NOSTYLE            
= 0 
 176 CAPTIONBAR_GRADIENT_V         
= 1 
 177 CAPTIONBAR_GRADIENT_H         
= 2 
 178 CAPTIONBAR_SINGLE             
= 3 
 179 CAPTIONBAR_RECTANGLE          
= 4 
 180 CAPTIONBAR_FILLED_RECTANGLE   
= 5 
 185 # pixels of the bmp to be aligned from the right filled with space 
 186 FPB_BMP_RIGHTSPACE 
= 2 
 188 # Now supported! Single fold forces 
 189 # other panels to close when they are open, and only opens the current panel. 
 190 # This will allow the open panel to gain the full size left in the client area 
 191 FPB_SINGLE_FOLD 
= 0x0001 
 193 # All panels are stacked to the bottom. When they are expanded again they 
 195 FPB_COLLAPSE_TO_BOTTOM 
= 0x0002 
 197 # Now supported! Single fold plus panels 
 198 # will be stacked at the bottom 
 199 FPB_EXCLUSIVE_FOLD 
= 0x0004 
 202 FPB_HORIZONTAL 
= wx
.HORIZONTAL
 
 203 FPB_VERTICAL 
= wx
.VERTICAL  
 
 205 # Default Extrastyle of the FoldPanelBar  
 206 FPB_DEFAULT_EXTRASTYLE 
= 0 
 207 # Default style of the FoldPanelBar  
 208 FPB_DEFAULT_STYLE 
= wx
.TAB_TRAVERSAL | wx
.NO_BORDER
 
 210 # FoldPanelItem default settings 
 214 FPB_DEFAULT_LEFTSPACING 
= 5 
 215 FPB_DEFAULT_RIGHTSPACING 
= 10 
 216 FPB_DEFAULT_SPACING 
= 8 
 218 FPB_DEFAULT_LEFTLINESPACING 
= 2 
 219 FPB_DEFAULT_RIGHTLINESPACING 
= 2 
 222 # ------------------------------------------------------------------------------ # 
 223 # class CaptionBarStyle 
 224 # ------------------------------------------------------------------------------ # 
 226 class CaptionBarStyle
: 
 228     This class encapsulates the styles you wish to set for the 
 229     `CaptionBar` (this is the part of the FoldPanel where the caption 
 230     is displayed). It can either be applied at creation time be 
 231     reapplied when styles need to be changed. 
 233     At construction time, all styles are set to their default 
 234     transparency.  This means none of the styles will be applied to 
 235     the `CaptionBar` in question, meaning it will be created using the 
 236     default internals. When setting i.e the color, font or panel 
 237     style, these styles become active to be used. 
 242         """ Default constructor for this class.""" 
 247     def ResetDefaults(self
): 
 248         """ Resets default CaptionBarStyle."""         
 249         self
._firstColourUsed 
= False 
 250         self
._secondColourUsed 
= False 
 251         self
._textColourUsed 
= False 
 252         self
._captionFontUsed 
= False 
 253         self
._captionStyleUsed 
= False 
 254         self
._captionStyle 
= CAPTIONBAR_GRADIENT_V
 
 257     # ------- CaptionBar Font ------- 
 259     def SetCaptionFont(self
, font
): 
 261         Sets font for the caption bar. 
 263         If this is not set, the font property is undefined and will 
 264         not be used. Use `CaptionFontUsed` to check if this style is 
 267         self
._captionFont 
= font
 
 268         self
._captionFontUsed 
= True 
 271     def CaptionFontUsed(self
): 
 272         """ Checks if the caption bar font is set. """         
 273         return self
._captionFontUsed
 
 276     def GetCaptionFont(self
): 
 278         Returns the font for the caption bar. 
 280         Please be warned this will result in an assertion failure when 
 281         this property is not previously set. 
 283         :see: `SetCaptionFont`, `CaptionFontUsed` 
 285         return self
._captionFont
 
 288     # ------- First Colour ------- 
 290     def SetFirstColour(self
, colour
): 
 292         Sets first colour for the caption bar. 
 294         If this is not set, the colour property is undefined and will 
 295         not be used. Use `FirstColourUsed` to check if this style is 
 298         self
._firstColour 
= colour
 
 299         self
._firstColourUsed 
= True 
 302     def FirstColourUsed(self
): 
 303         """ Checks if the first colour of the caption bar is set."""         
 304         return self
._firstColourUsed
 
 307     def GetFirstColour(self
): 
 309         Returns the first colour for the caption bar. 
 311         Please be warned this will result in an assertion failure when 
 312         this property is not previously set. 
 314         :see: `SetFirstColour`, `FirstColourUsed` 
 316         return self
._firstColour
 
 319     # ------- Second Colour ------- 
 321     def SetSecondColour(self
, colour
): 
 323         Sets second colour for the caption bar. 
 325         If this is not set, the colour property is undefined and will 
 326         not be used. Use `SecondColourUsed` to check if this style is 
 329         self
._secondColour 
= colour
 
 330         self
._secondColourUsed 
= True 
 333     def SecondColourUsed(self
): 
 334         """ Checks if the second colour of the caption bar is set."""         
 335         return self
._secondColourUsed
 
 338     def GetSecondColour(self
): 
 340         Returns the second colour for the caption bar. 
 342         Please be warned this will result in an assertion failure when 
 343         this property is not previously set. 
 345         :see: `SetSecondColour`, `SecondColourUsed` 
 347         return self
._secondColour
 
 350     # ------- Caption Text Colour ------- 
 352     def SetCaptionColour(self
, colour
): 
 354         Sets caption colour for the caption bar. 
 356         If this is not set, the colour property is undefined and will 
 357         not be used. Use `CaptionColourUsed` to check if this style is 
 360         self
._textColour 
= colour
 
 361         self
._textColourUsed 
= True 
 364     def CaptionColourUsed(self
): 
 365         """ Checks if the caption colour of the caption bar is set."""         
 366         return self
._textColourUsed
 
 369     def GetCaptionColour(self
): 
 371         Returns the caption colour for the caption bar. 
 373         Please be warned this will result in an assertion failure 
 374         when this property is not previously set. 
 375         See also SetCaptionColour(), CaptionColourUsed() 
 377         return self
._textColour
 
 380     # ------- CaptionStyle  ------- 
 382     def SetCaptionStyle(self
, style
): 
 384         Sets caption style for the caption bar. 
 386         If this is not set, the property is undefined and will not be 
 387         used. Use CaptionStyleUsed() to check if this style is used. 
 388         The following styles can be applied: 
 390             * CAPTIONBAR_GRADIENT_V: Draws a vertical gradient from top to bottom 
 392             * CAPTIONBAR_GRADIENT_H: Draws a horizontal gradient from 
 395             * CAPTIONBAR_SINGLE: Draws a single filled rectangle to 
 398             * CAPTIONBAR_RECTANGLE: Draws a single colour with a 
 399               rectangle around the caption 
 401             * CAPTIONBAR_FILLED_RECTANGLE: Draws a filled rectangle 
 402               and a border around it 
 405         self
._captionStyle 
= style
 
 406         self
._captionStyleUsed 
= True 
 409     def CaptionStyleUsed(self
): 
 410         """ Checks if the caption style of the caption bar is set."""         
 411         return self
._captionStyleUsed
 
 414     def GetCaptionStyle(self
): 
 416         Returns the caption style for the caption bar. 
 418         Please be warned this will result in an assertion failure 
 419         when this property is not previously set. 
 421         :see: `SetCaptionStyle`, `CaptionStyleUsed` 
 423         return self
._captionStyle
 
 426 #-----------------------------------# 
 428 #-----------------------------------# 
 429 wxEVT_CAPTIONBAR 
= wx
.NewEventType() 
 430 EVT_CAPTIONBAR 
= wx
.PyEventBinder(wxEVT_CAPTIONBAR
, 0) 
 433 # ---------------------------------------------------------------------------- # 
 434 # class CaptionBarEvent 
 435 # ---------------------------------------------------------------------------- # 
 437 class CaptionBarEvent(wx
.PyCommandEvent
): 
 439     This event will be sent when a EVT_CAPTIONBAR is mapped in the parent. 
 440     It is to notify the parent that the bar is now in collapsed or expanded 
 441     state. The parent should re-arrange the associated windows accordingly 
 443     def __init__(self
, evtType
): 
 444         """ Default Constructor For This Class."""         
 445         wx
.PyCommandEvent
.__init
__(self
, evtType
) 
 448     def GetFoldStatus(self
): 
 450         Returns whether the bar is expanded or collapsed. True means 
 453         return not self
._bar
.IsCollapsed() 
 457         """ Returns The CaptionBar Selected."""         
 461     def SetTag(self
, tag
): 
 462         """ Assign A Tag To The Selected CaptionBar."""         
 467         """ Returns The Tag Assigned To The Selected CaptionBar."""         
 471     def SetBar(self
, bar
): 
 473         Sets the bar associated with this event. 
 475         Should not used by any other then the originator of the event. 
 480 # -------------------------------------------------------------------------------- # 
 482 # -------------------------------------------------------------------------------- # 
 484 class CaptionBar(wx
.Window
): 
 486     This class is a graphical caption component that consists of a 
 487     caption and a clickable arrow. 
 489     The CaptionBar fires an event EVT_CAPTIONBAR which is a 
 490     `CaptionBarEvent`.  This event can be caught and the parent window 
 491     can act upon the collapsed or expanded state of the bar (which is 
 492     actually just the icon which changed).  The parent panel can 
 493     reduce size or expand again. 
 496     # Define Empty CaptionBar Style 
 497     EmptyCaptionBarStyle 
= CaptionBarStyle() 
 499     def __init__(self
, parent
, id, pos
, size
, caption
="", 
 500                  foldIcons
=None, cbstyle
=EmptyCaptionBarStyle
, 
 501                  rightIndent
=FPB_BMP_RIGHTSPACE
, 
 502                  iconWidth
=16, iconHeight
=16, collapsed
=False): 
 503         """ Default Class Constructor.""" 
 505         wx
.Window
.__init
__(self
, parent
, wx
.ID_ANY
, pos
=pos
, 
 506                            size
=(20,20), style
=wx
.NO_BORDER
) 
 508         self
._controlCreated 
= False 
 509         self
._collapsed 
= collapsed
 
 510         self
.ApplyCaptionStyle(cbstyle
, True) 
 512         if foldIcons 
is None: 
 513             foldIcons 
= wx
.ImageList(16, 16) 
 515             bmp 
= GetExpandedIconBitmap() 
 517             bmp 
= GetCollapsedIconBitmap() 
 522             assert foldIcons
.GetImageCount() > 1 
 523             iconWidth
, iconHeight 
= foldIcons
.GetSize(0) 
 525         self
._caption 
= caption
 
 526         self
._foldIcons 
= foldIcons
 
 527         self
._style 
= cbstyle
 
 528         self
._rightIndent 
= rightIndent
 
 529         self
._iconWidth 
= iconWidth
 
 530         self
._iconHeight 
= iconHeight
 
 531         self
._oldSize 
= wx
.Size(20,20) 
 533         self
._controlCreated 
= True 
 535         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
 536         self
.Bind(wx
.EVT_SIZE
, self
.OnSize
) 
 537         self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouseEvent
) 
 538         self
.Bind(wx
.EVT_CHAR
, self
.OnChar
) 
 541     def ApplyCaptionStyle(self
, cbstyle
=EmptyCaptionBarStyle
, applyDefault
=True): 
 542         """ Applies the style defined in cbstyle to the CaptionBar.""" 
 548             # get first colour from style or make it default 
 549             if not newstyle
.FirstColourUsed(): 
 550                 newstyle
.SetFirstColour(wx
.WHITE
) 
 552             # get second colour from style or make it default 
 553             if not newstyle
.SecondColourUsed(): 
 554                 # make the second colour slightly darker then the background 
 555                 color 
= self
.GetParent().GetBackgroundColour() 
 556                 r
, g
, b 
= int(color
.Red()), int(color
.Green()), int(color
.Blue()) 
 557                 color 
= ((r 
>> 1) + 20, (g 
>> 1) + 20, (b 
>> 1) + 20) 
 558                 newstyle
.SetSecondColour(wx
.Colour(color
[0], color
[1], color
[2])) 
 561             if not newstyle
.CaptionColourUsed(): 
 562                 newstyle
.SetCaptionColour(wx
.BLACK
) 
 565             if not newstyle
.CaptionFontUsed(): 
 566                 newstyle
.SetCaptionFont(self
.GetParent().GetFont()) 
 568             # apply caption style 
 569             if not newstyle
.CaptionStyleUsed(): 
 570                 newstyle
.SetCaptionStyle(CAPTIONBAR_GRADIENT_V
) 
 572         self
._style 
= newstyle
 
 575     def SetCaptionStyle(self
, cbstyle
=EmptyCaptionBarStyle
, applyDefault
=True): 
 577         Sets CaptionBar styles with CapionBarStyle class. 
 579         All styles that are actually set, are applied. If you set 
 580         applyDefault to True, all other (not defined) styles will be 
 581         set to default. If it is False, the styles which are not set 
 582         in the CaptionBarStyle will be ignored. 
 584         self
.ApplyCaptionStyle(cbstyle
, applyDefault
) 
 588     def GetCaptionStyle(self
): 
 590         Returns the current style of the captionbar in a 
 591         `CaptionBarStyle` class. 
 593         This can be used to change and set back the changes. 
 598     def IsCollapsed(self
): 
 600         Returns wether the status of the bar is expanded or collapsed. 
 602         return self
._collapsed
 
 605     def SetRightIndent(self
, pixels
): 
 607         Sets the amount of pixels on the right from which the bitmap 
 610         If this is 0, it will be drawn all the way to the right, 
 611         default is equal to FPB_BMP_RIGHTSPACE. Assign this before 
 612         assigning an image list to prevent a redraw. 
 615         self
._rightIndent 
= pixels
 
 622         This sets the internal state / representation to collapsed. 
 624         This does not trigger a `CaptionBarEvent` to be sent to the 
 627         self
._collapsed 
= True 
 628         self
.RedrawIconBitmap() 
 633         This sets the internal state / representation to expanded. 
 635         This does not trigger a `CaptionBarEvent` to be sent to the 
 638         self
._collapsed 
= False 
 639         self
.RedrawIconBitmap() 
 642     def SetBoldFont(self
): 
 643         """ Sets the CaptionBarFont weight to BOLD.""" 
 645         self
.GetFont().SetWeight(wx
.BOLD
) 
 648     def SetNormalFont(self
): 
 649         """ Sets the CaptionBarFont weight to NORMAL.""" 
 651         self
.GetFont().SetWeight(wx
.NORMAL
) 
 654     def IsVertical(self
): 
 656         Returns wether the CaptionBar Has Default Orientation Or Not. 
 661         fld 
= self
.GetParent().GetGrandParent() 
 662         if isinstance(fld
, FoldPanelBar
): 
 663             return self
.GetParent().GetGrandParent().IsVertical() 
 665             raise "ERROR: Wrong Parent " + repr(fld
) 
 668     def OnPaint(self
, event
): 
 669         """ The paint event for flat or gradient fill. """ 
 671         if not self
._controlCreated
: 
 675         dc 
= wx
.PaintDC(self
) 
 676         wndRect 
= self
.GetRect() 
 677         vertical 
= self
.IsVertical() 
 679         # TODO: Maybe first a memory DC should draw all, and then paint it on 
 680         # the caption. This way a flickering arrow during resize is not visible 
 682         self
.FillCaptionBackground(dc
) 
 683         dc
.SetFont(self
._style
.GetCaptionFont()) 
 684         dc
.SetTextForeground(self
._style
.GetCaptionColour()) 
 687             dc
.DrawText(self
._caption
, 4, FPB_EXTRA_Y
/2) 
 689             dc
.DrawRotatedText(self
._caption
, FPB_EXTRA_Y
/2, 
 690                                wndRect
.GetBottom() - 4, 90) 
 692         # draw small icon, either collapsed or expanded 
 693         # based on the state of the bar. If we have any bmp's 
 697             index 
= self
._collapsed
 
 700                 drw 
= wndRect
.GetRight() - self
._iconWidth 
- self
._rightIndent
 
 701                 self
._foldIcons
.Draw(index
, dc
, drw
, 
 702                                      (wndRect
.GetHeight() - self
._iconHeight
)/2, 
 703                                      wx
.IMAGELIST_DRAW_TRANSPARENT
) 
 705                 self
._foldIcons
.Draw(index
, dc
, 
 706                                      (wndRect
.GetWidth() - self
._iconWidth
)/2, 
 707                                      self
._rightIndent
, wx
.IMAGELIST_DRAW_TRANSPARENT
) 
 712     def FillCaptionBackground(self
, dc
): 
 714         Fills the background of the caption with either a gradient or 
 718         style 
= self
._style
.GetCaptionStyle() 
 720         if style 
== CAPTIONBAR_GRADIENT_V
: 
 721             if self
.IsVertical(): 
 722                 self
.DrawVerticalGradient(dc
, self
.GetRect()) 
 724                 self
.DrawHorizontalGradient(dc
, self
.GetRect()) 
 726         elif style 
== CAPTIONBAR_GRADIENT_H
: 
 727             if self
.IsVertical(): 
 728                 self
.DrawHorizontalGradient(dc
, self
.GetRect()) 
 730                 self
.DrawVerticalGradient(dc
, self
.GetRect()) 
 732         elif style 
== CAPTIONBAR_SINGLE
: 
 733             self
.DrawSingleColour(dc
, self
.GetRect()) 
 734         elif style 
== CAPTIONBAR_RECTANGLE 
or style 
== CAPTIONBAR_FILLED_RECTANGLE
: 
 735             self
.DrawSingleRectangle(dc
, self
.GetRect()) 
 737             raise "STYLE Error: Undefined Style Selected: " + repr(style
) 
 740     def OnMouseEvent(self
, event
): 
 742         Catches the mouse click-double click. 
 744         If clicked on the arrow (single) or double on the caption we change state 
 745         and an event must be fired to let this panel collapse or expand. 
 749         vertical 
= self
.IsVertical() 
 751         if event
.LeftDown() and self
._foldIcons
: 
 753             pt 
= event
.GetPosition() 
 754             rect 
= self
.GetRect() 
 756             drw 
= (rect
.GetWidth() - self
._iconWidth 
- self
._rightIndent
) 
 757             if vertical 
and pt
.x 
> drw 
or not vertical 
and \
 
 758                pt
.y 
< (self
._iconHeight 
+ self
._rightIndent
): 
 761         elif event
.LeftDClick(): 
 762             self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
)) 
 765         elif event
.Entering() and self
._foldIcons
: 
 766             pt 
= event
.GetPosition() 
 767             rect 
= self
.GetRect() 
 769             drw 
= (rect
.GetWidth() - self
._iconWidth 
- self
._rightIndent
) 
 770             if vertical 
and pt
.x 
> drw 
or not vertical 
and \
 
 771                pt
.y 
< (self
._iconHeight 
+ self
._rightIndent
): 
 772                 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
)) 
 774                 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
)) 
 776         elif event
.Leaving(): 
 777             self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
)) 
 780             pt 
= event
.GetPosition() 
 781             rect 
= self
.GetRect() 
 783             drw 
= (rect
.GetWidth() - self
._iconWidth 
- self
._rightIndent
)            
 784             if vertical 
and pt
.x 
> drw 
or not vertical 
and \
 
 785                pt
.y 
< (self
._iconHeight 
+ self
._rightIndent
): 
 786                 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
)) 
 788                 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
)) 
 790         # send the collapse, expand event to the parent 
 793             event 
= CaptionBarEvent(wxEVT_CAPTIONBAR
) 
 794             event
.SetId(self
.GetId()) 
 795             event
.SetEventObject(self
) 
 797             self
.GetEventHandler().ProcessEvent(event
) 
 800     def OnChar(self
, event
): 
 801         """ Unused Methods. Any Ideas?!?""" 
 802         # TODO: Anything here? 
 806     def DoGetBestSize(self
): 
 808         Returns the best size for this panel, based upon the font 
 809         assigned to this window, and the caption string 
 812         if self
.IsVertical(): 
 813             x
, y 
= self
.GetTextExtent(self
._caption
) 
 815             y
, x 
= self
.GetTextExtent(self
._caption
) 
 817         if x 
< self
._iconWidth
: 
 820         if y 
< self
._iconHeight
: 
 823         # TODO: The extra FPB_EXTRA_X constants should be adjustable as well 
 825         return wx
.Size(x 
+ FPB_EXTRA_X
, y 
+ FPB_EXTRA_Y
) 
 828     def DrawVerticalGradient(self
, dc
, rect
): 
 829         """ Gradient fill from colour 1 to colour 2 with top to bottom. """ 
 831         if  rect
.height 
< 1 or rect
.width 
< 1: 
 834         dc
.SetPen(wx
.TRANSPARENT_PEN
) 
 836         # calculate gradient coefficients 
 837         col2 
= self
._style
.GetSecondColour() 
 838         col1 
= self
._style
.GetFirstColour() 
 840         r1
, g1
, b1 
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue()) 
 841         r2
, g2
, b2 
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue()) 
 843         flrect 
= float(rect
.height
) 
 845         rstep 
= float((r2 
- r1
)) / flrect
 
 846         gstep 
= float((g2 
- g1
)) / flrect
 
 847         bstep 
= float((b2 
- b1
)) / flrect
 
 851         for y 
in range(rect
.y
, rect
.y 
+ rect
.height
): 
 852             currCol 
= (r1 
+ rf
, g1 
+ gf
, b1 
+ bf
) 
 854             dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
)) 
 855             dc
.DrawRectangle(rect
.x
, rect
.y 
+ (y 
- rect
.y
), rect
.width
, rect
.height
) 
 861     def DrawHorizontalGradient(self
, dc
, rect
): 
 862         """ Gradient fill from colour 1 to colour 2 with left to right. """ 
 864         if rect
.height 
< 1 or rect
.width 
< 1: 
 867         dc
.SetPen(wx
.TRANSPARENT_PEN
) 
 869         # calculate gradient coefficients 
 870         col2 
= self
._style
.GetSecondColour() 
 871         col1 
= self
._style
.GetFirstColour() 
 873         r1
, g1
, b1 
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue()) 
 874         r2
, g2
, b2 
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue()) 
 876         flrect 
= float(rect
.width
) 
 878         rstep 
= float((r2 
- r1
)) / flrect
 
 879         gstep 
= float((g2 
- g1
)) / flrect
 
 880         bstep 
= float((b2 
- b1
)) / flrect
 
 884         for x 
in range(rect
.x
, rect
.x 
+ rect
.width
): 
 885             currCol 
= (r1 
+ rf
, g1 
+ gf
, b1 
+ bf
) 
 887             dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
)) 
 888             dc
.DrawRectangle(rect
.x 
+ (x 
- rect
.x
), rect
.y
, 1, rect
.height
) 
 894     def DrawSingleColour(self
, dc
, rect
): 
 895         """ Single colour fill. This is the most easy one to find. """ 
 897         if rect
.height 
< 1 or rect
.width 
< 1: 
 900         dc
.SetPen(wx
.TRANSPARENT_PEN
) 
 902         # draw simple rectangle 
 903         dc
.SetBrush(wx
.Brush(self
._style
.GetFirstColour(), wx
.SOLID
)) 
 904         dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, rect
.height
) 
 907     def DrawSingleRectangle(self
, dc
, rect
): 
 908         """ Single rectangle. This is the most easy one to find. """ 
 910         if rect
.height 
< 2 or rect
.width 
< 1: 
 913         # single frame, set up internal fill colour 
 915         if self
._style
.GetCaptionStyle() == CAPTIONBAR_RECTANGLE
: 
 916             color 
= self
.GetParent().GetBackgroundColour() 
 917             br 
= wx
.Brush(color
, wx
.SOLID
) 
 919             color 
= self
._style
.GetFirstColour() 
 920             br 
= wx
.Brush(color
, wx
.SOLID
) 
 922         # setup the pen frame 
 924         pen 
= wx
.Pen(self
._style
.GetSecondColour()) 
 927         dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, rect
.height 
- 1) 
 929         bgpen 
= wx
.Pen(self
.GetParent().GetBackgroundColour()) 
 931         dc
.DrawLine(rect
.x
, rect
.y 
+ rect
.height 
- 1, rect
.x 
+ rect
.width
, 
 932                     rect
.y 
+ rect
.height 
- 1) 
 935     def OnSize(self
, event
): 
 936         """ Handles the size events for the CaptionBar.""" 
 938         if not self
._controlCreated
: 
 942         size 
= event
.GetSize() 
 946             # What I am doing here is simply invalidating the part of the window 
 947             # exposed. So when I make a rect with as width the newly exposed part, 
 948             # and the x,y of the old window size origin, I don't need a bitmap 
 949             # calculation in it, or do I ? The bitmap needs redrawing anyway.  
 950             # Leave it like this until I figured it out. 
 952             # set rect to redraw as old bitmap area which is entitled to redraw 
 954             rect 
= wx
.Rect(size
.GetWidth() - self
._iconWidth 
- self
._rightIndent
, 0, 
 955                            self
._iconWidth 
+ self
._rightIndent
, 
 956                            self
._iconWidth 
+ self
._rightIndent
) 
 958             # adjust rectangle when more is slided so we need to redraw all 
 959             # the old stuff but not all (ugly flickering) 
 961             diffX 
= size
.GetWidth() - self
._oldSize
.GetWidth() 
 965                 # adjust the rect with all the crap to redraw 
 967                 rect
.SetWidth(rect
.GetWidth() + diffX 
+ 10) 
 968                 rect
.SetX(rect
.GetX() - diffX 
- 10) 
 970             self
.RefreshRect(rect
) 
 974             rect 
= self
.GetRect() 
 975             self
.RefreshRect(rect
) 
 980     def RedrawIconBitmap(self
): 
 981         """ Redraws the icons (if they exists). """ 
 985             # invalidate the bitmap area and force a redraw 
 987             rect 
= self
.GetRect() 
 989             rect
.SetX(rect
.GetWidth() - self
._iconWidth 
- self
._rightIndent
) 
 990             rect
.SetWidth(self
._iconWidth 
+ self
._rightIndent
) 
 991             self
.RefreshRect(rect
) 
 994 # ---------------------------------------------------------------------------------- # 
 996 # ---------------------------------------------------------------------------------- # 
 998 class FoldPanelBar(wx
.Panel
): 
1000     The FoldPanelBar is a class which can maintain a list of 
1001     collapsable panels.  Once a panel is collapsed, only it's caption 
1002     bar is visible to the user. This will provide more space for the 
1003     other panels, or allow the user to close panels which are not used 
1004     often to get the most out of the work area. 
1006     This control is easy to use. Simply create it as a child for a 
1007     panel or sash window, and populate panels with 
1008     `AddFoldPanel`. Then use the `AdddFoldPanelWindow` to add 
1009     `wx.Window` derived controls to the current fold panel. Use 
1010     `AddFoldPanelSeparator` to put separators between the groups of 
1011     controls that need a visual separator to group them 
1012     together. After all is constructed, the user can fold the panels 
1013     by doubleclicking on the bar or single click on the arrow, which 
1014     will indicate the collapsed or expanded state. 
1016     # Define Empty CaptionBar Style 
1017     EmptyCaptionBarStyle 
= CaptionBarStyle() 
1019     def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
, 
1020                  style
=FPB_DEFAULT_STYLE
, extraStyle
=FPB_DEFAULT_EXTRASTYLE
):  
1021         """ Default Class Constructor. """ 
1023         self
._controlCreated 
= False 
1024         self
._extraStyle 
= extraStyle
 
1026         # make sure there is any orientation 
1027         if style 
& FPB_HORIZONTAL 
!= FPB_HORIZONTAL
: 
1028             style 
= style | FPB_VERTICAL
 
1030         if style 
& FPB_HORIZONTAL 
== 4: 
1031             self
._isVertical 
= False 
1033             self
._isVertical 
= True 
1036         # create the panel (duh!). This causes a size event, which we are going 
1037         # to skip when we are not initialised 
1039         wx
.Panel
.__init
__(self
, parent
, id, pos
, size
, style
) 
1041         # the fold panel area 
1043         self
._foldPanel 
= wx
.Panel(self
, wx
.ID_ANY
, pos
, size
, 
1044                                    wx
.NO_BORDER | wx
.TAB_TRAVERSAL
) 
1046         self
._controlCreated 
= True 
1049         self
.Bind(EVT_CAPTIONBAR
, self
.OnPressCaption
) 
1050         self
.Bind(wx
.EVT_SIZE
, self
.OnSizePanel
) 
1053     def AddFoldPanel(self
, caption
="", collapsed
=False, foldIcons
=None, 
1054                      cbstyle
=EmptyCaptionBarStyle
): 
1056         Adds a fold panel to the list of panels. 
1058         If the flag collapsed is set to True, the panel is collapsed 
1059         initially.  The FoldPanel item which is returned, can be used 
1060         as a reference to perform actions upon the fold panel like 
1061         collapsing it, expanding it, or deleting it from the list. 
1063         Use this foldpanel to add windows to it. Please consult 
1064         `AddFoldPanelWindow` and `AddFoldPanelSeparator` to know how 
1065         to add items derived from `wx.Window` to the panels. 
1068         # create a fold panel item, which is first only the caption. 
1069         # the user can now add a panel area which will be folded in 
1072         if foldIcons 
is None: 
1073             foldIcons 
= wx
.ImageList(16, 16) 
1075             bmp 
= GetExpandedIconBitmap() 
1077             bmp 
= GetCollapsedIconBitmap() 
1080         item 
= FoldPanelItem(self
._foldPanel
, -1, caption
=caption
, 
1081                              foldIcons
=foldIcons
, 
1082                              collapsed
=collapsed
, cbstyle
=cbstyle
) 
1085         if len(self
._panels
) > 0: 
1086             pos 
= self
._panels
[-1].GetItemPos() + self
._panels
[-1].GetPanelLength() 
1088         item
.Reposition(pos
) 
1089         self
._panels
.append(item
) 
1094     def AddFoldPanelWindow(self
, panel
, window
, flags
=FPB_ALIGN_WIDTH
, 
1095                            Spacing
=FPB_DEFAULT_SPACING
, 
1096                            leftSpacing
=FPB_DEFAULT_LEFTLINESPACING
, 
1097                            rightSpacing
=FPB_DEFAULT_RIGHTLINESPACING
): 
1099         Adds a `wx.Window` derived instance to the referenced 
1102         IMPORTANT: Make the window be a child of the FoldPanel. See 
1103         example that follows. The flags to be used are: 
1105             * FPB_ALIGN_WIDTH: Which means the wxWindow to be added 
1106               will be aligned to fit the width of the FoldPanel when 
1107               it is resized.  Very handy for sizer items, buttons and 
1110             * FPB_ALIGN_LEFT: Aligns left instead of fitting the 
1111               width of the child window to be added. Use either this 
1112               one or FPB_ALIGN_WIDTH. 
1114         The wx.Window to be added can be slightly indented from left 
1115         and right so it is more visibly placed in the FoldPanel. Use 
1116         Spacing > 0 to give the control an y offset from the previous 
1117         wx.Window added, use leftSpacing to give it a slight indent 
1118         from the left, and rightSpacing also reserves a little space 
1119         on the right so the wxWindow can be properly placed in the 
1122         The following example adds a FoldPanel to the FoldPanelBar and 
1123         adds two wx.Window derived controls to the FoldPanel:: 
1125             # create the FoldPanelBar 
1126             >>> m_pnl = FoldPanelBar(self, wx.ID_ANY, wx.DefaultPosition, 
1127                                      wx.DefaultSize, FPB_DEFAULT_STYLE, 
1128                                      FPB_COLLAPSE_TO_BOTTOM) 
1130             # add a foldpanel to the control. "Test me" is the caption and it is 
1131             # initially not collapsed. 
1132             >>> item = m_pnl.AddFoldPanel("Test me", False) 
1134             # now add a button to the fold panel. Mind that the button should be 
1135             # made child of the FoldPanel and not of the main form. 
1136             >>> m_pnl.AddFoldPanelWindow(item, wx.Button(item, ID_COLLAPSEME, 
1139             # add a separator between the two controls. This is purely a visual 
1140             # line that can have a certain color and also the indents and width 
1141             # aligning like a control. 
1142             >>> m_pnl.AddFoldPanelSeparator(item) 
1144             # now add a text ctrl. Also very easy. Align this on width so that 
1145             # when the control gets wider the text control also sizes along. 
1146             >>> m_pnl.AddFoldPanelWindow(item, wx.TextCtrl(item, wx.ID_ANY, "Comment"), 
1147                                          FPB_ALIGN_WIDTH, FPB_DEFAULT_SPACING, 20) 
1152             item 
= self
._panels
.index(panel
) 
1154             raise "ERROR: Invalid Panel Passed To AddFoldPanelWindow: " + repr(panel
) 
1156         panel
.AddWindow(window
, flags
, Spacing
, leftSpacing
, rightSpacing
) 
1158         # TODO: Take old and new height, and if difference, reposition all the lower 
1159         # panels this is because the user can add new wxWindow controls somewhere in 
1160         # between when other panels are already present. 
1165     def AddFoldPanelSeparator(self
, panel
, colour
=wx
.BLACK
, 
1166                               Spacing
=FPB_DEFAULT_SPACING
, 
1167                               leftSpacing
=FPB_DEFAULT_LEFTLINESPACING
, 
1168                               rightSpacing
=FPB_DEFAULT_RIGHTLINESPACING
): 
1170         Adds a separator line to the current FoldPanel. 
1172         The seperator is a simple line which is drawn and is no real 
1173         component.  It can be used to separate groups of controls 
1174         which belong to each other.  The colour is adjustable, and it 
1175         takes the same Spacing, leftSpacing and rightSpacing as 
1176         `AddFoldPanelWindow`. 
1180             item 
= self
._panels
.index(panel
) 
1182             raise "ERROR: Invalid Panel Passed To AddFoldPanelSeparator: " + repr(panel
) 
1184         panel
.AddSeparator(colour
, Spacing
, leftSpacing
, rightSpacing
) 
1188     def OnSizePanel(self
, event
): 
1189         """ Handles the EVT_SIZE event for the FoldPanelBar. """ 
1191         # skip all stuff when we are not initialised yet 
1193         if not self
._controlCreated
: 
1197         foldrect 
= self
.GetRect() 
1199         # fold panel itself. If too little space, 
1205         self
._foldPanel
.SetSize(foldrect
[2:]) 
1207         if self
._extraStyle 
& FPB_COLLAPSE_TO_BOTTOM 
or self
._extraStyle 
& FPB_EXCLUSIVE_FOLD
: 
1208             rect 
= self
.RepositionCollapsedToBottom() 
1209             vertical 
= self
.IsVertical() 
1210             if vertical 
and rect
.GetHeight() > 0 or not vertical 
and rect
.GetWidth() > 0: 
1211                 self
.RefreshRect(rect
) 
1213         # TODO: A smart way to check wether the old - new width of the 
1214         # panel changed, if so no need to resize the fold panel items 
1216         self
.RedisplayFoldPanelItems() 
1219     def OnPressCaption(self
, event
): 
1220         """ Handles the EVT_CAPTIONBAR event in the FoldPanelBar. """ 
1222         # act upon the folding or expanding status of the bar 
1223         # to expand or collapse the panel(s) 
1225         if event
.GetFoldStatus(): 
1226             self
.Collapse(event
.GetTag()) 
1228             self
.Expand(event
.GetTag()) 
1233     def RefreshPanelsFrom(self
, item
): 
1234         """ Refreshes all the panels from given index down to last one. """ 
1237             i 
= self
._panels
.index(item
) 
1239             raise "ERROR: Invalid Panel Passed To RefreshPanelsFrom: " + repr(item
) 
1243         # if collapse to bottom is on, the panels that are not expanded 
1244         # should be drawn at the bottom. All panels that are expanded 
1245         # are drawn on top. The last expanded panel gets all the extra space 
1247         if self
._extraStyle 
& FPB_COLLAPSE_TO_BOTTOM 
or self
._extraStyle 
& FPB_EXCLUSIVE_FOLD
: 
1251             for panels 
in self
._panels
: 
1253                 if panels
.IsExpanded(): 
1254                     offset 
= offset 
+ panels
.Reposition(offset
) 
1256             # put all non collapsed panels at the bottom where there is space,  
1257             # else put them right behind the expanded ones 
1259             self
.RepositionCollapsedToBottom() 
1263             pos 
= self
._panels
[i
].GetItemPos() + self
._panels
[i
].GetPanelLength() 
1264             for j 
in range(i
+1, len(self
._panels
)): 
1265                 pos 
= pos 
+ self
._panels
[j
].Reposition(pos
) 
1270     def RedisplayFoldPanelItems(self
): 
1271         """ Resizes the fold panels so they match the width. """ 
1272         # resize them all. No need to reposition 
1273         for panels 
in self
._panels
: 
1274             panels
.ResizePanel() 
1278     def RepositionCollapsedToBottom(self
): 
1280         Repositions all the collapsed panels to the bottom. 
1282         When it is not possible to align them to the bottom, stick 
1283         them behind the visible panels. The Rect holds the slack area 
1284         left between last repositioned panel and the bottom 
1285         panels. This needs to get a refresh. 
1288         value 
= wx
.Rect(0,0,0,0) 
1289         vertical 
= self
.IsVertical() 
1291         # determine wether the number of panels left 
1292         # times the size of their captions is enough 
1293         # to be placed in the left over space 
1297         collapsed
, expanded
, values 
= self
.GetPanelsLength(collapsed
, expanded
) 
1299         # if no room stick them behind the normal ones, else 
1302         if (vertical 
and [self
.GetSize().GetHeight()] or \
 
1303             [self
.GetSize().GetWidth()])[0] - expanded 
- collapsed 
< 0: 
1307             # value is the region which is left unpainted 
1308             # I will send it back as 'slack' so it does not need to 
1311             value
.SetHeight(self
.GetSize().GetHeight()) 
1312             value
.SetWidth(self
.GetSize().GetWidth()) 
1315                 value
.SetY(expanded
) 
1316                 value
.SetHeight(value
.GetHeight() - expanded
) 
1318                 value
.SetX(expanded
) 
1319                 value
.SetWidth(value
.GetWidth() - expanded
) 
1321             offset 
= (vertical 
and [self
.GetSize().GetHeight()] or \
 
1322                       [self
.GetSize().GetWidth()])[0] - collapsed
 
1327         for panels 
in self
._panels
: 
1328             if not panels
.IsExpanded(): 
1329                 offset 
= offset 
+ panels
.Reposition(offset
) 
1334     def GetPanelsLength(self
, collapsed
, expanded
): 
1336         Returns the length of the panels that are expanded and 
1339         This is useful to determine quickly what size is used to 
1340         display, and what is left at the bottom (right) to align the 
1346         # assumed here that all the panels that are expanded 
1347         # are positioned after each other from 0,0 to end. 
1349         for j 
in range(0, len(self
._panels
)): 
1350             offset 
= self
._panels
[j
].GetPanelLength() 
1351             value 
= value 
+ offset
 
1352             if self
._panels
[j
].IsExpanded(): 
1353                 expanded 
= expanded 
+ offset
 
1355                 collapsed 
= collapsed 
+ offset
 
1357         return collapsed
, expanded
, value
 
1360     def Collapse(self
, foldpanel
): 
1362         Collapses the given FoldPanel reference, and updates the 
1365         In the FPB_COLLAPSE_TO_BOTTOM style, all collapsed captions 
1366         are put at the bottom of the control. In the normal mode, they 
1367         stay where they are. 
1371             item 
= self
._panels
.index(foldpanel
) 
1373             raise "ERROR: Invalid Panel Passed To Collapse: " + repr(foldpanel
) 
1375         foldpanel
.Collapse() 
1376         self
.RefreshPanelsFrom(foldpanel
) 
1379     def Expand(self
, foldpanel
): 
1381         Expands the given FoldPanel reference, and updates the 
1384         In the FPB_COLLAPSE_TO_BOTTOM style, they will be removed from 
1385         the bottom and the order where the panel originally was placed 
1391         if self
._extraStyle 
& FPB_SINGLE_FOLD 
or self
._extraStyle 
& FPB_EXCLUSIVE_FOLD
: 
1393             for panel 
in self
._panels
: 
1399             if self
._extraStyle 
& FPB_EXCLUSIVE_FOLD
: 
1400                 self
.RepositionCollapsedToBottom() 
1401             self
.RefreshPanelsFrom(self
._panels
[0]) 
1403             self
.RefreshPanelsFrom(foldpanel
) 
1406     def ApplyCaptionStyle(self
, foldpanel
, cbstyle
): 
1408         Sets the style of the caption bar (`CaptionBar`) of the 
1411         The changes are applied immediately. All styles not set in the 
1412         CaptionBarStyle class are not applied. Use the CaptionBar 
1413         reference to indicate what captionbar you want to apply the 
1414         style to. To apply one style to all CaptionBar items, use 
1415         `ApplyCaptionStyleAll` 
1417         foldpanel
.ApplyCaptionStyle(cbstyle
) 
1420     def ApplyCaptionStyleAll(self
, cbstyle
): 
1422         Sets the style of all the caption bars of the FoldPanel. 
1424         The changes are applied immediately. 
1426         for panels 
in self
._panels
: 
1427             self
.ApplyCaptionStyle(panels
, cbstyle
) 
1430     def GetCaptionStyle(self
, foldpanel
): 
1432         Returns the currently used caption style for the FoldPanel. 
1434         It is returned as a CaptionBarStyle class. After modifying it, 
1435         it can be set again. 
1437         return foldpanel
.GetCaptionStyle() 
1440     def IsVertical(self
): 
1442         Returns whether the CaptionBar has default orientation or not. 
1444         Default is vertical. 
1446         return self
._isVertical
 
1449     def GetFoldPanel(self
, item
): 
1451         Returns the panel associated with the index "item". 
1453         See the example at the bottom of the module, especially the events 
1454         for the "Collapse Me" and "Expand Me" buttons. 
1457             ind 
= self
._panels
[item
] 
1458             return self
._panels
[item
] 
1460             raise "ERROR: List Index Out Of Range Or Bad Item Passed: " + repr(item
) + \
 
1461                   ". Item Should Be An Integer Between " + repr(0) + " And " + \
 
1462                   repr(len(self
._panels
)) 
1466         """ Returns the number of panels in the FoldPanelBar. """ 
1469             return len(self
._panels
) 
1471             raise "ERROR: No Panels Have Been Added To FoldPanelBar" 
1475 # --------------------------------------------------------------------------------- # 
1476 # class FoldPanelItem 
1477 # --------------------------------------------------------------------------------- # 
1479 class FoldPanelItem(wx
.Panel
): 
1481     This class is a child sibling of the `FoldPanelBar` class. It will 
1482     contain a `CaptionBar` class for receiving of events, and a the 
1483     rest of the area can be populated by a `wx.Panel` derived class. 
1485     # Define Empty CaptionBar Style 
1486     EmptyCaptionBarStyle 
= CaptionBarStyle() 
1488     def __init__(self
, parent
, id=wx
.ID_ANY
, caption
="", foldIcons
=None, 
1489                  collapsed
=False, cbstyle
=EmptyCaptionBarStyle
): 
1490         """ Default Class Constructor. """ 
1492         wx
.Panel
.__init
__(self
, parent
, id, wx
.Point(0,0), style
=wx
.CLIP_CHILDREN
) 
1493         self
._controlCreated 
= False 
1496         self
._LastInsertPos 
= 0 
1498         self
._userSized 
= False 
1500         if foldIcons 
is None: 
1501             foldIcons 
= wx
.ImageList(16, 16) 
1503             bmp 
= GetExpandedIconBitmap() 
1505             bmp 
= GetCollapsedIconBitmap() 
1508         self
._foldIcons 
= foldIcons
 
1510         # create the caption bar, in collapsed or expanded state 
1512         self
._captionBar 
= CaptionBar(self
, wx
.ID_ANY
, wx
.Point(0,0), 
1513                                       size
=wx
.DefaultSize
, caption
=caption
, 
1514                                       foldIcons
=foldIcons
, cbstyle
=cbstyle
) 
1517             self
._captionBar
.Collapse() 
1519         self
._controlCreated 
= True 
1521         # make initial size for component, if collapsed, the 
1522         # size is determined on the panel height and won't change 
1524         size 
= self
._captionBar
.GetSize() 
1526         self
._PanelSize 
= (self
.IsVertical() and [size
.GetHeight()] or \
 
1527                            [size
.GetWidth()])[0] 
1529         self
._LastInsertPos 
= self
._PanelSize
 
1532         self
.Bind(EVT_CAPTIONBAR
, self
.OnPressCaption
) 
1533         self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
) 
1536     def AddWindow(self
, window
, flags
=FPB_ALIGN_WIDTH
, Spacing
=FPB_DEFAULT_SPACING
, 
1537                   leftSpacing
=FPB_DEFAULT_LEFTLINESPACING
, 
1538                   rightSpacing
=FPB_DEFAULT_RIGHTLINESPACING
): 
1540         Adds a window item to the list of items on this panel. 
1542         The flags are FPB_ALIGN_LEFT for a non sizing window element, 
1543         and FPB_ALIGN_WIDTH for a width aligned item. The Spacing 
1544         parameter reserves a number of pixels before the window 
1545         element, and leftSpacing is an indent.  rightSpacing is only 
1546         relevant when the style FPB_ALIGN_WIDTH is chosen. 
1549         wi 
= FoldWindowItem(self
, window
, Type
="WINDOW", flags
=flags
, Spacing
=Spacing
, 
1550                             leftSpacing
=leftSpacing
, rightSpacing
=rightSpacing
) 
1552         self
._items
.append(wi
) 
1554         vertical 
= self
.IsVertical() 
1556         self
._Spacing 
= Spacing
 
1557         self
._leftSpacing 
= leftSpacing
 
1558         self
._rightSpacing 
= rightSpacing
 
1560         xpos 
= (vertical 
and [leftSpacing
] or [self
._LastInsertPos 
+ Spacing
])[0] 
1561         ypos 
= (vertical 
and [self
._LastInsertPos 
+ Spacing
] or [leftSpacing
])[0] 
1563         window
.SetDimensions(xpos
, ypos
, -1, -1, wx
.SIZE_USE_EXISTING
) 
1565         self
._LastInsertPos 
= self
._LastInsertPos 
+ wi
.GetWindowLength(vertical
) 
1569     def AddSeparator(self
, colour
=wx
.BLACK
, Spacing
=FPB_DEFAULT_SPACING
, 
1570                      leftSpacing
=FPB_DEFAULT_LEFTSPACING
, 
1571                      rightSpacing
=FPB_DEFAULT_RIGHTSPACING
): 
1573         Adds a separator item to the list of items on this panel. """ 
1575         wi 
= FoldWindowItem(self
, window
=None, Type
="SEPARATOR", 
1576                             flags
=FPB_ALIGN_WIDTH
, y
=self
._LastInsertPos
, 
1577                             colour
=colour
, Spacing
=Spacing
, leftSpacing
=leftSpacing
, 
1578                             rightSpacing
=rightSpacing
) 
1580         self
._items
.append(wi
) 
1581         self
._LastInsertPos 
= self
._LastInsertPos 
+ \
 
1582                               wi
.GetWindowLength(self
.IsVertical()) 
1587     def Reposition(self
, pos
): 
1589         Repositions this FoldPanelBar and reports the length occupied 
1590         for the next FoldPanelBar in the list. 
1592         # NOTE: Call Resize before Reposition when an item is added, because the new 
1593         # size needed will be calculated by Resize. Of course the relative position 
1594         # of the controls have to be correct in respect to the caption bar 
1598         vertical 
= self
.IsVertical() 
1599         xpos 
= (vertical 
and [-1] or [pos
])[0] 
1600         ypos 
= (vertical 
and [pos
] or [-1])[0] 
1602         self
.SetDimensions(xpos
, ypos
, -1, -1, wx
.SIZE_USE_EXISTING
) 
1607         return self
.GetPanelLength() 
1610     def OnPressCaption(self
, event
): 
1611         """ Handles the EVT_CAPTIONBAR event in the FoldPanelItem. """ 
1613         # tell the upper container we are responsible 
1614         # for this event, so it can fold the panel item 
1621     def ResizePanel(self
): 
1622         """ Resizes the panel. """ 
1624         # prevent unnecessary updates by blocking repaints for a sec 
1628         vertical 
= self
.IsVertical() 
1629         # force this panel to take the width of the parent panel and the y of the 
1630         # user or calculated width (which will be recalculated by the contents here) 
1633         if self
._captionBar
.IsCollapsed(): 
1634             size 
= self
._captionBar
.GetSize() 
1635             self
._PanelSize 
= (vertical 
and [size
.GetHeight()] or [size
.GetWidth()])[0] 
1637             size 
= self
.GetBestSize() 
1638             self
._PanelSize 
= (vertical 
and [size
.GetHeight()] or [size
.GetWidth()])[0] 
1642                     size
.SetHeight(self
._UserSize
) 
1644                     size
.SetWidth(self
._UserSize
) 
1646         pnlsize 
= self
.GetParent().GetSize() 
1649             size
.SetWidth(pnlsize
.GetWidth()) 
1651             size
.SetHeight(pnlsize
.GetHeight()) 
1653         # resize caption bar 
1654         xsize 
= (vertical 
and [size
.GetWidth()] or [-1])[0] 
1655         ysize 
= (vertical 
and [-1] or [size
.GetHeight()])[0] 
1657         self
._captionBar
.SetSize((xsize
, ysize
)) 
1662         # go by all the controls and call Layout 
1664         for items 
in self
._items
: 
1665             items
.ResizeItem((vertical 
and [size
.GetWidth()] or \
 
1666                               [size
.GetHeight()])[0], vertical
) 
1671     def OnPaint(self
, event
): 
1672         """ Handles the EVT_PAINT event in the FoldPanelItem. """ 
1674         # draw all the items that are lines 
1676         dc 
= wx
.PaintDC(self
) 
1677         vertical 
= self
.IsVertical() 
1679         for item 
in self
._items
: 
1681             if item
.GetType() == "SEPARATOR": 
1682                 pen 
= wx
.Pen(item
.GetLineColour(), 1, wx
.SOLID
) 
1684                 a 
= item
.GetLeftSpacing() 
1685                 b 
= item
.GetLineY() + item
.GetSpacing() 
1686                 c 
= item
.GetLineLength() 
1690                     dc
.DrawLine(a
, b
, d
, b
) 
1692                     dc
.DrawLine(b
, a
, b
, d
) 
1697     def IsVertical(self
): 
1699         Returns wether the CaptionBar Has Default Orientation Or Not. 
1701         Default is vertical. 
1704         # grandparent of FoldPanelItem is FoldPanelBar 
1705         # default is vertical 
1707         if isinstance(self
.GetGrandParent(), FoldPanelBar
): 
1708             return self
.GetGrandParent().IsVertical() 
1710             raise "ERROR: Wrong Parent " + repr(self
.GetGrandParent()) 
1713     def IsExpanded(self
): 
1715         Returns expanded or collapsed status.  If the panel is 
1716         expanded, True is returned. 
1719         return not self
._captionBar
.IsCollapsed() 
1722     def GetItemPos(self
): 
1723         """ Returns item's position. """ 
1725         return self
._itemPos
 
1729         # this should not be called by the user, because it doesn't trigger the 
1730         # parent  to tell it that we are collapsed or expanded, it only changes 
1733         self
._captionBar
.Collapse() 
1738         # this should not be called by the user, because it doesn't trigger the 
1739         # parent to tell it that we are collapsed or expanded, it only changes 
1742         self
._captionBar
.Expand() 
1746     def GetPanelLength(self
): 
1747         """ Returns size of panel. """ 
1749         if self
._captionBar
.IsCollapsed(): 
1750             return self
.GetCaptionLength() 
1751         elif self
._userSized
: 
1752             return self
._UserSize
 
1754         return self
._PanelSize
 
1757     def GetCaptionLength(self
): 
1759         Returns height of caption only. This is for folding 
1760         calculation purposes. 
1763         size 
= self
._captionBar
.GetSize() 
1764         return (self
.IsVertical() and [size
.GetHeight()] or [size
.GetWidth()])[0] 
1767     def ApplyCaptionStyle(self
, cbstyle
): 
1768         """ Applies the style defined in cbstyle to the CaptionBar.""" 
1770         self
._captionBar
.SetCaptionStyle(cbstyle
) 
1773     def GetCaptionStyle(self
): 
1775         Returns the current style of the captionbar in a 
1776         CaptionBarStyle class. 
1778         This can be used to change and set back the changes. 
1781         return self
._captionBar
.GetCaptionStyle() 
1784 # ----------------------------------------------------------------------------------- # 
1785 # class FoldWindowItem 
1786 # ----------------------------------------------------------------------------------- # 
1788 class FoldWindowItem
: 
1790     This class is a child sibling of the `FoldPanelItem` class. It 
1791     will contain wx.Window that can be either a separator (a colored 
1792     line simulated by a wx.Window) or a wxPython controls (such as a 
1793     wx.Button, a wx.ListCtrl etc...). 
1795     def __init__(self
, parent
, window
=None, **kw
): 
1797         Default Class Constructor 
1801             Type = "WINDOW", flags = FPB_ALIGN_WIDTH, 
1802             Spacing = FPB_DEFAULT_SPACING, 
1803             leftSpacing = FPB_DEFAULT_LEFTSPACING, 
1804             rightSpacing = FPB_DEFAULT_RIGHTSPACING 
1809             y, lineColor = wx.BLACK, 
1810             flags = FPB_ALIGN_WIDTH, 
1811             Spacing = FPB_DEFAULT_SPACING, 
1812             leftSpacing = FPB_DEFAULT_LEFTLINESPACING, 
1813             rightSpacing = FPB_DEFAULT_RIGHTLINESPACING 
1817         if not kw
.has_key("Type"): 
1818             raise 'ERROR: Missing Window Type Information. This Should Be "WINDOW" Or "SEPARATOR"' 
1820         if kw
.get("Type") == "WINDOW": 
1821             # Window constructor. This initialises the class as a wx.Window Type 
1823             if kw
.has_key("flags"): 
1824                 self
._flags 
= kw
.get("flags") 
1826                 self
._flags 
= FPB_ALIGN_WIDTH
 
1827             if kw
.has_key("Spacing"): 
1828                 self
._Spacing 
= kw
.get("Spacing") 
1830                 self
._Spacing 
= FPB_DEFAULT_SPACING
 
1831             if kw
.has_key("leftSpacing"): 
1832                 self
._leftSpacing 
= kw
.get("leftSpacing") 
1834                 self
._leftSpacing 
= FPB_DEFAULT_LEFTSPACING
 
1835             if kw
.has_key("rightSpacing"): 
1836                 self
._rightSpacing 
= kw
.get("rightSpacing") 
1838                 self
._rightSpacing 
= FPB_DEFAULT_RIGHTSPACING
 
1841             self
._sepLineColour 
= None 
1845         elif kw
.get("Type") == "SEPARATOR": 
1846             # separator constructor. This initialises the class as a separator type 
1849                 self
._lineY 
= kw
.get("y") 
1851                 raise "ERROR: Undefined Y Position For The Separator" 
1852             if kw
.has_key("lineColour"): 
1853                 self
._sepLineColour 
= kw
.get("lineColour") 
1855                 self
._sepLineColour 
= wx
.BLACK
 
1856             if kw
.has_key("flags"): 
1857                 self
._flags 
= kw
.get("flags") 
1859                 self
._flags 
= FPB_ALIGN_WIDTH
 
1860             if kw
.has_key("Spacing"): 
1861                 self
._Spacing 
= kw
.get("Spacing") 
1863                 self
._Spacing 
= FPB_DEFAULT_SPACING
 
1864             if kw
.has_key("leftSpacing"): 
1865                 self
._leftSpacing 
= kw
.get("leftSpacing") 
1867                 self
._leftSpacing 
= FPB_DEFAULT_LEFTSPACING
 
1868             if kw
.has_key("rightSpacing"): 
1869                 self
._rightSpacing 
= kw
.get("rightSpacing") 
1871                 self
._rightSpacing 
= FPB_DEFAULT_RIGHTSPACING
 
1876             raise "ERROR: Undefined Window Type Selected: " + repr(kw
.get("Type")) 
1878         self
._type 
= kw
.get("Type") 
1879         self
._lineLength 
= 0 
1888     def GetLineLength(self
): 
1889         return self
._lineLength
 
1891     def GetLineColour(self
): 
1892         return self
._sepLineColour
 
1894     def GetLeftSpacing(self
): 
1895         return self
._leftSpacing
 
1897     def GetRightSpacing(self
): 
1898         return self
._rightSpacing
 
1900     def GetSpacing(self
): 
1901         return self
._Spacing
 
1904     def GetWindowLength(self
, vertical
=True): 
1906         Returns space needed by the window if type is FoldWindowItem 
1907         "WINDOW" and returns the total size plus the extra spacing. 
1911         if self
._type 
== "WINDOW": 
1912             size 
= self
._wnd
.GetSize() 
1913             value 
= (vertical 
and [size
.GetHeight()] or [size
.GetWidth()])[0] + \
 
1916         elif self
._type 
== "SEPARATOR": 
1917             value 
= 1 + self
._Spacing
 
1922     def ResizeItem(self
, size
, vertical
=True): 
1924         Resizes the element, whatever it is. 
1926         A separator or line will be always aligned by width or height 
1927         depending on orientation of the whole panel. 
1930         if self
._flags 
& FPB_ALIGN_WIDTH
: 
1931             # align by taking full width 
1932             mySize 
= size 
- self
._leftSpacing 
- self
._rightSpacing
 
1935                 mySize 
= 10  # can't have negative width 
1937             if self
._type 
== "SEPARATOR": 
1938                 self
._lineLength 
= mySize
 
1940                 xsize 
= (vertical 
and [mySize
] or [-1])[0] 
1941                 ysize 
= (vertical 
and [-1] or [mySize
])[0] 
1943                 self
._wnd
.SetSize((xsize
, ysize
))