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