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