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 Latest Revision: Andrea Gavana @ 05 Nov 2005, 23.30 CET
120 #----------------------------------------------------------------------
121 # Collapsed And Expanded Bitmap Images
122 # Created With img2py.py
123 #----------------------------------------------------------------------
125 def GetCollapsedIconData():
127 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
128 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
129 \x00\x007IDAT8\x8dcddbf\xa0\x040Q\xa4{\xf0\x1b\xf0\xff\xdf\xdf\xff\x03\xe7\
130 \x02\x98\xed\x84\\A\x1b\x17\xa0\xdb\x8a\xcf\x15\xd4w\x01.\xdbp\x89S\xec\x02\
131 \xc6\xd1\xbc\xc0\x00\x00\x9a\xf5\x1b\xfa\xf9m$?\x00\x00\x00\x00IEND\xaeB`\
134 def GetCollapsedIconBitmap():
135 return wx
.BitmapFromImage(GetCollapsedIconImage())
137 def GetCollapsedIconImage():
139 stream
= cStringIO
.StringIO(GetCollapsedIconData())
140 return wx
.ImageFromStream(stream
)
142 #----------------------------------------------------------------------
143 def GetExpandedIconData():
145 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
146 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
147 \x00\x00BIDAT8\x8dcddbf\xa0\x040Q\xa4{P\x18\xc0\x82.\xf0\xff\xdf\xdf\xff\xb8\
148 \x143213R\xdd\x05\x18\x06`\xb3\x05\x9f8m\x02\x11\xdd6\\\xb6\xd3\xce\x05\xc8\
149 \xb6\xe2\xb3\x9d*.`\x1c\xcd\x0b\x0c\x00\x9e\xbc\x04W\x19\xcfa\xb5\x00\x00\
150 \x00\x00IEND\xaeB`\x82'
152 def GetExpandedIconBitmap():
153 return wx
.BitmapFromImage(GetExpandedIconImage())
155 def GetExpandedIconImage():
157 stream
= cStringIO
.StringIO(GetExpandedIconData())
158 return wx
.ImageFromStream(stream
)
160 #----------------------------------------------------------------------
162 #----------------------------------------------------------------------
163 # FOLDPANELBAR Starts Here
164 #----------------------------------------------------------------------
168 #- CAPTIONBAR_GRADIENT_V: Draws a vertical gradient from top to bottom
169 #- CAPTIONBAR_GRADIENT_H: Draws a horizontal gradient from left to right
170 #- CAPTIONBAR_SINGLE: Draws a single filled rectangle to draw the caption
171 #- CAPTIONBAR_RECTANGLE: Draws a single colour with a rectangle around the caption
172 #- CAPTIONBAR_FILLED_RECTANGLE: Draws a filled rectangle and a border around it
174 CAPTIONBAR_NOSTYLE
= 0
175 CAPTIONBAR_GRADIENT_V
= 1
176 CAPTIONBAR_GRADIENT_H
= 2
177 CAPTIONBAR_SINGLE
= 3
178 CAPTIONBAR_RECTANGLE
= 4
179 CAPTIONBAR_FILLED_RECTANGLE
= 5
184 # pixels of the bmp to be aligned from the right filled with space
185 FPB_BMP_RIGHTSPACE
= 2
187 # Now supported! Single fold forces
188 # other panels to close when they are open, and only opens the current panel.
189 # This will allow the open panel to gain the full size left in the client area
190 FPB_SINGLE_FOLD
= 0x0001
192 # All panels are stacked to the bottom. When they are expanded again they
194 FPB_COLLAPSE_TO_BOTTOM
= 0x0002
196 # Now supported! Single fold plus panels
197 # will be stacked at the bottom
198 FPB_EXCLUSIVE_FOLD
= 0x0004
201 FPB_HORIZONTAL
= wx
.HORIZONTAL
202 FPB_VERTICAL
= wx
.VERTICAL
204 # Default Extrastyle of the FoldPanelBar
205 FPB_DEFAULT_EXTRASTYLE
= 0
206 # Default style of the FoldPanelBar
207 FPB_DEFAULT_STYLE
= wx
.TAB_TRAVERSAL | wx
.NO_BORDER
209 # FoldPanelItem default settings
213 FPB_DEFAULT_LEFTSPACING
= 5
214 FPB_DEFAULT_RIGHTSPACING
= 10
215 FPB_DEFAULT_SPACING
= 8
217 FPB_DEFAULT_LEFTLINESPACING
= 2
218 FPB_DEFAULT_RIGHTLINESPACING
= 2
221 # ------------------------------------------------------------------------------ #
222 # class CaptionBarStyle
223 # ------------------------------------------------------------------------------ #
225 class CaptionBarStyle
:
227 This class encapsulates the styles you wish to set for the
228 `CaptionBar` (this is the part of the FoldPanel where the caption
229 is displayed). It can either be applied at creation time be
230 reapplied when styles need to be changed.
232 At construction time, all styles are set to their default
233 transparency. This means none of the styles will be applied to
234 the `CaptionBar` in question, meaning it will be created using the
235 default internals. When setting i.e the color, font or panel
236 style, these styles become active to be used.
241 """ Default constructor for this class."""
246 def ResetDefaults(self
):
247 """ Resets default CaptionBarStyle."""
248 self
._firstColourUsed
= False
249 self
._secondColourUsed
= False
250 self
._textColourUsed
= False
251 self
._captionFontUsed
= False
252 self
._captionStyleUsed
= False
253 self
._captionStyle
= CAPTIONBAR_GRADIENT_V
256 # ------- CaptionBar Font -------
258 def SetCaptionFont(self
, font
):
260 Sets font for the caption bar.
262 If this is not set, the font property is undefined and will
263 not be used. Use `CaptionFontUsed` to check if this style is
266 self
._captionFont
= font
267 self
._captionFontUsed
= True
270 def CaptionFontUsed(self
):
271 """ Checks if the caption bar font is set. """
272 return self
._captionFontUsed
275 def GetCaptionFont(self
):
277 Returns the font for the caption bar.
279 Please be warned this will result in an assertion failure when
280 this property is not previously set.
282 :see: `SetCaptionFont`, `CaptionFontUsed`
284 return self
._captionFont
287 # ------- First Colour -------
289 def SetFirstColour(self
, colour
):
291 Sets first colour for the caption bar.
293 If this is not set, the colour property is undefined and will
294 not be used. Use `FirstColourUsed` to check if this style is
297 self
._firstColour
= colour
298 self
._firstColourUsed
= True
301 def FirstColourUsed(self
):
302 """ Checks if the first colour of the caption bar is set."""
303 return self
._firstColourUsed
306 def GetFirstColour(self
):
308 Returns the first colour for the caption bar.
310 Please be warned this will result in an assertion failure when
311 this property is not previously set.
313 :see: `SetFirstColour`, `FirstColourUsed`
315 return self
._firstColour
318 # ------- Second Colour -------
320 def SetSecondColour(self
, colour
):
322 Sets second colour for the caption bar.
324 If this is not set, the colour property is undefined and will
325 not be used. Use `SecondColourUsed` to check if this style is
328 self
._secondColour
= colour
329 self
._secondColourUsed
= True
332 def SecondColourUsed(self
):
333 """ Checks if the second colour of the caption bar is set."""
334 return self
._secondColourUsed
337 def GetSecondColour(self
):
339 Returns the second colour for the caption bar.
341 Please be warned this will result in an assertion failure when
342 this property is not previously set.
344 :see: `SetSecondColour`, `SecondColourUsed`
346 return self
._secondColour
349 # ------- Caption Text Colour -------
351 def SetCaptionColour(self
, colour
):
353 Sets caption colour for the caption bar.
355 If this is not set, the colour property is undefined and will
356 not be used. Use `CaptionColourUsed` to check if this style is
359 self
._textColour
= colour
360 self
._textColourUsed
= True
363 def CaptionColourUsed(self
):
364 """ Checks if the caption colour of the caption bar is set."""
365 return self
._textColourUsed
368 def GetCaptionColour(self
):
370 Returns the caption colour for the caption bar.
372 Please be warned this will result in an assertion failure
373 when this property is not previously set.
374 See also SetCaptionColour(), CaptionColourUsed()
376 return self
._textColour
379 # ------- CaptionStyle -------
381 def SetCaptionStyle(self
, style
):
383 Sets caption style for the caption bar.
385 If this is not set, the property is undefined and will not be
386 used. Use CaptionStyleUsed() to check if this style is used.
387 The following styles can be applied:
389 * CAPTIONBAR_GRADIENT_V: Draws a vertical gradient from top to bottom
391 * CAPTIONBAR_GRADIENT_H: Draws a horizontal gradient from
394 * CAPTIONBAR_SINGLE: Draws a single filled rectangle to
397 * CAPTIONBAR_RECTANGLE: Draws a single colour with a
398 rectangle around the caption
400 * CAPTIONBAR_FILLED_RECTANGLE: Draws a filled rectangle
401 and a border around it
404 self
._captionStyle
= style
405 self
._captionStyleUsed
= True
408 def CaptionStyleUsed(self
):
409 """ Checks if the caption style of the caption bar is set."""
410 return self
._captionStyleUsed
413 def GetCaptionStyle(self
):
415 Returns the caption style for the caption bar.
417 Please be warned this will result in an assertion failure
418 when this property is not previously set.
420 :see: `SetCaptionStyle`, `CaptionStyleUsed`
422 return self
._captionStyle
425 #-----------------------------------#
427 #-----------------------------------#
428 wxEVT_CAPTIONBAR
= wx
.NewEventType()
429 EVT_CAPTIONBAR
= wx
.PyEventBinder(wxEVT_CAPTIONBAR
, 0)
432 # ---------------------------------------------------------------------------- #
433 # class CaptionBarEvent
434 # ---------------------------------------------------------------------------- #
436 class CaptionBarEvent(wx
.PyCommandEvent
):
438 This event will be sent when a EVT_CAPTIONBAR is mapped in the parent.
439 It is to notify the parent that the bar is now in collapsed or expanded
440 state. The parent should re-arrange the associated windows accordingly
442 def __init__(self
, evtType
):
443 """ Default Constructor For This Class."""
444 wx
.PyCommandEvent
.__init
__(self
, evtType
)
447 def GetFoldStatus(self
):
449 Returns whether the bar is expanded or collapsed. True means
452 return not self
._bar
.IsCollapsed()
456 """ Returns The CaptionBar Selected."""
460 def SetTag(self
, tag
):
461 """ Assign A Tag To The Selected CaptionBar."""
466 """ Returns The Tag Assigned To The Selected CaptionBar."""
470 def SetBar(self
, bar
):
472 Sets the bar associated with this event.
474 Should not used by any other then the originator of the event.
479 # -------------------------------------------------------------------------------- #
481 # -------------------------------------------------------------------------------- #
483 class CaptionBar(wx
.Window
):
485 This class is a graphical caption component that consists of a
486 caption and a clickable arrow.
488 The CaptionBar fires an event EVT_CAPTIONBAR which is a
489 `CaptionBarEvent`. This event can be caught and the parent window
490 can act upon the collapsed or expanded state of the bar (which is
491 actually just the icon which changed). The parent panel can
492 reduce size or expand again.
495 # Define Empty CaptionBar Style
496 EmptyCaptionBarStyle
= CaptionBarStyle()
498 def __init__(self
, parent
, id, pos
, size
, caption
="",
499 foldIcons
=None, cbstyle
=EmptyCaptionBarStyle
,
500 rightIndent
=FPB_BMP_RIGHTSPACE
,
501 iconWidth
=16, iconHeight
=16, collapsed
=False):
502 """ Default Class Constructor."""
504 wx
.Window
.__init
__(self
, parent
, wx
.ID_ANY
, pos
=wx
.DefaultPosition
,
505 size
=(20,20), style
=wx
.NO_BORDER
)
507 self
._controlCreated
= False
508 self
._collapsed
= collapsed
509 self
.ApplyCaptionStyle(cbstyle
, True)
511 if foldIcons
is None:
512 foldIcons
= wx
.ImageList(16, 16)
514 bmp
= GetExpandedIconBitmap()
516 bmp
= GetCollapsedIconBitmap()
521 assert foldIcons
.GetImageCount() > 1
522 iconWidth
, iconHeight
= foldIcons
.GetSize(0)
524 self
._caption
= caption
525 self
._foldIcons
= foldIcons
526 self
._style
= cbstyle
527 self
._rightIndent
= rightIndent
528 self
._iconWidth
= iconWidth
529 self
._iconHeight
= iconHeight
530 self
._oldSize
= wx
.Size(20,20)
532 self
._controlCreated
= True
534 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
535 self
.Bind(wx
.EVT_SIZE
, self
.OnSize
)
536 self
.Bind(wx
.EVT_MOUSE_EVENTS
, self
.OnMouseEvent
)
537 self
.Bind(wx
.EVT_CHAR
, self
.OnChar
)
540 def ApplyCaptionStyle(self
, cbstyle
=EmptyCaptionBarStyle
, applyDefault
=True):
541 """ Applies the style defined in cbstyle to the CaptionBar."""
547 # get first colour from style or make it default
548 if not newstyle
.FirstColourUsed():
549 newstyle
.SetFirstColour(wx
.WHITE
)
551 # get second colour from style or make it default
552 if not newstyle
.SecondColourUsed():
553 # make the second colour slightly darker then the background
554 color
= self
.GetParent().GetBackgroundColour()
555 r
, g
, b
= int(color
.Red()), int(color
.Green()), int(color
.Blue())
556 color
= ((r
>> 1) + 20, (g
>> 1) + 20, (b
>> 1) + 20)
557 newstyle
.SetSecondColour(wx
.Colour(color
[0], color
[1], color
[2]))
560 if not newstyle
.CaptionColourUsed():
561 newstyle
.SetCaptionColour(wx
.BLACK
)
564 if not newstyle
.CaptionFontUsed():
565 newstyle
.SetCaptionFont(self
.GetParent().GetFont())
567 # apply caption style
568 if not newstyle
.CaptionStyleUsed():
569 newstyle
.SetCaptionStyle(CAPTIONBAR_GRADIENT_V
)
571 self
._style
= newstyle
574 def SetCaptionStyle(self
, cbstyle
=EmptyCaptionBarStyle
, applyDefault
=True):
576 Sets CaptionBar styles with CapionBarStyle class.
578 All styles that are actually set, are applied. If you set
579 applyDefault to True, all other (not defined) styles will be
580 set to default. If it is False, the styles which are not set
581 in the CaptionBarStyle will be ignored.
583 self
.ApplyCaptionStyle(cbstyle
, applyDefault
)
587 def GetCaptionStyle(self
):
589 Returns the current style of the captionbar in a
590 `CaptionBarStyle` class.
592 This can be used to change and set back the changes.
597 def IsCollapsed(self
):
599 Returns wether the status of the bar is expanded or collapsed.
601 return self
._collapsed
604 def SetRightIndent(self
, pixels
):
606 Sets the amount of pixels on the right from which the bitmap
609 If this is 0, it will be drawn all the way to the right,
610 default is equal to FPB_BMP_RIGHTSPACE. Assign this before
611 assigning an image list to prevent a redraw.
614 self
._rightIndent
= pixels
621 This sets the internal state / representation to collapsed.
623 This does not trigger a `CaptionBarEvent` to be sent to the
626 self
._collapsed
= True
627 self
.RedrawIconBitmap()
632 This sets the internal state / representation to expanded.
634 This does not trigger a `CaptionBarEvent` to be sent to the
637 self
._collapsed
= False
638 self
.RedrawIconBitmap()
641 def SetBoldFont(self
):
642 """ Sets the CaptionBarFont weight to BOLD."""
644 self
.GetFont().SetWeight(wx
.BOLD
)
647 def SetNormalFont(self
):
648 """ Sets the CaptionBarFont weight to NORMAL."""
650 self
.GetFont().SetWeight(wx
.NORMAL
)
653 def IsVertical(self
):
655 Returns wether the CaptionBar Has Default Orientation Or Not.
660 fld
= self
.GetParent().GetGrandParent()
661 if isinstance(fld
, FoldPanelBar
):
662 return self
.GetParent().GetGrandParent().IsVertical()
664 raise "ERROR: Wrong Parent " + repr(fld
)
667 def OnPaint(self
, event
):
668 """ The paint event for flat or gradient fill. """
670 if not self
._controlCreated
:
674 dc
= wx
.PaintDC(self
)
675 wndRect
= self
.GetRect()
676 vertical
= self
.IsVertical()
678 # TODO: Maybe first a memory DC should draw all, and then paint it on
679 # the caption. This way a flickering arrow during resize is not visible
681 self
.FillCaptionBackground(dc
)
682 dc
.SetFont(self
._style
.GetCaptionFont())
683 dc
.SetTextForeground(self
._style
.GetCaptionColour())
686 dc
.DrawText(self
._caption
, 4, FPB_EXTRA_Y
/2)
688 dc
.DrawRotatedText(self
._caption
, FPB_EXTRA_Y
/2,
689 wndRect
.GetBottom() - 4, 90)
691 # draw small icon, either collapsed or expanded
692 # based on the state of the bar. If we have any bmp's
696 index
= self
._collapsed
699 drw
= wndRect
.GetRight() - self
._iconWidth
- self
._rightIndent
700 self
._foldIcons
.Draw(index
, dc
, drw
,
701 (wndRect
.GetHeight() - self
._iconHeight
)/2,
702 wx
.IMAGELIST_DRAW_TRANSPARENT
)
704 self
._foldIcons
.Draw(index
, dc
,
705 (wndRect
.GetWidth() - self
._iconWidth
)/2,
706 self
._rightIndent
, wx
.IMAGELIST_DRAW_TRANSPARENT
)
711 def FillCaptionBackground(self
, dc
):
713 Fills the background of the caption with either a gradient or
717 style
= self
._style
.GetCaptionStyle()
719 if style
== CAPTIONBAR_GRADIENT_V
:
720 if self
.IsVertical():
721 self
.DrawVerticalGradient(dc
, self
.GetRect())
723 self
.DrawHorizontalGradient(dc
, self
.GetRect())
725 elif style
== CAPTIONBAR_GRADIENT_H
:
726 if self
.IsVertical():
727 self
.DrawHorizontalGradient(dc
, self
.GetRect())
729 self
.DrawVerticalGradient(dc
, self
.GetRect())
731 elif style
== CAPTIONBAR_SINGLE
:
732 self
.DrawSingleColour(dc
, self
.GetRect())
733 elif style
== CAPTIONBAR_RECTANGLE
or style
== CAPTIONBAR_FILLED_RECTANGLE
:
734 self
.DrawSingleRectangle(dc
, self
.GetRect())
736 raise "STYLE Error: Undefined Style Selected: " + repr(style
)
739 def OnMouseEvent(self
, event
):
741 Catches the mouse click-double click.
743 If clicked on the arrow (single) or double on the caption we change state
744 and an event must be fired to let this panel collapse or expand.
748 vertical
= self
.IsVertical()
750 if event
.LeftDown() and self
._foldIcons
:
752 pt
= event
.GetPosition()
753 rect
= self
.GetRect()
755 drw
= (rect
.GetWidth() - self
._iconWidth
- self
._rightIndent
)
756 if vertical
and pt
.x
> drw
or not vertical
and \
757 pt
.y
< (self
._iconHeight
+ self
._rightIndent
):
760 elif event
.LeftDClick():
761 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
764 elif event
.Entering() and self
._foldIcons
:
765 pt
= event
.GetPosition()
766 rect
= self
.GetRect()
768 drw
= (rect
.GetWidth() - self
._iconWidth
- self
._rightIndent
)
769 if vertical
and pt
.x
> drw
or not vertical
and \
770 pt
.y
< (self
._iconHeight
+ self
._rightIndent
):
771 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
773 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
775 elif event
.Leaving():
776 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
779 pt
= event
.GetPosition()
780 rect
= self
.GetRect()
782 drw
= (rect
.GetWidth() - self
._iconWidth
- self
._rightIndent
)
783 if vertical
and pt
.x
> drw
or not vertical
and \
784 pt
.y
< (self
._iconHeight
+ self
._rightIndent
):
785 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_HAND
))
787 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_ARROW
))
789 # send the collapse, expand event to the parent
792 event
= CaptionBarEvent(wxEVT_CAPTIONBAR
)
793 event
.SetId(self
.GetId())
794 event
.SetEventObject(self
)
796 self
.GetEventHandler().ProcessEvent(event
)
799 def OnChar(self
, event
):
800 """ Unused Methods. Any Ideas?!?"""
801 # TODO: Anything here?
805 def DoGetBestSize(self
):
807 Returns the best size for this panel, based upon the font
808 assigned to this window, and the caption string
811 if self
.IsVertical():
812 x
, y
= self
.GetTextExtent(self
._caption
)
814 y
, x
= self
.GetTextExtent(self
._caption
)
816 if x
< self
._iconWidth
:
819 if y
< self
._iconHeight
:
822 # TODO: The extra FPB_EXTRA_X constants should be adjustable as well
824 return wx
.Size(x
+ FPB_EXTRA_X
, y
+ FPB_EXTRA_Y
)
827 def DrawVerticalGradient(self
, dc
, rect
):
828 """ Gradient fill from colour 1 to colour 2 with top to bottom. """
830 if rect
.height
< 1 or rect
.width
< 1:
833 dc
.SetPen(wx
.TRANSPARENT_PEN
)
835 # calculate gradient coefficients
836 col2
= self
._style
.GetSecondColour()
837 col1
= self
._style
.GetFirstColour()
839 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
840 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
842 flrect
= float(rect
.height
)
844 rstep
= float((r2
- r1
)) / flrect
845 gstep
= float((g2
- g1
)) / flrect
846 bstep
= float((b2
- b1
)) / flrect
850 for y
in range(rect
.y
, rect
.y
+ rect
.height
):
851 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
853 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
854 dc
.DrawRectangle(rect
.x
, rect
.y
+ (y
- rect
.y
), rect
.width
, rect
.height
)
860 def DrawHorizontalGradient(self
, dc
, rect
):
861 """ Gradient fill from colour 1 to colour 2 with left to right. """
863 if rect
.height
< 1 or rect
.width
< 1:
866 dc
.SetPen(wx
.TRANSPARENT_PEN
)
868 # calculate gradient coefficients
869 col2
= self
._style
.GetSecondColour()
870 col1
= self
._style
.GetFirstColour()
872 r1
, g1
, b1
= int(col1
.Red()), int(col1
.Green()), int(col1
.Blue())
873 r2
, g2
, b2
= int(col2
.Red()), int(col2
.Green()), int(col2
.Blue())
875 flrect
= float(rect
.width
)
877 rstep
= float((r2
- r1
)) / flrect
878 gstep
= float((g2
- g1
)) / flrect
879 bstep
= float((b2
- b1
)) / flrect
883 for x
in range(rect
.x
, rect
.x
+ rect
.width
):
884 currCol
= (r1
+ rf
, g1
+ gf
, b1
+ bf
)
886 dc
.SetBrush(wx
.Brush(currCol
, wx
.SOLID
))
887 dc
.DrawRectangle(rect
.x
+ (x
- rect
.x
), rect
.y
, 1, rect
.height
)
893 def DrawSingleColour(self
, dc
, rect
):
894 """ Single colour fill. This is the most easy one to find. """
896 if rect
.height
< 1 or rect
.width
< 1:
899 dc
.SetPen(wx
.TRANSPARENT_PEN
)
901 # draw simple rectangle
902 dc
.SetBrush(wx
.Brush(self
._style
.GetFirstColour(), wx
.SOLID
))
903 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, rect
.height
)
906 def DrawSingleRectangle(self
, dc
, rect
):
907 """ Single rectangle. This is the most easy one to find. """
909 if rect
.height
< 2 or rect
.width
< 1:
912 # single frame, set up internal fill colour
914 if self
._style
.GetCaptionStyle() == CAPTIONBAR_RECTANGLE
:
915 color
= self
.GetParent().GetBackgroundColour()
916 br
= wx
.Brush(color
, wx
.SOLID
)
918 color
= self
._style
.GetFirstColour()
919 br
= wx
.Brush(color
, wx
.SOLID
)
921 # setup the pen frame
923 pen
= wx
.Pen(self
._style
.GetSecondColour())
926 dc
.DrawRectangle(rect
.x
, rect
.y
, rect
.width
, rect
.height
- 1)
928 bgpen
= wx
.Pen(self
.GetParent().GetBackgroundColour())
930 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
- 1, rect
.x
+ rect
.width
,
931 rect
.y
+ rect
.height
- 1)
934 def OnSize(self
, event
):
935 """ Handles the size events for the CaptionBar."""
937 if not self
._controlCreated
:
941 size
= event
.GetSize()
945 # What I am doing here is simply invalidating the part of the window
946 # exposed. So when I make a rect with as width the newly exposed part,
947 # and the x,y of the old window size origin, I don't need a bitmap
948 # calculation in it, or do I ? The bitmap needs redrawing anyway.
949 # Leave it like this until I figured it out.
951 # set rect to redraw as old bitmap area which is entitled to redraw
953 rect
= wx
.Rect(size
.GetWidth() - self
._iconWidth
- self
._rightIndent
, 0,
954 self
._iconWidth
+ self
._rightIndent
,
955 self
._iconWidth
+ self
._rightIndent
)
957 # adjust rectangle when more is slided so we need to redraw all
958 # the old stuff but not all (ugly flickering)
960 diffX
= size
.GetWidth() - self
._oldSize
.GetWidth()
964 # adjust the rect with all the crap to redraw
966 rect
.SetWidth(rect
.GetWidth() + diffX
+ 10)
967 rect
.SetX(rect
.GetX() - diffX
- 10)
969 self
.RefreshRect(rect
)
973 rect
= self
.GetRect()
974 self
.RefreshRect(rect
)
979 def RedrawIconBitmap(self
):
980 """ Redraws the icons (if they exists). """
984 # invalidate the bitmap area and force a redraw
986 rect
= self
.GetRect()
988 rect
.SetX(rect
.GetWidth() - self
._iconWidth
- self
._rightIndent
)
989 rect
.SetWidth(self
._iconWidth
+ self
._rightIndent
)
990 self
.RefreshRect(rect
)
993 # ---------------------------------------------------------------------------------- #
995 # ---------------------------------------------------------------------------------- #
997 class FoldPanelBar(wx
.Panel
):
999 The FoldPanelBar is a class which can maintain a list of
1000 collapsable panels. Once a panel is collapsed, only it's caption
1001 bar is visible to the user. This will provide more space for the
1002 other panels, or allow the user to close panels which are not used
1003 often to get the most out of the work area.
1005 This control is easy to use. Simply create it as a child for a
1006 panel or sash window, and populate panels with
1007 `AddFoldPanel`. Then use the AdddFoldPanelWindow` to add
1008 `wx.Window` derived controls to the current fold panel. Use
1009 `AddFoldPanelSeparator` to put separators between the groups of
1010 controls that need a visual separator to group them
1011 together. After all is constructed, the user can fold the panels
1012 by doubleclicking on the bar or single click on the arrow, which
1013 will indicate the collapsed or expanded state.
1015 # Define Empty CaptionBar Style
1016 EmptyCaptionBarStyle
= CaptionBarStyle()
1018 def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
1019 style
=FPB_DEFAULT_STYLE
, extraStyle
=FPB_DEFAULT_EXTRASTYLE
):
1020 """ Default Class Constructor. """
1022 self
._controlCreated
= False
1023 self
._extraStyle
= extraStyle
1025 # make sure there is any orientation
1026 if style
& FPB_HORIZONTAL
!= FPB_HORIZONTAL
:
1027 style
= style | FPB_VERTICAL
1029 if style
& FPB_HORIZONTAL
== 4:
1030 self
._isVertical
= False
1032 self
._isVertical
= True
1035 # create the panel (duh!). This causes a size event, which we are going
1036 # to skip when we are not initialised
1038 wx
.Panel
.__init
__(self
, parent
, id, pos
, size
, style
)
1040 # the fold panel area
1042 self
._foldPanel
= wx
.Panel(self
, wx
.ID_ANY
, pos
, size
,
1043 wx
.NO_BORDER | wx
.TAB_TRAVERSAL
)
1045 self
._controlCreated
= True
1048 self
.Bind(EVT_CAPTIONBAR
, self
.OnPressCaption
)
1049 self
.Bind(wx
.EVT_SIZE
, self
.OnSizePanel
)
1052 def AddFoldPanel(self
, caption
="", collapsed
=False, foldIcons
=None,
1053 cbstyle
=EmptyCaptionBarStyle
):
1055 Adds a fold panel to the list of panels.
1057 If the flag collapsed is set to True, the panel is collapsed
1058 initially. The FoldPanel item which is returned, can be used
1059 as a reference to perform actions upon the fold panel like
1060 collapsing it, expanding it, or deleting it from the list.
1062 Use this foldpanel to add windows to it. Please consult
1063 `AddFoldPanelWindow` and `AddFoldPanelSeparator` to know how
1064 to add items derived from `wx.Window` to the panels.
1067 # create a fold panel item, which is first only the caption.
1068 # the user can now add a panel area which will be folded in
1071 if foldIcons
is None:
1072 foldIcons
= wx
.ImageList(16, 16)
1074 bmp
= GetExpandedIconBitmap()
1076 bmp
= GetCollapsedIconBitmap()
1079 item
= FoldPanelItem(self
._foldPanel
, -1, caption
=caption
,
1080 foldIcons
=foldIcons
,
1081 collapsed
=collapsed
, cbstyle
=cbstyle
)
1084 if len(self
._panels
) > 0:
1085 pos
= self
._panels
[-1].GetItemPos() + self
._panels
[-1].GetPanelLength()
1087 item
.Reposition(pos
)
1088 self
._panels
.append(item
)
1093 def AddFoldPanelWindow(self
, panel
, window
, flags
=FPB_ALIGN_WIDTH
,
1094 Spacing
=FPB_DEFAULT_SPACING
,
1095 leftSpacing
=FPB_DEFAULT_LEFTLINESPACING
,
1096 rightSpacing
=FPB_DEFAULT_RIGHTLINESPACING
):
1098 Adds a `wx.Window` derived instance to the referenced
1101 IMPORTANT: Make the window be a child of the FoldPanel. See
1102 example that follows. The flags to be used are:
1104 * FPB_ALIGN_WIDTH: Which means the wxWindow to be added
1105 will be aligned to fit the width of the FoldPanel when
1106 it is resized. Very handy for sizer items, buttons and
1109 * FPB_ALIGN_LEFT: Aligns left instead of fitting the
1110 width of the child window to be added. Use either this
1111 one or FPB_ALIGN_WIDTH.
1113 The wx.Window to be added can be slightly indented from left
1114 and right so it is more visibly placed in the FoldPanel. Use
1115 Spacing > 0 to give the control an y offset from the previous
1116 wx.Window added, use leftSpacing to give it a slight indent
1117 from the left, and rightSpacing also reserves a little space
1118 on the right so the wxWindow can be properly placed in the
1121 The following example adds a FoldPanel to the FoldPanelBar and
1122 adds two wx.Window derived controls to the FoldPanel::
1124 # create the FoldPanelBar
1125 >>> m_pnl = FoldPanelBar(self, wx.ID_ANY, wx.DefaultPosition,
1126 wx.DefaultSize, FPB_DEFAULT_STYLE,
1127 FPB_COLLAPSE_TO_BOTTOM)
1129 # add a foldpanel to the control. "Test me" is the caption and it is
1130 # initially not collapsed.
1131 >>> item = m_pnl.AddFoldPanel("Test me", False)
1133 # now add a button to the fold panel. Mind that the button should be
1134 # made child of the FoldPanel and not of the main form.
1135 >>> m_pnl.AddFoldPanelWindow(item, wx.Button(item, ID_COLLAPSEME,
1138 # add a separator between the two controls. This is purely a visual
1139 # line that can have a certain color and also the indents and width
1140 # aligning like a control.
1141 >>> m_pnl.AddFoldPanelSeparator(item)
1143 # now add a text ctrl. Also very easy. Align this on width so that
1144 # when the control gets wider the text control also sizes along.
1145 >>> m_pnl.AddFoldPanelWindow(item, wx.TextCtrl(item, wx.ID_ANY, "Comment"),
1146 FPB_ALIGN_WIDTH, FPB_DEFAULT_SPACING, 20)
1151 item
= self
._panels
.index(panel
)
1153 raise "ERROR: Invalid Panel Passed To AddFoldPanelWindow: " + repr(panel
)
1155 panel
.AddWindow(window
, flags
, Spacing
, leftSpacing
, rightSpacing
)
1157 # TODO: Take old and new height, and if difference, reposition all the lower
1158 # panels this is because the user can add new wxWindow controls somewhere in
1159 # between when other panels are already present.
1164 def AddFoldPanelSeparator(self
, panel
, colour
=wx
.BLACK
,
1165 Spacing
=FPB_DEFAULT_SPACING
,
1166 leftSpacing
=FPB_DEFAULT_LEFTLINESPACING
,
1167 rightSpacing
=FPB_DEFAULT_RIGHTLINESPACING
):
1169 Adds a separator line to the current FoldPanel.
1171 The seperator is a simple line which is drawn and is no real
1172 component. It can be used to separate groups of controls
1173 which belong to each other. The colour is adjustable, and it
1174 takes the same Spacing, leftSpacing and rightSpacing as
1175 `AddFoldPanelWindow`.
1179 item
= self
._panels
.index(panel
)
1181 raise "ERROR: Invalid Panel Passed To AddFoldPanelSeparator: " + repr(panel
)
1183 panel
.AddSeparator(colour
, Spacing
, leftSpacing
, rightSpacing
)
1187 def OnSizePanel(self
, event
):
1188 """ Handles the EVT_SIZE event for the FoldPanelBar. """
1190 # skip all stuff when we are not initialised yet
1192 if not self
._controlCreated
:
1196 foldrect
= self
.GetRect()
1198 # fold panel itself. If too little space,
1204 self
._foldPanel
.SetSize(foldrect
[2:])
1206 if self
._extraStyle
& FPB_COLLAPSE_TO_BOTTOM
or self
._extraStyle
& FPB_EXCLUSIVE_FOLD
:
1207 rect
= self
.RepositionCollapsedToBottom()
1208 vertical
= self
.IsVertical()
1209 if vertical
and rect
.GetHeight() > 0 or not vertical
and rect
.GetWidth() > 0:
1210 self
.RefreshRect(rect
)
1212 # TODO: A smart way to check wether the old - new width of the
1213 # panel changed, if so no need to resize the fold panel items
1215 self
.RedisplayFoldPanelItems()
1218 def OnPressCaption(self
, event
):
1219 """ Handles the EVT_CAPTIONBAR event in the FoldPanelBar. """
1221 # act upon the folding or expanding status of the bar
1222 # to expand or collapse the panel(s)
1224 if event
.GetFoldStatus():
1225 self
.Collapse(event
.GetTag())
1227 self
.Expand(event
.GetTag())
1232 def RefreshPanelsFrom(self
, item
):
1233 """ Refreshes all the panels from given index down to last one. """
1236 i
= self
._panels
.index(item
)
1238 raise "ERROR: Invalid Panel Passed To RefreshPanelsFrom: " + repr(item
)
1242 # if collapse to bottom is on, the panels that are not expanded
1243 # should be drawn at the bottom. All panels that are expanded
1244 # are drawn on top. The last expanded panel gets all the extra space
1246 if self
._extraStyle
& FPB_COLLAPSE_TO_BOTTOM
or self
._extraStyle
& FPB_EXCLUSIVE_FOLD
:
1250 for panels
in self
._panels
:
1252 if panels
.IsExpanded():
1253 offset
= offset
+ panels
.Reposition(offset
)
1255 # put all non collapsed panels at the bottom where there is space,
1256 # else put them right behind the expanded ones
1258 self
.RepositionCollapsedToBottom()
1262 pos
= self
._panels
[i
].GetItemPos() + self
._panels
[i
].GetPanelLength()
1263 for j
in range(i
+1, len(self
._panels
)):
1264 pos
= pos
+ self
._panels
[j
].Reposition(pos
)
1269 def RedisplayFoldPanelItems(self
):
1270 """ Resizes the fold panels so they match the width. """
1271 # resize them all. No need to reposition
1272 for panels
in self
._panels
:
1273 panels
.ResizePanel()
1277 def RepositionCollapsedToBottom(self
):
1279 Repositions all the collapsed panels to the bottom.
1281 When it is not possible to align them to the bottom, stick
1282 them behind the visible panels. The Rect holds the slack area
1283 left between last repositioned panel and the bottom
1284 panels. This needs to get a refresh.
1287 value
= wx
.Rect(0,0,0,0)
1288 vertical
= self
.IsVertical()
1290 # determine wether the number of panels left
1291 # times the size of their captions is enough
1292 # to be placed in the left over space
1296 collapsed
, expanded
, values
= self
.GetPanelsLength(collapsed
, expanded
)
1298 # if no room stick them behind the normal ones, else
1301 if (vertical
and [self
.GetSize().GetHeight()] or \
1302 [self
.GetSize().GetWidth()])[0] - expanded
- collapsed
< 0:
1306 # value is the region which is left unpainted
1307 # I will send it back as 'slack' so it does not need to
1310 value
.SetHeight(self
.GetSize().GetHeight())
1311 value
.SetWidth(self
.GetSize().GetWidth())
1314 value
.SetY(expanded
)
1315 value
.SetHeight(value
.GetHeight() - expanded
)
1317 value
.SetX(expanded
)
1318 value
.SetWidth(value
.GetWidth() - expanded
)
1320 offset
= (vertical
and [self
.GetSize().GetHeight()] or \
1321 [self
.GetSize().GetWidth()])[0] - collapsed
1326 for panels
in self
._panels
:
1327 if not panels
.IsExpanded():
1328 offset
= offset
+ panels
.Reposition(offset
)
1333 def GetPanelsLength(self
, collapsed
, expanded
):
1335 Returns the length of the panels that are expanded and
1338 This is useful to determine quickly what size is used to
1339 display, and what is left at the bottom (right) to align the
1345 # assumed here that all the panels that are expanded
1346 # are positioned after each other from 0,0 to end.
1348 for j
in range(0, len(self
._panels
)):
1349 offset
= self
._panels
[j
].GetPanelLength()
1350 value
= value
+ offset
1351 if self
._panels
[j
].IsExpanded():
1352 expanded
= expanded
+ offset
1354 collapsed
= collapsed
+ offset
1356 return collapsed
, expanded
, value
1359 def Collapse(self
, foldpanel
):
1361 Collapses the given FoldPanel reference, and updates the
1364 In the FPB_COLLAPSE_TO_BOTTOM style, all collapsed captions
1365 are put at the bottom of the control. In the normal mode, they
1366 stay where they are.
1370 item
= self
._panels
.index(foldpanel
)
1372 raise "ERROR: Invalid Panel Passed To Collapse: " + repr(foldpanel
)
1374 foldpanel
.Collapse()
1375 self
.RefreshPanelsFrom(foldpanel
)
1378 def Expand(self
, foldpanel
):
1380 Expands the given FoldPanel reference, and updates the
1383 In the FPB_COLLAPSE_TO_BOTTOM style, they will be removed from
1384 the bottom and the order where the panel originally was placed
1390 if self
._extraStyle
& FPB_SINGLE_FOLD
or self
._extraStyle
& FPB_EXCLUSIVE_FOLD
:
1392 for panel
in self
._panels
:
1398 if self
._extraStyle
& FPB_EXCLUSIVE_FOLD
:
1399 self
.RepositionCollapsedToBottom()
1400 self
.RefreshPanelsFrom(self
._panels
[0])
1402 self
.RefreshPanelsFrom(foldpanel
)
1405 def ApplyCaptionStyle(self
, foldpanel
, cbstyle
):
1407 Sets the style of the caption bar (`CaptionBar`) of the
1410 The changes are applied immediately. All styles not set in the
1411 CaptionBarStyle class are not applied. Use the CaptionBar
1412 reference to indicate what captionbar you want to apply the
1413 style to. To apply one style to all CaptionBar items, use
1414 `ApplyCaptionStyleAll`
1416 foldpanel
.ApplyCaptionStyle(cbstyle
)
1419 def ApplyCaptionStyleAll(self
, cbstyle
):
1421 Sets the style of all the caption bars of the FoldPanel.
1423 The changes are applied immediately.
1425 for panels
in self
._panels
:
1426 self
.ApplyCaptionStyle(panels
, cbstyle
)
1429 def GetCaptionStyle(self
, foldpanel
):
1431 Returns the currently used caption style for the FoldPanel.
1433 It is returned as a CaptionBarStyle class. After modifying it,
1434 it can be set again.
1436 return foldpanel
.GetCaptionStyle()
1439 def IsVertical(self
):
1441 Returns whether the CaptionBar has default orientation or not.
1443 Default is vertical.
1445 return self
._isVertical
1448 def GetFoldPanel(self
, item
):
1450 Returns the panel associated with the index "item".
1452 See the example at the bottom of the module, especially the events
1453 for the "Collapse Me" and "Expand Me" buttons.
1456 ind
= self
._panels
[item
]
1457 return self
._panels
[item
]
1459 raise "ERROR: List Index Out Of Range Or Bad Item Passed: " + repr(item
) + \
1460 ". Item Should Be An Integer Between " + repr(0) + " And " + \
1461 repr(len(self
._panels
))
1465 """ Returns the number of panels in the FoldPanelBar. """
1468 return len(self
._panels
)
1470 raise "ERROR: No Panels Have Been Added To FoldPanelBar"
1474 # --------------------------------------------------------------------------------- #
1475 # class FoldPanelItem
1476 # --------------------------------------------------------------------------------- #
1478 class FoldPanelItem(wx
.Panel
):
1480 This class is a child sibling of the `FoldPanelBar` class. It will
1481 contain a `CaptionBar` class for receiving of events, and a the
1482 rest of the area can be populated by a `wx.Panel` derived class.
1484 # Define Empty CaptionBar Style
1485 EmptyCaptionBarStyle
= CaptionBarStyle()
1487 def __init__(self
, parent
, id=wx
.ID_ANY
, caption
="", foldIcons
=None,
1488 collapsed
=False, cbstyle
=EmptyCaptionBarStyle
):
1489 """ Default Class Constructor. """
1491 wx
.Panel
.__init
__(self
, parent
, id, style
=wx
.CLIP_CHILDREN
)
1492 self
._controlCreated
= False
1495 self
._LastInsertPos
= 0
1497 self
._userSized
= False
1499 if foldIcons
is None:
1500 foldIcons
= wx
.ImageList(16, 16)
1502 bmp
= GetExpandedIconBitmap()
1504 bmp
= GetCollapsedIconBitmap()
1507 self
._foldIcons
= foldIcons
1509 # create the caption bar, in collapsed or expanded state
1511 self
._captionBar
= CaptionBar(self
, wx
.ID_ANY
, wx
.Point(0,0),
1512 size
=wx
.DefaultSize
, caption
=caption
,
1513 foldIcons
=foldIcons
, cbstyle
=cbstyle
)
1516 self
._captionBar
.Collapse()
1518 self
._controlCreated
= True
1520 # make initial size for component, if collapsed, the
1521 # size is determined on the panel height and won't change
1523 size
= self
._captionBar
.GetSize()
1525 self
._PanelSize
= (self
.IsVertical() and [size
.GetHeight()] or \
1526 [size
.GetWidth()])[0]
1528 self
._LastInsertPos
= self
._PanelSize
1531 self
.Bind(EVT_CAPTIONBAR
, self
.OnPressCaption
)
1532 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1535 def AddWindow(self
, window
, flags
=FPB_ALIGN_WIDTH
, Spacing
=FPB_DEFAULT_SPACING
,
1536 leftSpacing
=FPB_DEFAULT_LEFTLINESPACING
,
1537 rightSpacing
=FPB_DEFAULT_RIGHTLINESPACING
):
1539 Adds a window item to the list of items on this panel.
1541 The flags are FPB_ALIGN_LEFT for a non sizing window element,
1542 and FPB_ALIGN_WIDTH for a width aligned item. The Spacing
1543 parameter reserves a number of pixels before the window
1544 element, and leftSpacing is an indent. rightSpacing is only
1545 relevant when the style FPB_ALIGN_WIDTH is chosen.
1548 wi
= FoldWindowItem(self
, window
, Type
="WINDOW", flags
=flags
, Spacing
=Spacing
,
1549 leftSpacing
=leftSpacing
, rightSpacing
=rightSpacing
)
1551 self
._items
.append(wi
)
1553 vertical
= self
.IsVertical()
1555 self
._Spacing
= Spacing
1556 self
._leftSpacing
= leftSpacing
1557 self
._rightSpacing
= rightSpacing
1559 xpos
= (vertical
and [leftSpacing
] or [self
._LastInsertPos
+ Spacing
])[0]
1560 ypos
= (vertical
and [self
._LastInsertPos
+ Spacing
] or [leftSpacing
])[0]
1562 window
.SetDimensions(xpos
, ypos
, -1, -1, wx
.SIZE_USE_EXISTING
)
1564 self
._LastInsertPos
= self
._LastInsertPos
+ wi
.GetWindowLength(vertical
)
1568 def AddSeparator(self
, colour
=wx
.BLACK
, Spacing
=FPB_DEFAULT_SPACING
,
1569 leftSpacing
=FPB_DEFAULT_LEFTSPACING
,
1570 rightSpacing
=FPB_DEFAULT_RIGHTSPACING
):
1572 Adds a separator item to the list of items on this panel. """
1574 wi
= FoldWindowItem(self
, window
=None, Type
="SEPARATOR",
1575 flags
=FPB_ALIGN_WIDTH
, y
=self
._LastInsertPos
,
1576 colour
=colour
, Spacing
=Spacing
, leftSpacing
=leftSpacing
,
1577 rightSpacing
=rightSpacing
)
1579 self
._items
.append(wi
)
1580 self
._LastInsertPos
= self
._LastInsertPos
+ \
1581 wi
.GetWindowLength(self
.IsVertical())
1586 def Reposition(self
, pos
):
1588 Repositions this FoldPanelBar and reports the length occupied
1589 for the next FoldPanelBar in the list.
1591 # NOTE: Call Resize before Reposition when an item is added, because the new
1592 # size needed will be calculated by Resize. Of course the relative position
1593 # of the controls have to be correct in respect to the caption bar
1597 vertical
= self
.IsVertical()
1598 xpos
= (vertical
and [-1] or [pos
])[0]
1599 ypos
= (vertical
and [pos
] or [-1])[0]
1601 self
.SetDimensions(xpos
, ypos
, -1, -1, wx
.SIZE_USE_EXISTING
)
1606 return self
.GetPanelLength()
1609 def OnPressCaption(self
, event
):
1610 """ Handles the EVT_CAPTIONBAR event in the FoldPanelItem. """
1612 # tell the upper container we are responsible
1613 # for this event, so it can fold the panel item
1620 def ResizePanel(self
):
1621 """ Resizes the panel. """
1623 # prevent unnecessary updates by blocking repaints for a sec
1627 vertical
= self
.IsVertical()
1628 # force this panel to take the width of the parent panel and the y of the
1629 # user or calculated width (which will be recalculated by the contents here)
1632 if self
._captionBar
.IsCollapsed():
1633 size
= self
._captionBar
.GetSize()
1634 self
._PanelSize
= (vertical
and [size
.GetHeight()] or [size
.GetWidth()])[0]
1636 size
= self
.GetBestSize()
1637 self
._PanelSize
= (vertical
and [size
.GetHeight()] or [size
.GetWidth()])[0]
1641 size
.SetHeight(self
._UserSize
)
1643 size
.SetWidth(self
._UserSize
)
1645 pnlsize
= self
.GetParent().GetSize()
1648 size
.SetWidth(pnlsize
.GetWidth())
1650 size
.SetHeight(pnlsize
.GetHeight())
1652 # resize caption bar
1653 xsize
= (vertical
and [size
.GetWidth()] or [-1])[0]
1654 ysize
= (vertical
and [-1] or [size
.GetHeight()])[0]
1656 self
._captionBar
.SetSize((xsize
, ysize
))
1661 # go by all the controls and call Layout
1663 for items
in self
._items
:
1664 items
.ResizeItem((vertical
and [size
.GetWidth()] or \
1665 [size
.GetHeight()])[0], vertical
)
1670 def OnPaint(self
, event
):
1671 """ Handles the EVT_PAINT event in the FoldPanelItem. """
1673 # draw all the items that are lines
1675 dc
= wx
.PaintDC(self
)
1676 vertical
= self
.IsVertical()
1678 for item
in self
._items
:
1680 if item
.GetType() == "SEPARATOR":
1681 pen
= wx
.Pen(item
.GetLineColour(), 1, wx
.SOLID
)
1683 a
= item
.GetLeftSpacing()
1684 b
= item
.GetLineY() + item
.GetSpacing()
1685 c
= item
.GetLineLength()
1689 dc
.DrawLine(a
, b
, d
, b
)
1691 dc
.DrawLine(b
, a
, b
, d
)
1696 def IsVertical(self
):
1698 Returns wether the CaptionBar Has Default Orientation Or Not.
1700 Default is vertical.
1703 # grandparent of FoldPanelItem is FoldPanelBar
1704 # default is vertical
1706 if isinstance(self
.GetGrandParent(), FoldPanelBar
):
1707 return self
.GetGrandParent().IsVertical()
1709 raise "ERROR: Wrong Parent " + repr(self
.GetGrandParent())
1712 def IsExpanded(self
):
1714 Returns expanded or collapsed status. If the panel is
1715 expanded, True is returned.
1718 return not self
._captionBar
.IsCollapsed()
1721 def GetItemPos(self
):
1722 """ Returns item's position. """
1724 return self
._itemPos
1728 # this should not be called by the user, because it doesn't trigger the
1729 # parent to tell it that we are collapsed or expanded, it only changes
1732 self
._captionBar
.Collapse()
1737 # this should not be called by the user, because it doesn't trigger the
1738 # parent to tell it that we are collapsed or expanded, it only changes
1741 self
._captionBar
.Expand()
1745 def GetPanelLength(self
):
1746 """ Returns size of panel. """
1748 if self
._captionBar
.IsCollapsed():
1749 return self
.GetCaptionLength()
1750 elif self
._userSized
:
1751 return self
._UserSize
1753 return self
._PanelSize
1756 def GetCaptionLength(self
):
1758 Returns height of caption only. This is for folding
1759 calculation purposes.
1762 size
= self
._captionBar
.GetSize()
1763 return (self
.IsVertical() and [size
.GetHeight()] or [size
.GetWidth()])[0]
1766 def ApplyCaptionStyle(self
, cbstyle
):
1767 """ Applies the style defined in cbstyle to the CaptionBar."""
1769 self
._captionBar
.SetCaptionStyle(cbstyle
)
1772 def GetCaptionStyle(self
):
1774 Returns the current style of the captionbar in a
1775 CaptionBarStyle class.
1777 This can be used to change and set back the changes.
1780 return self
._captionBar
.GetCaptionStyle()
1783 # ----------------------------------------------------------------------------------- #
1784 # class FoldWindowItem
1785 # ----------------------------------------------------------------------------------- #
1787 class FoldWindowItem
:
1789 This class is a child sibling of the `FoldPanelItem` class. It
1790 will contain wx.Window that can be either a separator (a colored
1791 line simulated by a wx.Window) or a wxPython controls (such as a
1792 wx.Button, a wx.ListCtrl etc...).
1794 def __init__(self
, parent
, window
=None, **kw
):
1796 Default Class Constructor
1800 Type = "WINDOW", flags = FPB_ALIGN_WIDTH,
1801 Spacing = FPB_DEFAULT_SPACING,
1802 leftSpacing = FPB_DEFAULT_LEFTSPACING,
1803 rightSpacing = FPB_DEFAULT_RIGHTSPACING
1808 y, lineColor = wx.BLACK,
1809 flags = FPB_ALIGN_WIDTH,
1810 Spacing = FPB_DEFAULT_SPACING,
1811 leftSpacing = FPB_DEFAULT_LEFTLINESPACING,
1812 rightSpacing = FPB_DEFAULT_RIGHTLINESPACING
1816 if not kw
.has_key("Type"):
1817 raise 'ERROR: Missing Window Type Information. This Should Be "WINDOW" Or "SEPARATOR"'
1819 if kw
.get("Type") == "WINDOW":
1820 # Window constructor. This initialises the class as a wx.Window Type
1822 if kw
.has_key("flags"):
1823 self
._flags
= kw
.get("flags")
1825 self
._flags
= FPB_ALIGN_WIDTH
1826 if kw
.has_key("Spacing"):
1827 self
._Spacing
= kw
.get("Spacing")
1829 self
._Spacing
= FPB_DEFAULT_SPACING
1830 if kw
.has_key("leftSpacing"):
1831 self
._leftSpacing
= kw
.get("leftSpacing")
1833 self
._leftSpacing
= FPB_DEFAULT_LEFTSPACING
1834 if kw
.has_key("rightSpacing"):
1835 self
._rightSpacing
= kw
.get("rightSpacing")
1837 self
._rightSpacing
= FPB_DEFAULT_RIGHTSPACING
1840 self
._sepLineColour
= None
1844 elif kw
.get("Type") == "SEPARATOR":
1845 # separator constructor. This initialises the class as a separator type
1848 self
._lineY
= kw
.get("y")
1850 raise "ERROR: Undefined Y Position For The Separator"
1851 if kw
.has_key("lineColour"):
1852 self
._sepLineColour
= kw
.get("lineColour")
1854 self
._sepLineColour
= wx
.BLACK
1855 if kw
.has_key("flags"):
1856 self
._flags
= kw
.get("flags")
1858 self
._flags
= FPB_ALIGN_WIDTH
1859 if kw
.has_key("Spacing"):
1860 self
._Spacing
= kw
.get("Spacing")
1862 self
._Spacing
= FPB_DEFAULT_SPACING
1863 if kw
.has_key("leftSpacing"):
1864 self
._leftSpacing
= kw
.get("leftSpacing")
1866 self
._leftSpacing
= FPB_DEFAULT_LEFTSPACING
1867 if kw
.has_key("rightSpacing"):
1868 self
._rightSpacing
= kw
.get("rightSpacing")
1870 self
._rightSpacing
= FPB_DEFAULT_RIGHTSPACING
1875 raise "ERROR: Undefined Window Type Selected: " + repr(kw
.get("Type"))
1877 self
._type
= kw
.get("Type")
1878 self
._lineLength
= 0
1887 def GetLineLength(self
):
1888 return self
._lineLength
1890 def GetLineColour(self
):
1891 return self
._sepLineColour
1893 def GetLeftSpacing(self
):
1894 return self
._leftSpacing
1896 def GetRightSpacing(self
):
1897 return self
._rightSpacing
1899 def GetSpacing(self
):
1900 return self
._Spacing
1903 def GetWindowLength(self
, vertical
=True):
1905 Returns space needed by the window if type is FoldWindowItem
1906 "WINDOW" and returns the total size plus the extra spacing.
1910 if self
._type
== "WINDOW":
1911 size
= self
._wnd
.GetSize()
1912 value
= (vertical
and [size
.GetHeight()] or [size
.GetWidth()])[0] + \
1915 elif self
._type
== "SEPARATOR":
1916 value
= 1 + self
._Spacing
1921 def ResizeItem(self
, size
, vertical
=True):
1923 Resizes the element, whatever it is.
1925 A separator or line will be always aligned by width or height
1926 depending on orientation of the whole panel.
1929 if self
._flags
& FPB_ALIGN_WIDTH
:
1930 # align by taking full width
1931 mySize
= size
- self
._leftSpacing
- self
._rightSpacing
1934 mySize
= 10 # can't have negative width
1936 if self
._type
== "SEPARATOR":
1937 self
._lineLength
= mySize
1939 xsize
= (vertical
and [mySize
] or [-1])[0]
1940 ysize
= (vertical
and [-1] or [mySize
])[0]
1942 self
._wnd
.SetSize((xsize
, ysize
))