SetBestFittingSize --> SetInitialSize SetBestSize --> SetInitialSize SetInitialBestSize --> SetInitialSize git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42816 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			1989 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1989 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # --------------------------------------------------------------------------- #
 | |
| # FANCYBUTTONPANEL Widget wxPython IMPLEMENTATION
 | |
| #
 | |
| # Original C++ Code From Eran. You Can Find It At:
 | |
| #
 | |
| # http://wxforum.shadonet.com/viewtopic.php?t=6619
 | |
| #
 | |
| # License: wxWidgets license
 | |
| #
 | |
| #
 | |
| # Python Code By:
 | |
| #
 | |
| # Andrea Gavana, @ 02 Oct 2006
 | |
| # Latest Revision: 17 Oct 2006, 17.00 GMT
 | |
| #
 | |
| #
 | |
| # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
 | |
| # Write To Me At:
 | |
| #
 | |
| # andrea.gavana@gmail.com
 | |
| # gavana@kpo.kz
 | |
| #
 | |
| # Or, Obviously, To The wxPython Mailing List!!!
 | |
| #
 | |
| #
 | |
| # End Of Comments
 | |
| # --------------------------------------------------------------------------- #
 | |
| 
 | |
| """
 | |
| With `ButtonPanel` class you have a panel with gradient coloring
 | |
| on it and with the possibility to place some buttons on it. Using a
 | |
| standard panel with normal wx.Buttons leads to an ugly result: the
 | |
| buttons are placed correctly on the panel - but with grey area around
 | |
| them.  Gradient coloring is kept behind the images - this was achieved
 | |
| due to the PNG format and the transparency of the bitmaps.
 | |
| 
 | |
| The image are functioning like a buttons and can be caught in your
 | |
| code using the usual self.Bind(wx.EVT_BUTTON, self.OnButton) method.
 | |
| 
 | |
| The control is generic, and support theming (well, I tested it under
 | |
| Windows with the three defauls themes: grey, blue, silver and the
 | |
| classic look).
 | |
| 
 | |
| 
 | |
| Usage
 | |
| -----
 | |
| 
 | |
| ButtonPanel supports 4 alignments: left, right, top, bottom, which have a
 | |
| different meaning and behavior wrt wx.Toolbar. The easiest thing is to try
 | |
| the demo to understand, but I'll try to explain how it works.
 | |
| 
 | |
| CASE 1: ButtonPanel has a main caption text
 | |
| 
 | |
| Left alignment means ButtonPanel is horizontal, with the text aligned to the
 | |
| left. When you shrink the demo frame, if there is not enough room for all
 | |
| the controls to be shown, the controls closest to the text are hidden;
 | |
| 
 | |
| Right alignment means ButtonPanel is horizontal, with the text aligned to the
 | |
| right. Item layout as above;
 | |
| 
 | |
| Top alignment means ButtonPanel is vertical, with the text aligned to the top.
 | |
| Item layout as above;
 | |
| 
 | |
| Bottom alignment means ButtonPanel is vertical, with the text aligned to the
 | |
| bottom. Item layout as above.
 | |
| 
 | |
| 
 | |
| CASE 2: ButtonPanel has *no* main caption text
 | |
| In this case, left and right alignment are the same (as top and bottom are the same),
 | |
| but the layout strategy changes: now if there is not enough room for all the controls
 | |
| to be shown, the last added items are hidden ("last" means on the far right for
 | |
| horizontal ButtonPanels and far bottom for vertical ButtonPanels).
 | |
| 
 | |
| 
 | |
| The following example shows a simple implementation that uses ButtonPanel
 | |
| inside a very simple frame::
 | |
| 
 | |
|   class MyFrame(wx.Frame):
 | |
| 
 | |
|       def __init__(self, parent, id=-1, title="ButtonPanel", pos=wx.DefaultPosition,
 | |
|                    size=(800, 600), style=wx.DEFAULT_FRAME_STYLE):
 | |
|                  
 | |
|           wx.Frame.__init__(self, parent, id, title, pos, size, style)
 | |
| 
 | |
|           mainPanel = wx.Panel(self, -1)
 | |
|           self.logtext = wx.TextCtrl(mainPanel, -1, "", style=wx.TE_MULTILINE)
 | |
| 
 | |
|           vSizer = wx.BoxSizer(wx.VERTICAL) 
 | |
|           mainPanel.SetSizer(vSizer) 
 | |
| 
 | |
|           alignment = BP_ALIGN_RIGHT 
 | |
| 
 | |
|           titleBar = ButtonPanel(mainPanel, -1, "A Simple Test & Demo")
 | |
| 
 | |
|           btn1 = ButtonInfo(wx.NewId(), wx.Bitmap("png4.png", wx.BITMAP_TYPE_PNG))
 | |
|           titleBar.AddButton(btn1)
 | |
|           self.Bind(wx.EVT_BUTTON, self.OnButton, btn1)
 | |
| 
 | |
|           btn2 = ButtonInfo(wx.NewId(), wx.Bitmap("png3.png", wx.BITMAP_TYPE_PNG))
 | |
|           titleBar.AddButton(btn2)
 | |
|           self.Bind(wx.EVT_BUTTON, self.OnButton, btn2)
 | |
| 
 | |
|           btn3 = ButtonInfo(wx.NewId(), wx.Bitmap("png2.png", wx.BITMAP_TYPE_PNG))
 | |
|           titleBar.AddButton(btn3)
 | |
|           self.Bind(wx.EVT_BUTTON, self.OnButton, btn3)
 | |
| 
 | |
|           btn4 = ButtonInfo(wx.NewId(), wx.Bitmap("png1.png", wx.BITMAP_TYPE_PNG))
 | |
|           titleBar.AddButton(btn4)
 | |
|           self.Bind(wx.EVT_BUTTON, self.OnButton, btn4)
 | |
| 
 | |
|           vSizer.Add(titleBar, 0, wx.EXPAND)
 | |
|           vSizer.Add((20, 20))
 | |
|           vSizer.Add(self.logtext, 1, wx.EXPAND|wx.ALL, 5)
 | |
|   
 | |
|           vSizer.Layout()
 | |
|   
 | |
|   # our normal wxApp-derived class, as usual
 | |
| 
 | |
|   app = wx.PySimpleApp()
 | |
|   
 | |
|   frame = MyFrame(None)
 | |
|   app.SetTopWindow(frame)
 | |
|   frame.Show()
 | |
|   
 | |
|   app.MainLoop()
 | |
| 
 | |
| 
 | |
| License And Version:
 | |
| 
 | |
| ButtonPanel Is Freeware And Distributed Under The wxPython License. 
 | |
| 
 | |
| Latest Revision: Andrea Gavana @ 12 Oct 2006, 17.00 GMT
 | |
| Version 0.3.
 | |
| 
 | |
| """
 | |
| 
 | |
| 
 | |
| import wx
 | |
| 
 | |
| # Some constants to tune the BPArt class
 | |
| BP_BACKGROUND_COLOR = 0
 | |
| """ Background brush colour when no gradient shading exists. """
 | |
| BP_GRADIENT_COLOR_FROM = 1
 | |
| """ Starting gradient colour, used only when BP_USE_GRADIENT style is applied. """
 | |
| BP_GRADIENT_COLOR_TO = 2
 | |
| """ Ending gradient colour, used only when BP_USE_GRADIENT style is applied. """
 | |
| BP_BORDER_COLOR = 3
 | |
| """ Pen colour to paint the border of ButtonPanel. """
 | |
| BP_TEXT_COLOR = 4
 | |
| """ Main ButtonPanel caption colour. """
 | |
| BP_BUTTONTEXT_COLOR = 5
 | |
| """ Text colour for buttons with text. """
 | |
| BP_BUTTONTEXT_INACTIVE_COLOR = 6
 | |
| """ Text colour for inactive buttons with text. """
 | |
| BP_SELECTION_BRUSH_COLOR = 7
 | |
| """ Brush colour to be used when hovering or selecting a button. """
 | |
| BP_SELECTION_PEN_COLOR = 8
 | |
| """ Pen colour to be used when hovering or selecting a button. """
 | |
| BP_SEPARATOR_COLOR = 9
 | |
| """ Pen colour used to paint the separators. """
 | |
| BP_TEXT_FONT = 10
 | |
| """ Font of the ButtonPanel main caption. """
 | |
| BP_BUTTONTEXT_FONT = 11
 | |
| """ Text font for the buttons with text. """
 | |
| 
 | |
| BP_BUTTONTEXT_ALIGN_BOTTOM = 12
 | |
| """ Flag that indicates the image and text in buttons is stacked. """
 | |
| BP_BUTTONTEXT_ALIGN_RIGHT = 13
 | |
| """ Flag that indicates the text is shown alongside the image in buttons with text. """
 | |
| 
 | |
| BP_SEPARATOR_SIZE = 14
 | |
| """
 | |
| Separator size. NB: This is not the line width, but the sum of the space before
 | |
| and after the separator line plus the width of the line.
 | |
| """
 | |
| BP_MARGINS_SIZE = 15
 | |
| """
 | |
| Size of the left/right margins in ButtonPanel (top/bottom for vertically
 | |
| aligned ButtonPanels).
 | |
| """
 | |
| BP_BORDER_SIZE = 16
 | |
| """ Size of the border. """
 | |
| BP_PADDING_SIZE = 17
 | |
| """ Inter-tool separator size. """
 | |
| 
 | |
| # Caption Gradient Type
 | |
| BP_GRADIENT_NONE = 0
 | |
| """ No gradient shading should be used to paint the background. """
 | |
| BP_GRADIENT_VERTICAL = 1
 | |
| """ Vertical gradient shading should be used to paint the background. """
 | |
| BP_GRADIENT_HORIZONTAL = 2
 | |
| """ Horizontal gradient shading should be used to paint the background. """
 | |
| 
 | |
| # Flags for HitTest() method
 | |
| BP_HT_BUTTON = 200
 | |
| BP_HT_NONE = 201
 | |
| 
 | |
| # Alignment of buttons in the panel
 | |
| BP_ALIGN_RIGHT = 1
 | |
| BP_ALIGN_LEFT = 2
 | |
| BP_ALIGN_TOP = 4
 | |
| BP_ALIGN_BOTTOM = 8
 | |
| 
 | |
| # ButtonPanel styles
 | |
| BP_DEFAULT_STYLE = 1
 | |
| BP_USE_GRADIENT = 2
 | |
| 
 | |
| # Delay used to cancel the longHelp in the statusbar field
 | |
| _DELAY = 3000
 | |
| 
 | |
| 
 | |
| # Check for the new method in 2.7 (not present in 2.6.3.3)
 | |
| if wx.VERSION_STRING < "2.7":
 | |
|     wx.Rect.Contains = lambda self, point: wx.Rect.Inside(self, point)
 | |
| 
 | |
|  
 | |
| def BrightenColour(color, factor): 
 | |
|     """ Bright the input colour by a factor."""
 | |
| 
 | |
|     val = color.Red()*factor
 | |
|     if val > 255:
 | |
|         red = 255
 | |
|     else:
 | |
|         red = val
 | |
|         
 | |
|     val = color.Green()*factor
 | |
|     if val > 255:
 | |
|         green = 255
 | |
|     else:
 | |
|         green = val
 | |
| 
 | |
|     val = color.Blue()*factor
 | |
|     if val > 255:
 | |
|         blue = 255
 | |
|     else:
 | |
|         blue = val
 | |
| 
 | |
|     return wx.Color(red, green, blue) 
 | |
| 
 | |
| 
 | |
| def GrayOut(anImage):
 | |
|     """
 | |
|     Convert the given image (in place) to a grayed-out version,
 | |
|     appropriate for a 'Disabled' appearance.
 | |
|     """
 | |
|     
 | |
|     factor = 0.7        # 0 < f < 1.  Higher Is Grayer
 | |
| 
 | |
|     anImage = anImage.ConvertToImage()
 | |
|     if anImage.HasAlpha():
 | |
|         anImage.ConvertAlphaToMask(1)
 | |
|     
 | |
|     if anImage.HasMask():
 | |
|         maskColor = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue())
 | |
|     else:
 | |
|         maskColor = None
 | |
|         
 | |
|     data = map(ord, list(anImage.GetData()))
 | |
|     
 | |
|     for i in range(0, len(data), 3):
 | |
| 
 | |
|         pixel = (data[i], data[i+1], data[i+2])
 | |
|         pixel = MakeGray(pixel, factor, maskColor)
 | |
| 
 | |
|         for x in range(3):
 | |
|             data[i+x] = pixel[x]
 | |
| 
 | |
|     anImage.SetData(''.join(map(chr, data)))
 | |
| 
 | |
|     anImage = anImage.ConvertToBitmap()
 | |
| 
 | |
|     return anImage
 | |
| 
 | |
| 
 | |
| def MakeGray((r,g,b), factor, maskColor):
 | |
|     """
 | |
|     Make a pixel grayed-out. If the pixel matches the maskColor, it won't be
 | |
|     changed.
 | |
|     """
 | |
|     
 | |
|     if (r,g,b) != maskColor:
 | |
|         return map(lambda x: int((230 - x) * factor) + x, (r,g,b))
 | |
|     else:
 | |
|         return (r,g,b)
 | |
| 
 | |
| 
 | |
| # ---------------------------------------------------------------------------- #
 | |
| # Class BPArt
 | |
| # Handles all the drawings for buttons, separators and text and allows the
 | |
| # programmer to set colours, sizes and gradient shadings for ButtonPanel
 | |
| # ---------------------------------------------------------------------------- #
 | |
| 
 | |
| class BPArt:
 | |
|     """
 | |
|     BPArt is an art provider class which does all of the drawing for ButtonPanel.
 | |
|     This allows the library caller to customize the BPArt or to completely replace
 | |
|     all drawing with custom BPArts.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, parentStyle):
 | |
|         """ Default class constructor. """
 | |
| 
 | |
|         base_color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)
 | |
| 
 | |
|         self._background_brush = wx.Brush(base_color, wx.SOLID)
 | |
|         self._gradient_color_to = wx.WHITE
 | |
|         self._gradient_color_from = wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
 | |
| 
 | |
|         if parentStyle & BP_USE_GRADIENT:
 | |
|             self._border_pen = wx.Pen(wx.WHITE, 3)
 | |
|             self._caption_text_color = wx.WHITE
 | |
|             self._buttontext_color = wx.Colour(70, 143, 255)
 | |
|             self._separator_pen = wx.Pen(BrightenColour(self._gradient_color_from, 1.4))
 | |
|             self._gradient_type = BP_GRADIENT_VERTICAL
 | |
|         else:
 | |
|             self._border_pen = wx.Pen(BrightenColour(base_color, 0.9), 3)
 | |
|             self._caption_text_color = wx.BLACK
 | |
|             self._buttontext_color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNTEXT)
 | |
|             self._separator_pen = wx.Pen(BrightenColour(base_color, 0.9))
 | |
|             self._gradient_type = BP_GRADIENT_NONE
 | |
|             
 | |
|         self._buttontext_inactive_color = wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT)
 | |
|         self._selection_brush = wx.Brush(wx.Color(225, 225, 255))
 | |
|         self._selection_pen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION))
 | |
|         
 | |
|         sysfont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
 | |
|         self._caption_font = wx.Font(sysfont.GetPointSize(), wx.DEFAULT, wx.NORMAL, wx.BOLD,
 | |
|                                      False, sysfont.GetFaceName())
 | |
|         self._buttontext_font = wx.Font(sysfont.GetPointSize(), wx.DEFAULT, wx.NORMAL, wx.NORMAL,
 | |
|                                         False, sysfont.GetFaceName())
 | |
|    
 | |
|         self._separator_size = 7
 | |
|         self._margins_size = wx.Size(6, 6)
 | |
|         self._caption_border_size = 3
 | |
|         self._padding_size = wx.Size(6, 6)
 | |
| 
 | |
| 
 | |
|     def GetMetric(self, id):
 | |
|         """ Returns sizes of customizable options. """
 | |
| 
 | |
|         if id == BP_SEPARATOR_SIZE:
 | |
|             return self._separator_size
 | |
|         elif id == BP_MARGINS_SIZE:
 | |
|             return self._margins_size
 | |
|         elif id == BP_BORDER_SIZE:
 | |
|             return self._caption_border_size
 | |
|         elif id == BP_PADDING_SIZE:
 | |
|             return self._padding_size
 | |
|         else:
 | |
|             raise "\nERROR: Invalid Metric Ordinal. "
 | |
| 
 | |
| 
 | |
|     def SetMetric(self, id, new_val):
 | |
|         """ Sets sizes for customizable options. """
 | |
| 
 | |
|         if id == BP_SEPARATOR_SIZE:
 | |
|             self._separator_size = new_val
 | |
|         elif id == BP_MARGINS_SIZE:
 | |
|             self._margins_size = new_val
 | |
|         elif id == BP_BORDER_SIZE:
 | |
|             self._caption_border_size = new_val
 | |
|             self._border_pen.SetWidth(new_val)
 | |
|         elif id == BP_PADDING_SIZE:
 | |
|             self._padding_size = new_val
 | |
|         else:
 | |
|             raise "\nERROR: Invalid Metric Ordinal. "
 | |
| 
 | |
| 
 | |
|     def GetColor(self, id):
 | |
|         """ Returns colours of customizable options. """
 | |
| 
 | |
|         if id == BP_BACKGROUND_COLOR:
 | |
|             return self._background_brush.GetColour()
 | |
|         elif id == BP_GRADIENT_COLOR_FROM:
 | |
|             return self._gradient_color_from
 | |
|         elif id == BP_GRADIENT_COLOR_TO:
 | |
|             return self._gradient_color_to
 | |
|         elif id == BP_BORDER_COLOR:
 | |
|             return self._border_pen.GetColour()
 | |
|         elif id == BP_TEXT_COLOR:
 | |
|             return self._caption_text_color
 | |
|         elif id == BP_BUTTONTEXT_COLOR:
 | |
|             return self._buttontext_color
 | |
|         elif id == BP_BUTTONTEXT_INACTIVE_COLOR:
 | |
|             return self._buttontext_inactive_color
 | |
|         elif id == BP_SELECTION_BRUSH_COLOR:
 | |
|             return self._selection_brush.GetColour()
 | |
|         elif id == BP_SELECTION_PEN_COLOR:
 | |
|             return self._selection_pen.GetColour()
 | |
|         elif id == BP_SEPARATOR_COLOR:
 | |
|             return self._separator_pen.GetColour()
 | |
|         else:
 | |
|             raise "\nERROR: Invalid Colour Ordinal. "
 | |
| 
 | |
| 
 | |
|     def SetColor(self, id, colour):
 | |
|         """ Sets colours for customizable options. """
 | |
| 
 | |
|         if id == BP_BACKGROUND_COLOR:
 | |
|             self._background_brush.SetColour(colour)
 | |
|         elif id == BP_GRADIENT_COLOR_FROM:
 | |
|             self._gradient_color_from = colour
 | |
|         elif id == BP_GRADIENT_COLOR_TO:
 | |
|             self._gradient_color_to = colour
 | |
|         elif id == BP_BORDER_COLOR:
 | |
|             self._border_pen.SetColour(colour)
 | |
|         elif id == BP_TEXT_COLOR:
 | |
|             self._caption_text_color = colour
 | |
|         elif id == BP_BUTTONTEXT_COLOR:
 | |
|             self._buttontext_color = colour
 | |
|         elif id == BP_BUTTONTEXT_INACTIVE_COLOR:
 | |
|             self._buttontext_inactive_color = colour
 | |
|         elif id == BP_SELECTION_BRUSH_COLOR:
 | |
|             self._selection_brush.SetColour(colour)
 | |
|         elif id == BP_SELECTION_PEN_COLOR:
 | |
|             self._selection_pen.SetColour(colour)
 | |
|         elif id == BP_SEPARATOR_COLOR:
 | |
|             self._separator_pen.SetColour(colour)
 | |
|         else:
 | |
|             raise "\nERROR: Invalid Colour Ordinal. "
 | |
|         
 | |
| 
 | |
|     GetColour = GetColor
 | |
|     SetColour = SetColor
 | |
| 
 | |
| 
 | |
|     def SetFont(self, id, font):
 | |
|         """ Sets font for customizable options. """
 | |
| 
 | |
|         if id == BP_TEXT_FONT:
 | |
|             self._caption_font = font
 | |
|         elif id == BP_BUTTONTEXT_FONT:
 | |
|             self._buttontext_font = font
 | |
| 
 | |
| 
 | |
|     def GetFont(self, id):
 | |
|         """ Returns font of customizable options. """
 | |
| 
 | |
|         if id == BP_TEXT_FONT:
 | |
|             return self._caption_font
 | |
|         elif id == BP_BUTTONTEXT_FONT:
 | |
|             return self._buttontext_font
 | |
|         
 | |
|         return wx.NoneFont
 | |
| 
 | |
| 
 | |
|     def SetGradientType(self, gradient):
 | |
|         """ Sets the gradient type for BPArt drawings. """
 | |
| 
 | |
|         self._gradient_type = gradient
 | |
|         
 | |
| 
 | |
|     def GetGradientType(self):
 | |
|         """ Returns the gradient type for BPArt drawings. """
 | |
| 
 | |
|         return self._gradient_type        
 | |
| 
 | |
|         
 | |
|     def DrawSeparator(self, dc, rect, isVertical):
 | |
|         """ Draws a separator in ButtonPanel. """
 | |
|                     
 | |
|         dc.SetPen(self._separator_pen)
 | |
| 
 | |
|         if isVertical:
 | |
|             ystart = yend = rect.y + rect.height/2
 | |
|             xstart = int(rect.x + 1.5*self._caption_border_size)
 | |
|             xend = int(rect.x + rect.width - 1.5*self._caption_border_size)
 | |
|             dc.DrawLine(xstart, ystart, xend, yend)
 | |
|         else:
 | |
|             xstart = xend = rect.x + rect.width/2
 | |
|             ystart = int(rect.y + 1.5*self._caption_border_size)
 | |
|             yend = int(rect.y + rect.height - 1.5*self._caption_border_size)
 | |
|             dc.DrawLine(xstart, ystart, xend, yend)
 | |
| 
 | |
| 
 | |
|     def DrawCaption(self, dc, rect, captionText):
 | |
|         """ Draws the main caption text in ButtonPanel. """
 | |
| 
 | |
|         textColour = self._caption_text_color
 | |
|         textFont = self._caption_font
 | |
|         padding = self._padding_size
 | |
|             
 | |
|         dc.SetTextForeground(textColour) 
 | |
|         dc.SetFont(textFont)
 | |
| 
 | |
|         dc.DrawText(captionText, rect.x + padding.x, rect.y+padding.y)
 | |
|             
 | |
| 
 | |
|     def DrawButton(self, dc, rect, parentSize, buttonBitmap, isVertical,
 | |
|                    buttonStatus, isToggled, textAlignment, text=""):
 | |
|         """ Draws a button in ButtonPanel, together with its text (if any). """
 | |
|         
 | |
|         bmpxsize, bmpysize = buttonBitmap.GetWidth(), buttonBitmap.GetHeight()
 | |
|         dx = dy = focus = 0
 | |
|         
 | |
|         borderw = self._caption_border_size
 | |
|         padding = self._padding_size
 | |
| 
 | |
|         buttonFont = self._buttontext_font
 | |
|         dc.SetFont(buttonFont)
 | |
|         
 | |
|         if isVertical:
 | |
|             
 | |
|             rect = wx.Rect(borderw, rect.y, rect.width-2*borderw, rect.height)
 | |
|             
 | |
|             if text != "":
 | |
| 
 | |
|                 textW, textH = dc.GetTextExtent(text)
 | |
|                 
 | |
|                 if textAlignment == BP_BUTTONTEXT_ALIGN_RIGHT:
 | |
|                     fullExtent = bmpxsize + padding.x/2 + textW
 | |
|                     bmpypos = rect.y + (rect.height - bmpysize)/2
 | |
|                     bmpxpos = rect.x + (rect.width - fullExtent)/2
 | |
|                     textxpos = bmpxpos + padding.x/2 + bmpxsize
 | |
|                     textypos = bmpypos + (bmpysize - textH)/2
 | |
|                 else:
 | |
|                     bmpxpos = rect.x + (rect.width - bmpxsize)/2
 | |
|                     bmpypos = rect.y + padding.y
 | |
|                     textxpos = rect.x + (rect.width - textW)/2
 | |
|                     textypos = bmpypos + bmpysize + padding.y/2
 | |
|             else:
 | |
|                 bmpxpos = rect.x + (rect.width - bmpxsize)/2
 | |
|                 bmpypos = rect.y + (rect.height - bmpysize)/2
 | |
|                 
 | |
|                 
 | |
|         else:
 | |
| 
 | |
|             rect = wx.Rect(rect.x, borderw, rect.width, rect.height-2*borderw)
 | |
| 
 | |
|             if text != "":
 | |
| 
 | |
|                 textW, textH = dc.GetTextExtent(text)
 | |
|                 
 | |
|                 if textAlignment == BP_BUTTONTEXT_ALIGN_RIGHT:
 | |
|                     fullExtent = bmpxsize + padding.x/2 + textW
 | |
|                     bmpypos = rect.y + (rect.height - bmpysize)/2
 | |
|                     bmpxpos = rect.x + (rect.width - fullExtent)/2
 | |
|                     textxpos = bmpxpos + padding.x/2 + bmpxsize
 | |
|                     textypos = bmpypos + (bmpysize - textH)/2
 | |
|                 else:
 | |
|                     fullExtent = bmpysize + padding.y/2 + textH
 | |
|                     bmpxpos = rect.x + (rect.width - bmpxsize)/2
 | |
|                     bmpypos = rect.y + (rect.height - fullExtent)/2
 | |
|                     textxpos = rect.x + (rect.width - textW)/2
 | |
|                     textypos = bmpypos + bmpysize + padding.y/2
 | |
|             else:
 | |
|                 bmpxpos = rect.x + (rect.width - bmpxsize)/2
 | |
|                 bmpypos = rect.y + (rect.height - bmpysize)/2
 | |
|                     
 | |
|         # Draw a button 
 | |
|         # [ Padding | Text | .. Buttons .. | Padding ]
 | |
| 
 | |
|         if buttonStatus in ["Pressed", "Toggled", "Hover"]:                
 | |
|             dc.SetBrush(self._selection_brush) 
 | |
|             dc.SetPen(self._selection_pen)
 | |
|             dc.DrawRoundedRectangleRect(rect, 4)
 | |
| 
 | |
|         if buttonStatus == "Pressed" or isToggled:
 | |
|             dx = dy = 1
 | |
|             
 | |
|         dc.DrawBitmap(buttonBitmap, bmpxpos+dx, bmpypos+dy, True)
 | |
| 
 | |
|         if text != "":
 | |
|             isEnabled = buttonStatus != "Disabled"
 | |
|             self.DrawLabel(dc, text, isEnabled, textxpos+dx, textypos+dy)
 | |
| 
 | |
|                 
 | |
|     def DrawLabel(self, dc, text, isEnabled, xpos, ypos):
 | |
|         """ Draws the label for a button. """
 | |
| 
 | |
|         if not isEnabled:
 | |
|             dc.SetTextForeground(self._buttontext_inactive_color)
 | |
|         else:
 | |
|             dc.SetTextForeground(self._buttontext_color)
 | |
|             
 | |
|         dc.DrawText(text, xpos, ypos)
 | |
| 
 | |
| 
 | |
|     def DrawButtonPanel(self, dc, rect, style):
 | |
|         """ Paint the ButtonPanel's background. """
 | |
| 
 | |
|         if style & BP_USE_GRADIENT:
 | |
|             # Draw gradient color in the backgroud of the panel 
 | |
|             self.FillGradientColor(dc, rect)
 | |
| 
 | |
|         # Draw a rectangle around the panel
 | |
|         backBrush = (style & BP_USE_GRADIENT and [wx.TRANSPARENT_BRUSH] or \
 | |
|                      [self._background_brush])[0]
 | |
|         
 | |
|         dc.SetBrush(backBrush) 
 | |
|         dc.SetPen(self._border_pen)
 | |
|         dc.DrawRectangleRect(rect) 
 | |
|         
 | |
| 
 | |
|     def FillGradientColor(self, dc, rect):
 | |
|         """ Gradient fill from colour 1 to colour 2 with top to bottom or left to right. """
 | |
| 
 | |
|         if rect.height < 1 or rect.width < 1: 
 | |
|             return 
 | |
| 
 | |
|         isVertical = self._gradient_type == BP_GRADIENT_VERTICAL
 | |
|         size = (isVertical and [rect.height] or [rect.width])[0]
 | |
|         start = (isVertical and [rect.y] or [rect.x])[0]
 | |
| 
 | |
|         # calculate gradient coefficients
 | |
| 
 | |
|         col2 = self._gradient_color_from
 | |
|         col1 = self._gradient_color_to
 | |
| 
 | |
|         rf, gf, bf = 0, 0, 0
 | |
|         rstep = float((col2.Red() - col1.Red()))/float(size)
 | |
|         gstep = float((col2.Green() - col1.Green()))/float(size)
 | |
|         bstep = float((col2.Blue() - col1.Blue()))/float(size)
 | |
| 
 | |
|         for coord in xrange(start, start + size):
 | |
|          
 | |
|             currCol = wx.Colour(col1.Red() + rf, col1.Green() + gf, col1.Blue() + bf)
 | |
|             dc.SetBrush(wx.Brush(currCol, wx.SOLID)) 
 | |
|             dc.SetPen(wx.Pen(currCol))
 | |
|             if isVertical:
 | |
|                 dc.DrawLine(rect.x, coord, rect.x + rect.width, coord) 
 | |
|             else:
 | |
|                 dc.DrawLine(coord, rect.y, coord, rect.y + rect.height)
 | |
|                 
 | |
|             rf += rstep
 | |
|             gf += gstep
 | |
|             bf += bstep 
 | |
|                 
 | |
| 
 | |
| class StatusBarTimer(wx.Timer):
 | |
|     """Timer used for deleting StatusBar long help after _DELAY seconds."""
 | |
| 
 | |
|     def __init__(self, owner):
 | |
|         """
 | |
|         Default class constructor.
 | |
|         For internal use: do not call it in your code!
 | |
|         """
 | |
|         
 | |
|         wx.Timer.__init__(self)
 | |
|         self._owner = owner        
 | |
| 
 | |
| 
 | |
|     def Notify(self):
 | |
|         """The timer has expired."""
 | |
| 
 | |
|         self._owner.OnStatusBarTimer()
 | |
| 
 | |
|         
 | |
| class Control(wx.EvtHandler):
 | |
| 
 | |
|     def __init__(self, parent, size=wx.Size(-1, -1)):
 | |
|         """
 | |
|         Default class constructor.
 | |
|         
 | |
|         Base class for all pseudo controls
 | |
|         parent = parent object
 | |
|         size = (width, height)
 | |
|         """
 | |
| 
 | |
|         wx.EvtHandler.__init__(self)
 | |
| 
 | |
|         self._parent = parent
 | |
|         self._id = wx.NewId()
 | |
|         self._size = size
 | |
|         self._isshown = True
 | |
|         self._focus = False
 | |
| 
 | |
| 
 | |
|     def Show(self, show=True):
 | |
|         """ Shows or hide the control. """
 | |
| 
 | |
|         self._isshown = show
 | |
| 
 | |
| 
 | |
|     def Hide(self):
 | |
|         """ Hides the control. """
 | |
| 
 | |
|         self.Show(False)
 | |
| 
 | |
| 
 | |
|     def IsShown(self):
 | |
|         """ Returns whether the control is shown or not. """
 | |
| 
 | |
|         return self._isshown        
 | |
|         
 | |
| 
 | |
|     def GetId(self):
 | |
|         """ Returns the control id. """
 | |
| 
 | |
|         return self._id
 | |
|     
 | |
| 
 | |
|     def GetBestSize(self):
 | |
|         """ Returns the control best size. """
 | |
| 
 | |
|         return self._size
 | |
| 
 | |
| 
 | |
|     def Disable(self):
 | |
|         """ Disables the control. """
 | |
| 
 | |
|         self.Enable(False)
 | |
| 
 | |
| 
 | |
|     def Enable(self, value=True):
 | |
|         """ Enables or disables the control. """
 | |
| 
 | |
|         self.disabled = not value
 | |
| 
 | |
| 
 | |
|     def SetFocus(self, focus=True):
 | |
|         """ Sets or kills the focus on the control. """
 | |
| 
 | |
|         self._focus = focus
 | |
| 
 | |
|         
 | |
|     def HasFocus(self):
 | |
|         """ Returns whether the control has the focus or not. """
 | |
| 
 | |
|         return self._focus
 | |
|     
 | |
|                 
 | |
|     def OnMouseEvent(self, x, y, event):
 | |
|         pass
 | |
| 
 | |
|     def Draw(self, rect):
 | |
|         pass
 | |
| 
 | |
| 
 | |
| 
 | |
| class Sizer(object):
 | |
|     """
 | |
|     Sizer
 | |
| 
 | |
|     This is a mix-in class to add pseudo support to a wx sizer.  Just create
 | |
|     a new class that derives from this class and the wx sizer and intercepts
 | |
|     any methods that add to the wx sizer.
 | |
|     """
 | |
|     def __init__(self):
 | |
|         self.children = [] # list of child Pseudo Controls
 | |
|     
 | |
|     # Sizer doesn't use the x1,y1,x2,y2 so allow it to 
 | |
|     # be called with or without the coordinates
 | |
|     def Draw(self, dc, x1=0, y1=0, x2=0, y2=0):
 | |
|         for item in self.children:
 | |
|             # use sizer coordinates rather than
 | |
|             # what is passed in
 | |
|             c = item.GetUserData()
 | |
|             c.Draw(dc, item.GetRect())
 | |
|             
 | |
|     def GetBestSize(self):
 | |
|         # this should be handled by the wx.Sizer based class
 | |
|         return self.GetMinSize()
 | |
| 
 | |
| 
 | |
| # Pseudo BoxSizer
 | |
| class BoxSizer(Sizer, wx.BoxSizer):
 | |
|     def __init__(self, orient=wx.HORIZONTAL):
 | |
|         wx.BoxSizer.__init__(self, orient)
 | |
|         Sizer.__init__(self)
 | |
| 
 | |
|     #-------------------------------------------
 | |
|     # sizer overrides (only called from Python)
 | |
|     #-------------------------------------------
 | |
|     # no support for user data if it's a pseudocontrol
 | |
|     # since that is already used
 | |
|     def Add(self, item, proportion=0, flag=0, border=0, userData=None):
 | |
|         # check to see if it's a pseudo object or sizer
 | |
|         if isinstance(item, Sizer):
 | |
|             szitem = wx.BoxSizer.Add(self, item, proportion, flag, border, item)
 | |
|             self.children.append(szitem)
 | |
|         elif isinstance(item, Control): # Control should be what ever class your controls come from
 | |
|             sz = item.GetBestSize()
 | |
|             # add a spacer to track this object
 | |
|             szitem = wx.BoxSizer.Add(self, sz, proportion, flag, border, item)
 | |
|             self.children.append(szitem)
 | |
|         else:
 | |
|             wx.BoxSizer.Add(self, item, proportion, flag, border, userData)
 | |
| 
 | |
|     def Prepend(self, item, proportion=0, flag=0, border=0, userData=None):
 | |
|         # check to see if it's a pseudo object or sizer
 | |
|         if isinstance(item, Sizer):
 | |
|             szitem = wx.BoxSizer.Prepend(self, item, proportion, flag, border, item)
 | |
|             self.children.append(szitem)
 | |
|         elif isinstance(item, Control): # Control should be what ever class your controls come from
 | |
|             sz = item.GetBestSize()
 | |
|             # add a spacer to track this object
 | |
|             szitem = wx.BoxSizer.Prepend(self, sz, proportion, flag, border, item)
 | |
|             self.children.insert(0,szitem)
 | |
|         else:
 | |
|             wx.BoxSizer.Prepend(self, item, proportion, flag, border, userData)
 | |
| 
 | |
|     def Insert(self, before, item, proportion=0, flag=0, border=0, userData=None, realIndex=None):
 | |
|         # check to see if it's a pseudo object or sizer
 | |
|         if isinstance(item, Sizer):
 | |
|             szitem = wx.BoxSizer.Insert(self, before, item, proportion, flag, border, item)
 | |
|             self.children.append(szitem)
 | |
|         elif isinstance(item, Control): # Control should be what ever class your controls come from
 | |
|             sz = item.GetBestSize()
 | |
|             # add a spacer to track this object
 | |
|             szitem = wx.BoxSizer.Insert(self, before, sz, proportion, flag, border, item)
 | |
|             if realIndex is not None:
 | |
|                 self.children.insert(realIndex,szitem)
 | |
|             else:
 | |
|                 self.children.insert(before,szitem)
 | |
|                 
 | |
|         else:
 | |
|             wx.BoxSizer.Insert(self, before, item, proportion, flag, border, userData)
 | |
| 
 | |
| 
 | |
|     def Remove(self, indx, pop=-1):
 | |
| 
 | |
|         if pop >= 0:
 | |
|             self.children.pop(pop)
 | |
| 
 | |
|         wx.BoxSizer.Remove(self, indx)
 | |
|         
 | |
| 
 | |
|     def Layout(self):
 | |
| 
 | |
|         for ii, child in enumerate(self.GetChildren()):
 | |
|             item = child.GetUserData()
 | |
|             if item and child.IsShown():
 | |
|                 self.SetItemMinSize(ii, *item.GetBestSize())
 | |
| 
 | |
|         wx.BoxSizer.Layout(self)
 | |
| 
 | |
|         
 | |
|     def Show(self, item, show=True):
 | |
| 
 | |
|         child = self.GetChildren()[item]
 | |
|         if child and child.GetUserData():
 | |
|             child.GetUserData().Show(show)
 | |
| 
 | |
|         wx.BoxSizer.Show(self, item, show)
 | |
|     
 | |
| 
 | |
| # ---------------------------------------------------------------------------- #
 | |
| # Class Separator
 | |
| # This class holds all the information to size and draw a separator inside
 | |
| # ButtonPanel
 | |
| # ---------------------------------------------------------------------------- #
 | |
| 
 | |
| class Separator(Control):
 | |
| 
 | |
|     def __init__(self, parent):
 | |
|         """ Default class constructor. """
 | |
| 
 | |
|         self._isshown = True
 | |
|         self._parent = parent
 | |
|         Control.__init__(self, parent)
 | |
| 
 | |
|     
 | |
|     def GetBestSize(self):
 | |
|         """ Returns the separator best size. """
 | |
| 
 | |
|         # 10 is completely arbitrary, but it works anyhow
 | |
|         if self._parent.IsVertical():
 | |
|             return wx.Size(10, self._parent._art.GetMetric(BP_SEPARATOR_SIZE))
 | |
|         else:
 | |
|             return wx.Size(self._parent._art.GetMetric(BP_SEPARATOR_SIZE), 10)
 | |
|         
 | |
|     
 | |
|     def Draw(self, dc, rect):
 | |
|         """ Draws the separator. Actually the drawing is done in BPArt. """
 | |
| 
 | |
|         if not self.IsShown():
 | |
|             return
 | |
| 
 | |
|         isVertical = self._parent.IsVertical()        
 | |
|         self._parent._art.DrawSeparator(dc, rect, isVertical)
 | |
|         
 | |
| 
 | |
| # ---------------------------------------------------------------------------- #
 | |
| # Class ButtonPanelText
 | |
| # This class is used to hold data about the main caption in ButtonPanel
 | |
| # ---------------------------------------------------------------------------- #
 | |
| 
 | |
| class ButtonPanelText(Control):
 | |
| 
 | |
|     def __init__(self, parent, text=""):
 | |
|         """ Default class constructor. """
 | |
| 
 | |
|         self._text = text
 | |
|         self._isshown = True
 | |
|         self._parent = parent
 | |
|         
 | |
|         Control.__init__(self, parent)
 | |
| 
 | |
| 
 | |
|     def GetText(self):
 | |
|         """ Returns the caption text. """
 | |
| 
 | |
|         return self._text
 | |
| 
 | |
| 
 | |
|     def SetText(self, text=""):
 | |
|         """ Sets the caption text. """
 | |
| 
 | |
|         self._text = text
 | |
| 
 | |
| 
 | |
|     def CreateDC(self):
 | |
|         """ Convenience function to create a DC. """
 | |
| 
 | |
|         dc = wx.ClientDC(self._parent)
 | |
|         textFont = self._parent._art.GetFont(BP_TEXT_FONT)
 | |
|         dc.SetFont(textFont)
 | |
| 
 | |
|         return dc        
 | |
| 
 | |
|         
 | |
|     def GetBestSize(self):
 | |
|         """ Returns the best size for the main caption in ButtonPanel. """
 | |
| 
 | |
|         if self._text == "":
 | |
|             return wx.Size(0, 0)
 | |
| 
 | |
|         dc = self.CreateDC()
 | |
|         rect = self._parent.GetClientRect()
 | |
|         
 | |
|         tw, th = dc.GetTextExtent(self._text)
 | |
|         padding = self._parent._art.GetMetric(BP_PADDING_SIZE)
 | |
|         self._size = wx.Size(tw+2*padding.x, th+2*padding.y)
 | |
| 
 | |
|         return self._size
 | |
| 
 | |
|     
 | |
|     def Draw(self, dc, rect):
 | |
|         """ Draws the main caption. Actually the drawing is done in BPArt. """
 | |
| 
 | |
|         if not self.IsShown():
 | |
|             return
 | |
| 
 | |
|         captionText = self.GetText()
 | |
|         self._parent._art.DrawCaption(dc, rect, captionText)
 | |
|         
 | |
|             
 | |
| # -- ButtonInfo class implementation ----------------------------------------
 | |
| # This class holds information about every button that is added to
 | |
| # ButtonPanel.  It is an auxiliary class that you should use
 | |
| # every time you add a button.
 | |
| 
 | |
| class ButtonInfo(Control):
 | |
| 
 | |
|     def __init__(self, parent, id=wx.ID_ANY, bmp=wx.NullBitmap,
 | |
|                  status="Normal", text="", kind=wx.ITEM_NORMAL,
 | |
|                  shortHelp="", longHelp=""):
 | |
|         """
 | |
|         Default class constructor.
 | |
| 
 | |
|         Parameters:
 | |
|         - parent: the parent window (ButtonPanel);
 | |
|         - id: the button id;
 | |
|         - bmp: the associated bitmap;
 | |
|         - status: button status (pressed, hovered, normal).
 | |
|         - text: text to be displayed either below of to the right of the button
 | |
|         - kind: button kind, may be wx.ITEM_NORMAL for standard buttons or
 | |
|           wx.ITEM_CHECK for toggle buttons;
 | |
|         - shortHelp: a short help to be shown in the button tooltip;
 | |
|         - longHelp: this string is shown in the statusbar (if any) of the parent
 | |
|           frame when the mouse pointer is inside the button.
 | |
|         """
 | |
|         
 | |
|         if id == wx.ID_ANY:
 | |
|             id = wx.NewId()
 | |
| 
 | |
|         self._status = status
 | |
|         self._rect = wx.Rect()
 | |
|         self._text = text
 | |
|         self._kind = kind
 | |
|         self._toggle = False
 | |
|         self._textAlignment = BP_BUTTONTEXT_ALIGN_BOTTOM
 | |
|         self._shortHelp = shortHelp
 | |
|         self._longHelp = longHelp
 | |
| 
 | |
|         disabledbmp = GrayOut(bmp)
 | |
| 
 | |
|         self._bitmaps = {"Normal": bmp, "Toggled": None, "Disabled": disabledbmp,
 | |
|                          "Hover": None, "Pressed": None}        
 | |
| 
 | |
|         Control.__init__(self, parent)
 | |
|         
 | |
| 
 | |
|     def GetBestSize(self):
 | |
|         """ Returns the best size for the button. """
 | |
| 
 | |
|         xsize = self.GetBitmap().GetWidth()
 | |
|         ysize = self.GetBitmap().GetHeight()
 | |
|         
 | |
|         if self.HasText():
 | |
|             # We have text in the button
 | |
|             dc = wx.ClientDC(self._parent)
 | |
|             normalFont = self._parent._art.GetFont(BP_BUTTONTEXT_FONT)
 | |
|             dc.SetFont(normalFont)
 | |
|             tw, th = dc.GetTextExtent(self.GetText())
 | |
| 
 | |
|             if self.GetTextAlignment() == BP_BUTTONTEXT_ALIGN_BOTTOM:
 | |
|                 xsize = max(xsize, tw)
 | |
|                 ysize = ysize + th
 | |
|             else:
 | |
|                 xsize = xsize + tw
 | |
|                 ysize = max(ysize, th)
 | |
| 
 | |
|         border = self._parent._art.GetMetric(BP_BORDER_SIZE)
 | |
|         padding = self._parent._art.GetMetric(BP_PADDING_SIZE)
 | |
|         
 | |
|         if self._parent.IsVertical():
 | |
|             xsize = xsize + 2*border
 | |
|         else:
 | |
|             ysize = ysize + 2*border
 | |
| 
 | |
|         self._size = wx.Size(xsize+2*padding.x, ysize+2*padding.y)
 | |
| 
 | |
|         return self._size
 | |
| 
 | |
|     
 | |
|     def Draw(self, dc, rect):
 | |
|         """ Draws the button on ButtonPanel. Actually the drawing is done in BPArt. """
 | |
| 
 | |
|         if not self.IsShown():
 | |
|             return
 | |
| 
 | |
|         buttonBitmap = self.GetBitmap()
 | |
|         isVertical = self._parent.IsVertical()
 | |
|         text = self.GetText()
 | |
|         parentSize = self._parent.GetSize()[not isVertical]
 | |
|         buttonStatus = self.GetStatus()
 | |
|         isToggled = self.GetToggled()
 | |
|         textAlignment = self.GetTextAlignment()
 | |
| 
 | |
|         self._parent._art.DrawButton(dc, rect, parentSize, buttonBitmap, isVertical,
 | |
|                                      buttonStatus, isToggled, textAlignment, text)
 | |
| 
 | |
|         self.SetRect(rect)
 | |
|         
 | |
|         
 | |
|     def CheckRefresh(self, status):
 | |
|         """ Checks whether a ButtonPanel repaint is needed or not. Convenience function. """
 | |
| 
 | |
|         if status == self._status:
 | |
|             self._parent.RefreshRect(self.GetRect())
 | |
| 
 | |
|         
 | |
|     def SetBitmap(self, bmp, status="Normal"):
 | |
|         """ Sets the associated bitmap. """
 | |
| 
 | |
|         self._bitmaps[status] = bmp
 | |
|         self.CheckRefresh(status)
 | |
| 
 | |
| 
 | |
|     def GetBitmap(self, status=None):
 | |
|         """ Returns the associated bitmap. """
 | |
| 
 | |
|         if status is None:
 | |
|             status = self._status
 | |
| 
 | |
|         if not self.IsEnabled():
 | |
|             status = "Disabled"
 | |
|             
 | |
|         if self._bitmaps[status] is None:
 | |
|             return self._bitmaps["Normal"]
 | |
|             
 | |
|         return self._bitmaps[status]
 | |
| 
 | |
| 
 | |
|     def GetRect(self):
 | |
|         """ Returns the button rect. """
 | |
| 
 | |
|         return self._rect
 | |
| 
 | |
| 
 | |
|     def GetStatus(self):
 | |
|         """ Returns the button status. """
 | |
| 
 | |
|         return self._status
 | |
| 
 | |
| 
 | |
|     def GetId(self):
 | |
|         """ Returns the button id. """
 | |
|         
 | |
|         return self._id
 | |
| 
 | |
| 
 | |
|     def SetRect(self, rect):
 | |
|         """ Sets the button rect. """
 | |
| 
 | |
|         self._rect = rect
 | |
|         
 | |
| 
 | |
|     def SetStatus(self, status):
 | |
|         """ Sets the button status. """
 | |
| 
 | |
|         if status == self._status:
 | |
|             return
 | |
|         
 | |
|         if self.GetToggled() and status == "Normal":
 | |
|             status = "Toggled"
 | |
|             
 | |
|         self._status = status
 | |
|         self._parent.RefreshRect(self.GetRect())
 | |
| 
 | |
| 
 | |
|     def GetTextAlignment(self):
 | |
|         """ Returns the text alignment in the button (bottom or right). """
 | |
| 
 | |
|         return self._textAlignment
 | |
| 
 | |
| 
 | |
|     def SetTextAlignment(self, alignment):
 | |
|         """ Sets the text alignment in the button (bottom or right). """
 | |
| 
 | |
|         if alignment == self._textAlignment:
 | |
|             return
 | |
| 
 | |
|         self._textAlignment = alignment
 | |
|         
 | |
| 
 | |
|     def GetToggled(self):
 | |
|         """ Returns whether a wx.ITEM_CHECK button is toggled or not. """
 | |
| 
 | |
|         if self._kind == wx.ITEM_NORMAL:
 | |
|             return False
 | |
| 
 | |
|         return self._toggle
 | |
|     
 | |
| 
 | |
|     def SetToggled(self, toggle=True):
 | |
|         """ Sets a wx.ITEM_CHECK button toggled/not toggled. """
 | |
| 
 | |
|         if self._kind == wx.ITEM_NORMAL:
 | |
|             return
 | |
| 
 | |
|         self._toggle = toggle
 | |
| 
 | |
| 
 | |
|     def SetId(self, id):
 | |
|         """ Sets the button id. """
 | |
| 
 | |
|         self._id = id
 | |
| 
 | |
| 
 | |
|     def AddStatus(self, name="Custom", bmp=wx.NullBitmap):
 | |
|         """
 | |
|         Add a programmer-defined status in addition to the 5 default status:
 | |
|         - Normal;
 | |
|         - Disabled;
 | |
|         - Hover;
 | |
|         - Pressed;
 | |
|         - Toggled.
 | |
|         """
 | |
| 
 | |
|         self._bitmaps.update({name: bmp})
 | |
| 
 | |
| 
 | |
|     def Enable(self, enable=True):
 | |
| 
 | |
|         if enable:
 | |
|             self._status = "Normal"
 | |
|         else:
 | |
|             self._status = "Disabled"
 | |
|         
 | |
| 
 | |
|     def IsEnabled(self):
 | |
| 
 | |
|         return self._status != "Disabled"
 | |
|         
 | |
|     
 | |
|     def SetText(self, text=""):
 | |
|         """ Sets the text of the button. """
 | |
| 
 | |
|         self._text = text
 | |
| 
 | |
| 
 | |
|     def GetText(self):
 | |
|         """ Returns the text associated to the button. """
 | |
| 
 | |
|         return self._text
 | |
| 
 | |
| 
 | |
|     def HasText(self):
 | |
|         """ Returns whether the button has text or not. """
 | |
| 
 | |
|         return self._text != ""
 | |
|     
 | |
| 
 | |
|     def SetKind(self, kind=wx.ITEM_NORMAL):
 | |
|         """ Sets the button type (standard or toggle). """
 | |
| 
 | |
|         self._kind = kind
 | |
| 
 | |
| 
 | |
|     def GetKind(self):
 | |
|         """ Returns the button type (standard or toggle). """
 | |
| 
 | |
|         return self._kind
 | |
| 
 | |
| 
 | |
|     def SetShortHelp(self, help=""):
 | |
|         """ Sets the help string to be shown in a tootip. """
 | |
|         
 | |
|         self._shortHelp = help
 | |
| 
 | |
| 
 | |
|     def GetShortHelp(self):
 | |
|         """ Returns the help string shown in a tootip. """
 | |
| 
 | |
|         return self._shortHelp
 | |
| 
 | |
| 
 | |
|     def SetLongHelp(self, help=""):
 | |
|         """ Sets the help string to be shown in the statusbar. """
 | |
| 
 | |
|         self._longHelp = help
 | |
| 
 | |
| 
 | |
|     def GetLongHelp(self):
 | |
|         """ Returns the help string shown in the statusbar. """
 | |
| 
 | |
|         return self._longHelp
 | |
|     
 | |
|                 
 | |
|     Bitmap = property(GetBitmap, SetBitmap)
 | |
|     Id     = property(GetId, SetId)
 | |
|     Rect   = property(GetRect, SetRect)
 | |
|     Status = property(GetStatus, SetStatus)
 | |
|     
 | |
| 
 | |
| # -- ButtonPanel class implementation ----------------------------------
 | |
| # This is the main class.
 | |
| 
 | |
| class ButtonPanel(wx.PyPanel):
 | |
| 
 | |
|     def __init__(self, parent, id=wx.ID_ANY, text="", style=BP_DEFAULT_STYLE,
 | |
|                  alignment=BP_ALIGN_LEFT, name="buttonPanel"):
 | |
|         """
 | |
|         Default class constructor.
 | |
| 
 | |
|         - parent: parent window 
 | |
|         - id: window ID 
 | |
|         - text: text to draw 
 | |
|         - style: window style
 | |
|         - alignment: alignment of buttons (left or right)
 | |
|         - name: window class name 
 | |
|         """        
 | |
|         
 | |
|         wx.PyPanel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize,
 | |
|                             wx.NO_BORDER, name=name)
 | |
|         
 | |
|         self._vButtons = []
 | |
|         self._vSeparators = []
 | |
| 
 | |
|         self._nStyle = style
 | |
|         self._alignment = alignment
 | |
|         self._statusTimer = None
 | |
|         self._useHelp = True
 | |
|         self._freezeCount = 0
 | |
|         self._currentButton = -1
 | |
|         self._haveTip = False
 | |
| 
 | |
|         self._art = BPArt(style)
 | |
| 
 | |
|         self._controlCreated = False
 | |
| 
 | |
|         direction = (self.IsVertical() and [wx.VERTICAL] or [wx.HORIZONTAL])[0]            
 | |
|         self._mainsizer = BoxSizer(direction)
 | |
|         self.SetSizer(self._mainsizer)
 | |
| 
 | |
|         margins = self._art.GetMetric(BP_MARGINS_SIZE)
 | |
|         
 | |
|         # First spacer to create some room before the first text/button/control
 | |
|         self._mainsizer.Add((margins.x, margins.y), 0)
 | |
|         
 | |
|         # Last spacer to create some room before the last text/button/control
 | |
|         self._mainsizer.Add((margins.x, margins.y), 0)        
 | |
| 
 | |
|         self.Bind(wx.EVT_SIZE, self.OnSize)        
 | |
|         self.Bind(wx.EVT_PAINT, self.OnPaint)
 | |
|         self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
 | |
|         self.Bind(wx.EVT_MOTION, self.OnMouseMove)
 | |
|         self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
 | |
|         self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
 | |
|         self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
 | |
|         self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnterWindow)
 | |
|         
 | |
|         self.SetBarText(text)
 | |
|         self.LayoutItems()
 | |
|         
 | |
|     
 | |
|     def SetBarText(self, text):
 | |
|         """ Sets the main caption text (leave text="" for no text). """
 | |
| 
 | |
|         self.Freeze()
 | |
|         
 | |
|         text = text.strip()
 | |
| 
 | |
|         if self._controlCreated:
 | |
|             self.RemoveText()
 | |
| 
 | |
|         self._text = ButtonPanelText(self, text)
 | |
|         lenChildren = len(self._mainsizer.GetChildren())
 | |
|         
 | |
|         if text == "":
 | |
|             # Even if we have no text, we insert it an empty spacer anyway
 | |
|             # it is easier to handle if you have to recreate the sizer after.
 | |
|             if self.IsStandard():
 | |
|                 self._mainsizer.Insert(1, self._text, 0, wx.ALIGN_CENTER,
 | |
|                                        userData=self._text, realIndex=0)
 | |
|             else:
 | |
|                 self._mainsizer.Insert(lenChildren-1, self._text, 0, wx.ALIGN_CENTER,
 | |
|                                        userData=self._text, realIndex=lenChildren)
 | |
| 
 | |
|             return
 | |
| 
 | |
|         # We have text, so insert the text and an expandable spacer
 | |
|         # alongside it. "Standard" ButtonPanel are left or top aligned.
 | |
|         if self.IsStandard():
 | |
|             self._mainsizer.Insert(1, self._text, 0, wx.ALIGN_CENTER,
 | |
|                                     userData=self._text, realIndex=0)
 | |
|             self._mainsizer.Insert(2, (0, 0), 1, wx.EXPAND)
 | |
|             
 | |
|         else:
 | |
|             self._mainsizer.Insert(lenChildren-1, self._text, 0, wx.ALIGN_CENTER,
 | |
|                                    userData=self._text, realIndex=lenChildren)
 | |
|             self._mainsizer.Insert(lenChildren-1, (0, 0), 1, wx.EXPAND)
 | |
|                 
 | |
| 
 | |
|     def RemoveText(self):
 | |
|         """ Removes the main caption text. """
 | |
|         
 | |
|         lenChildren = len(self._mainsizer.GetChildren())
 | |
|         lenCustom = len(self._vButtons) + len(self._vSeparators) + 1
 | |
|         
 | |
|         if self.IsStandard():
 | |
|             # Detach the text
 | |
|             self._mainsizer.Remove(1, 0)
 | |
|             if self.HasBarText():
 | |
|                 # Detach the expandable spacer
 | |
|                 self._mainsizer.Remove(1, -1)
 | |
|         else:
 | |
|             # Detach the text
 | |
|             self._mainsizer.Remove(lenChildren-2, lenCustom-1)
 | |
|             if self.HasBarText():
 | |
|                 # Detach the expandable spacer            
 | |
|                 self._mainsizer.Remove(lenChildren-3, -1)
 | |
| 
 | |
|                     
 | |
|     def GetBarText(self):
 | |
|         """ Returns the main caption text. """
 | |
| 
 | |
|         return self._text.GetText()
 | |
| 
 | |
| 
 | |
|     def HasBarText(self):
 | |
|         """ Returns whether ButtonPanel has a main caption text or not. """
 | |
| 
 | |
|         return hasattr(self, "_text") and self._text.GetText() != ""
 | |
| 
 | |
|             
 | |
|     def AddButton(self, btnInfo):
 | |
|         """
 | |
|         Adds a button to ButtonPanel. Remember to pass a ButtonInfo instance to
 | |
|         this method. See the demo for details.
 | |
|         """
 | |
| 
 | |
|         lenChildren = len(self._mainsizer.GetChildren())
 | |
|         self._mainsizer.Insert(lenChildren-1, btnInfo, 0, wx.ALIGN_CENTER|wx.EXPAND, userData=btnInfo)
 | |
|             
 | |
|         self._vButtons.append(btnInfo)
 | |
| 
 | |
| 
 | |
|     def AddSpacer(self, size=(0, 0), proportion=1, flag=wx.EXPAND):
 | |
|         """ Adds a spacer (stretchable or fixed-size) to ButtonPanel. """
 | |
| 
 | |
|         lenChildren = len(self._mainsizer.GetChildren())
 | |
|         self._mainsizer.Insert(lenChildren-1, size, proportion, flag)
 | |
|             
 | |
| 
 | |
|     def AddControl(self, control, proportion=0, flag=wx.ALIGN_CENTER|wx.ALL, border=None):
 | |
|         """ Adds a wxPython control to ButtonPanel. """
 | |
| 
 | |
|         lenChildren = len(self._mainsizer.GetChildren())
 | |
|         
 | |
|         if border is None:
 | |
|             border = self._art.GetMetric(BP_PADDING_SIZE)
 | |
|             border = max(border.x, border.y)
 | |
| 
 | |
|         self._mainsizer.Insert(lenChildren-1, control, proportion, flag, border)
 | |
|         
 | |
| 
 | |
|     def AddSeparator(self):
 | |
|         """ Adds a separator line to ButtonPanel. """
 | |
| 
 | |
|         lenChildren = len(self._mainsizer.GetChildren())
 | |
|         separator = Separator(self)
 | |
|         
 | |
|         self._mainsizer.Insert(lenChildren-1, separator, 0, wx.EXPAND)
 | |
|         self._vSeparators.append(separator)
 | |
|         
 | |
| 
 | |
|     def RemoveAllButtons(self):
 | |
|         """ Remove all the buttons from ButtonPanel. """
 | |
| 
 | |
|         self._vButtons = []
 | |
| 
 | |
|         
 | |
|     def RemoveAllSeparators(self):
 | |
|         """ Remove all the separators from ButtonPanel. """
 | |
| 
 | |
|         self._vSeparators = []
 | |
| 
 | |
|         
 | |
|     def GetAlignment(self):
 | |
|         """ Returns the button alignment (left, right, top, bottom). """
 | |
| 
 | |
|         return self._alignment
 | |
|     
 | |
| 
 | |
|     def SetAlignment(self, alignment):
 | |
|         """ Sets the button alignment (left, right, top, bottom). """
 | |
| 
 | |
|         if alignment == self._alignment:
 | |
|             return
 | |
| 
 | |
|         self.Freeze()
 | |
|         
 | |
|         text = self.GetBarText()
 | |
|         
 | |
|         # Remove the text in any case
 | |
|         self.RemoveText()
 | |
| 
 | |
|         # Remove the first and last spacers
 | |
|         self._mainsizer.Remove(0, -1)
 | |
|         self._mainsizer.Remove(len(self._mainsizer.GetChildren())-1, -1)
 | |
|         
 | |
|         self._alignment = alignment
 | |
| 
 | |
|         # Recreate the sizer accordingly to the new alignment
 | |
|         self.ReCreateSizer(text)
 | |
| 
 | |
| 
 | |
|     def IsVertical(self):
 | |
|         """ Returns whether ButtonPanel is vertically aligned or not. """
 | |
| 
 | |
|         return self._alignment not in [BP_ALIGN_RIGHT, BP_ALIGN_LEFT]
 | |
|         
 | |
| 
 | |
|     def IsStandard(self):
 | |
|         """ Returns whether ButtonPanel is aligned "Standard" (left/top) or not. """
 | |
| 
 | |
|         return self._alignment in [BP_ALIGN_LEFT, BP_ALIGN_TOP]
 | |
| 
 | |
| 
 | |
|     def DoLayout(self):
 | |
|         """
 | |
|         Do the Layout for ButtonPanel.
 | |
|         NB: Call this method every time you make a modification to the layout
 | |
|         or to the customizable sizes of the pseudo controls.
 | |
|         """
 | |
| 
 | |
|         margins = self._art.GetMetric(BP_MARGINS_SIZE)
 | |
|         lenChildren = len(self._mainsizer.GetChildren())
 | |
| 
 | |
|         self._mainsizer.SetItemMinSize(0, (margins.x, margins.y))
 | |
|         self._mainsizer.SetItemMinSize(lenChildren-1, (margins.x, margins.y))
 | |
|         
 | |
|         self._controlCreated = True
 | |
|         self.LayoutItems()
 | |
| 
 | |
|         # *VERY* WEIRD: the sizer seems not to respond to any layout until I
 | |
|         # change the ButtonPanel size and restore it back
 | |
|         size = self.GetSize()
 | |
|         self.SetSize((size.x+1, size.y+1))
 | |
|         self.SetSize((size.x, size.y))
 | |
|         
 | |
|         if self.IsFrozen():
 | |
|             self.Thaw()
 | |
|         
 | |
| 
 | |
|     def ReCreateSizer(self, text):
 | |
|         """ Recreates the ButtonPanel sizer accordingly to the alignment specified. """
 | |
|         
 | |
|         children = self._mainsizer.GetChildren()
 | |
|         self.RemoveAllButtons()
 | |
|         self.RemoveAllSeparators()
 | |
| 
 | |
|         # Create a new sizer depending on the alignment chosen
 | |
|         direction = (self.IsVertical() and [wx.VERTICAL] or [wx.HORIZONTAL])[0]            
 | |
|         self._mainsizer = BoxSizer(direction)
 | |
| 
 | |
|         margins = self._art.GetMetric(BP_MARGINS_SIZE)
 | |
|         # First spacer to create some room before the first text/button/control
 | |
|         self._mainsizer.Add((margins.x, margins.y), 0)
 | |
|         
 | |
|         # Last spacer to create some room before the last text/button/control
 | |
|         self._mainsizer.Add((margins.x, margins.y), 0)
 | |
|                 
 | |
|         # This is needed otherwise SetBarText goes mad        
 | |
|         self._controlCreated = False
 | |
| 
 | |
|         for child in children:
 | |
|             userData = child.GetUserData()
 | |
|             if userData:
 | |
|                 if isinstance(userData, ButtonInfo):
 | |
|                     # It is a ButtonInfo, can't be anything else
 | |
|                     self.AddButton(child.GetUserData())
 | |
|                 elif isinstance(userData, Separator):
 | |
|                     self.AddSeparator()
 | |
|                     
 | |
|             else:
 | |
|                 if child.IsSpacer():
 | |
|                     # This is a spacer, expandable or not
 | |
|                     self.AddSpacer(child.GetSize(), child.GetProportion(),
 | |
|                                    child.GetFlag())
 | |
|                 else:
 | |
|                     # This is a wxPython control
 | |
|                     self.AddControl(child.GetWindow(), child.GetProportion(),
 | |
|                                     child.GetFlag(), child.GetBorder())
 | |
| 
 | |
|         self.SetSizer(self._mainsizer)
 | |
| 
 | |
|         # Now add the text. It doesn't matter if there is no text        
 | |
|         self.SetBarText(text)
 | |
|         
 | |
|         self.DoLayout()
 | |
|         
 | |
|         self.Thaw()
 | |
|         
 | |
| 
 | |
|     def DoGetBestSize(self):
 | |
|         """ Returns the best size of ButtonPanel. """
 | |
| 
 | |
|         w = h = btnWidth = btnHeight = 0
 | |
|         isVertical = self.IsVertical()
 | |
| 
 | |
|         padding = self._art.GetMetric(BP_PADDING_SIZE)
 | |
|         border = self._art.GetMetric(BP_BORDER_SIZE)
 | |
|         margins = self._art.GetMetric(BP_MARGINS_SIZE)
 | |
|         separator_size = self._art.GetMetric(BP_SEPARATOR_SIZE)
 | |
| 
 | |
|         # Add the space required for the main caption        
 | |
|         if self.HasBarText():
 | |
|             w, h = self._text.GetBestSize()
 | |
|             if isVertical:
 | |
|                 h += padding.y
 | |
|             else:
 | |
|                 w += padding.x
 | |
|         else:
 | |
|             w = h = border
 | |
| 
 | |
|         # Add the button's sizes
 | |
|         for btn in self._vButtons:
 | |
|             
 | |
|             bw, bh = btn.GetBestSize()            
 | |
|             btnWidth = max(btnWidth, bw)
 | |
|             btnHeight = max(btnHeight, bh)
 | |
| 
 | |
|             if isVertical:            
 | |
|                 w = max(w, btnWidth)
 | |
|                 h += bh
 | |
|             else:
 | |
|                 h = max(h, btnHeight)
 | |
|                 w += bw
 | |
| 
 | |
|         # Add the control's sizes
 | |
|         for control in self.GetControls():
 | |
|             cw, ch = control.GetSize()
 | |
|             if isVertical:
 | |
|                 h += ch
 | |
|                 w = max(w, cw)
 | |
|             else:
 | |
|                 w += cw
 | |
|                 h = max(h, ch)
 | |
| 
 | |
|         # Add the separator's sizes and the 2 SizerItems at the beginning
 | |
|         # and at the end
 | |
|         if self.IsVertical():
 | |
|             h += 2*margins.y + len(self._vSeparators)*separator_size
 | |
|         else:
 | |
|             w += 2*margins.x + len(self._vSeparators)*separator_size
 | |
|             
 | |
|         return wx.Size(w, h)
 | |
| 
 | |
| 
 | |
|     def OnPaint(self, event):
 | |
|         """ Handles the wx.EVT_PAINT event for ButtonPanel. """
 | |
|         
 | |
|         dc = wx.BufferedPaintDC(self) 
 | |
|         rect = self.GetClientRect()
 | |
| 
 | |
|         self._art.DrawButtonPanel(dc, rect, self._nStyle)
 | |
|         self._mainsizer.Draw(dc)
 | |
|                 
 | |
| 
 | |
|     def OnEraseBackground(self, event):
 | |
|         """ Handles the wx.EVT_ERASE_BACKGROUND event for ButtonPanel (does nothing). """
 | |
| 
 | |
|         pass
 | |
|     
 | |
|  
 | |
|     def OnSize(self, event):
 | |
|         """ Handles the wx.EVT_SIZE event for ButtonPanel. """
 | |
| 
 | |
|         # NOTE: It seems like LayoutItems number of calls can be optimized in some way.
 | |
|         # Currently every DoLayout (or every parent Layout()) calls about 3 times
 | |
|         # the LayoutItems method. Any idea on how to improve it?
 | |
|         self.LayoutItems()
 | |
|         self.Refresh()
 | |
| 
 | |
|         event.Skip() 
 | |
| 
 | |
| 
 | |
|     def LayoutItems(self):
 | |
|         """
 | |
|         Layout the items using a different algorithm depending on the existance
 | |
|         of the main caption.
 | |
|         """
 | |
| 
 | |
|         nonspacers, allchildren = self.GetNonFlexibleChildren()
 | |
|         
 | |
|         if self.HasBarText():
 | |
|             self.FlexibleLayout(nonspacers, allchildren)
 | |
|         else:
 | |
|             self.SizeLayout(nonspacers, allchildren)
 | |
|             
 | |
|         self._mainsizer.Layout()
 | |
| 
 | |
| 
 | |
|     def SizeLayout(self, nonspacers, children):
 | |
|         """ Layout the items when no main caption exists. """
 | |
| 
 | |
|         size = self.GetSize()
 | |
|         isVertical = self.IsVertical()
 | |
|         
 | |
|         corner = 0
 | |
|         indx1 = len(nonspacers)
 | |
| 
 | |
|         for item in nonspacers:
 | |
|             corner += self.GetItemSize(item, isVertical)
 | |
|             if corner > size[isVertical]:
 | |
|                 indx1 = nonspacers.index(item)
 | |
|                 break
 | |
| 
 | |
|         # Leave out the last spacer, it has to be there always        
 | |
|         for ii in xrange(len(nonspacers)-1):
 | |
|             indx = children.index(nonspacers[ii])
 | |
|             self._mainsizer.Show(indx, ii < indx1)
 | |
|                 
 | |
| 
 | |
|     def GetItemSize(self, item, isVertical):
 | |
|         """ Returns the size of an item in the main ButtonPanel sizer. """
 | |
|         
 | |
|         if item.GetUserData():
 | |
|             return item.GetUserData().GetBestSize()[isVertical]
 | |
|         else:
 | |
|             return item.GetSize()[isVertical]
 | |
| 
 | |
| 
 | |
|     def FlexibleLayout(self, nonspacers, allchildren):
 | |
|         """ Layout the items when the main caption exists. """
 | |
| 
 | |
|         if len(nonspacers) < 2:
 | |
|             return
 | |
|         
 | |
|         isVertical = self.IsVertical()
 | |
|         isStandard = self.IsStandard()
 | |
|         
 | |
|         size = self.GetSize()[isVertical]
 | |
|         padding = self._art.GetMetric(BP_PADDING_SIZE)
 | |
|         
 | |
|         fixed = (isStandard and [nonspacers[1]] or [nonspacers[-2]])[0]
 | |
|         
 | |
|         if isStandard:
 | |
|             nonspacers.reverse()
 | |
|             leftendx = fixed.GetSize()[isVertical] + padding.x
 | |
|         else:
 | |
|             rightstartx = size - fixed.GetSize()[isVertical]
 | |
|             size = 0
 | |
| 
 | |
|         count = lennonspacers = len(nonspacers)
 | |
|         
 | |
|         for item in nonspacers:
 | |
|             if isStandard:
 | |
|                 size -= self.GetItemSize(item, isVertical)
 | |
|                 if size < leftendx:
 | |
|                     break
 | |
|             else:
 | |
|                 size += self.GetItemSize(item, isVertical)
 | |
|                 if size > rightstartx:
 | |
|                     break
 | |
|                 
 | |
|             count = count - 1
 | |
| 
 | |
|         nonspacers.reverse()
 | |
|         
 | |
|         for jj in xrange(2, lennonspacers):
 | |
|             indx = allchildren.index(nonspacers[jj])
 | |
|             self._mainsizer.Show(indx, jj >= count)
 | |
| 
 | |
|         
 | |
|     def GetNonFlexibleChildren(self):
 | |
|         """
 | |
|         Returns all the ButtonPanel main sizer's children that are not
 | |
|         flexible spacers.
 | |
|         """
 | |
| 
 | |
|         children1 = []
 | |
|         children2 = self._mainsizer.GetChildren()
 | |
|         
 | |
|         for child in children2:
 | |
|             if child.IsSpacer():
 | |
|                 if child.GetUserData() or child.GetProportion() == 0:
 | |
|                     children1.append(child)
 | |
|             else:
 | |
|                 children1.append(child)
 | |
| 
 | |
|         return children1, children2
 | |
| 
 | |
| 
 | |
|     def GetControls(self):
 | |
|         """ Returns the wxPython controls that belongs to ButtonPanel. """
 | |
|     
 | |
|         children2 = self._mainsizer.GetChildren()
 | |
|         children1 = [child for child in children2 if not child.IsSpacer()]
 | |
| 
 | |
|         return children1
 | |
| 
 | |
| 
 | |
|     def SetStyle(self, style):
 | |
|         """ Sets ButtonPanel style. """
 | |
| 
 | |
|         if style == self._nStyle:
 | |
|             return
 | |
| 
 | |
|         self._nStyle = style
 | |
|         self.Refresh()
 | |
| 
 | |
| 
 | |
|     def GetStyle(self):
 | |
|         """ Returns the ButtonPanel style. """
 | |
| 
 | |
|         return self._nStyle
 | |
| 
 | |
|         
 | |
|     def OnMouseMove(self, event):
 | |
|         """ Handles the wx.EVT_MOTION event for ButtonPanel. """
 | |
|         
 | |
|         # Check to see if we are hovering a button
 | |
|         tabId, flags = self.HitTest(event.GetPosition())
 | |
| 
 | |
|         if flags != BP_HT_BUTTON:
 | |
|             self.RemoveHelp()
 | |
|             self.RepaintOldSelection()
 | |
|             self._currentButton = -1
 | |
|             return
 | |
|         
 | |
|         btn = self._vButtons[tabId]
 | |
| 
 | |
|         if not btn.IsEnabled():
 | |
|             self.RemoveHelp()
 | |
|             self.RepaintOldSelection()
 | |
|             return
 | |
| 
 | |
|         if tabId != self._currentButton:
 | |
|             self.RepaintOldSelection()
 | |
|         
 | |
|         if btn.GetRect().Contains(event.GetPosition()):
 | |
|             btn.SetStatus("Hover")
 | |
|         else:
 | |
|             btn.SetStatus("Normal")
 | |
| 
 | |
|         if tabId != self._currentButton:
 | |
|             self.RemoveHelp()
 | |
|             self.DoGiveHelp(btn)
 | |
|             
 | |
|         self._currentButton = tabId
 | |
|                  
 | |
|         event.Skip() 
 | |
|  
 | |
| 
 | |
|     def OnLeftDown(self, event):
 | |
|         """ Handles the wx.EVT_LEFT_DOWN event for ButtonPanel. """
 | |
|  
 | |
|         tabId, hit = self.HitTest(event.GetPosition())
 | |
| 
 | |
|         if hit == BP_HT_BUTTON:
 | |
|             btn = self._vButtons[tabId]
 | |
|             if btn.IsEnabled():                 
 | |
|                 btn.SetStatus("Pressed")
 | |
|                 self._currentButton = tabId
 | |
|  
 | |
| 
 | |
|     def OnLeftUp(self, event):
 | |
|         """ Handles the wx.EVT_LEFT_UP event for ButtonPanel. """
 | |
|         
 | |
|         tabId, flags = self.HitTest(event.GetPosition())
 | |
|         
 | |
|         if flags != BP_HT_BUTTON:
 | |
|             return
 | |
|             
 | |
|         hit = self._vButtons[tabId]
 | |
| 
 | |
|         if hit.GetStatus() == "Disabled":
 | |
|             return
 | |
| 
 | |
|         for btn in self._vButtons:
 | |
|             if btn != hit:
 | |
|                 btn.SetFocus(False)
 | |
|                 
 | |
|         if hit.GetStatus() == "Pressed": 
 | |
|             # Fire a button click event 
 | |
|             btnEvent = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, hit.GetId())
 | |
|             self.GetEventHandler().ProcessEvent(btnEvent) 
 | |
| 
 | |
|             hit.SetToggled(not hit.GetToggled())
 | |
|             
 | |
|             # Update the button status to be hovered 
 | |
|             hit.SetStatus("Hover")
 | |
|             hit.SetFocus()
 | |
|             self._currentButton = tabId
 | |
|             
 | |
| 
 | |
|     def OnMouseLeave(self, event):
 | |
|         """ Handles the wx.EVT_LEAVE_WINDOW event for ButtonPanel. """
 | |
|         
 | |
|         # Reset all buttons statuses
 | |
|         for btn in self._vButtons:
 | |
|             if not btn.IsEnabled():
 | |
|                 continue
 | |
|             btn.SetStatus("Normal")
 | |
| 
 | |
|         self.RemoveHelp()
 | |
|                 
 | |
|         event.Skip() 
 | |
|  
 | |
| 
 | |
|     def OnMouseEnterWindow(self, event):
 | |
|         """ Handles the wx.EVT_ENTER_WINDOW event for ButtonPanel. """
 | |
| 
 | |
|         tabId, flags = self.HitTest(event.GetPosition())
 | |
| 
 | |
|         if flags == BP_HT_BUTTON:
 | |
|             
 | |
|             hit = self._vButtons[tabId]
 | |
| 
 | |
|             if hit.GetStatus() == "Disabled":
 | |
|                 event.Skip()
 | |
|                 return
 | |
| 
 | |
|             self.DoGiveHelp(hit)
 | |
|             self._currentButton = tabId
 | |
|                         
 | |
|         event.Skip() 
 | |
|  
 | |
| 
 | |
|     def DoGiveHelp(self, hit):
 | |
|         """ Gives tooltips and help in StatusBar. """
 | |
| 
 | |
|         if not self.GetUseHelp():
 | |
|             return
 | |
| 
 | |
|         shortHelp = hit.GetShortHelp()
 | |
|         if shortHelp:
 | |
|             self.SetToolTipString(shortHelp)
 | |
|             self._haveTip = True
 | |
| 
 | |
|         longHelp = hit.GetLongHelp()
 | |
|         if not longHelp:
 | |
|             return
 | |
|         
 | |
|         topLevel = wx.GetTopLevelParent(self)
 | |
|         
 | |
|         if isinstance(topLevel, wx.Frame) and topLevel.GetStatusBar():
 | |
|             statusBar = topLevel.GetStatusBar()
 | |
| 
 | |
|             if self._statusTimer and self._statusTimer.IsRunning():
 | |
|                 self._statusTimer.Stop()
 | |
|                 statusBar.PopStatusText(0)
 | |
|                 
 | |
|             statusBar.PushStatusText(longHelp, 0)
 | |
|             self._statusTimer = StatusBarTimer(self)
 | |
|             self._statusTimer.Start(_DELAY, wx.TIMER_ONE_SHOT)
 | |
| 
 | |
| 
 | |
|     def RemoveHelp(self):
 | |
|         """ Removes the tooltips and statusbar help (if any) for a button. """
 | |
| 
 | |
|         if not self.GetUseHelp():
 | |
|             return
 | |
| 
 | |
|         if self._haveTip:
 | |
|             self.SetToolTipString("")
 | |
|             self._haveTip = False
 | |
| 
 | |
|         if self._statusTimer and self._statusTimer.IsRunning():
 | |
|             topLevel = wx.GetTopLevelParent(self)
 | |
|             statusBar = topLevel.GetStatusBar()
 | |
|             self._statusTimer.Stop()
 | |
|             statusBar.PopStatusText(0)
 | |
|             self._statusTimer = None
 | |
|         
 | |
| 
 | |
|     def RepaintOldSelection(self):
 | |
|         """ Repaints the old selected/hovered button. """
 | |
|         
 | |
|         current = self._currentButton
 | |
|         
 | |
|         if current == -1:
 | |
|             return
 | |
| 
 | |
|         btn = self._vButtons[current]
 | |
|         if not btn.IsEnabled():
 | |
|             return
 | |
| 
 | |
|         btn.SetStatus("Normal")
 | |
| 
 | |
|                 
 | |
|     def OnStatusBarTimer(self):
 | |
|         """ Handles the timer expiring to delete the longHelp in the StatusBar. """
 | |
| 
 | |
|         topLevel = wx.GetTopLevelParent(self)
 | |
|         statusBar = topLevel.GetStatusBar()        
 | |
|         statusBar.PopStatusText(0)
 | |
|         
 | |
| 
 | |
|     def SetUseHelp(self, useHelp=True):
 | |
|         """ Sets whether or not shortHelp and longHelp should be displayed. """
 | |
| 
 | |
|         self._useHelp = useHelp
 | |
| 
 | |
| 
 | |
|     def GetUseHelp(self):
 | |
|         """ Returns whether or not shortHelp and longHelp should be displayed. """
 | |
|         
 | |
|         return self._useHelp
 | |
| 
 | |
|     
 | |
|     def HitTest(self, pt):
 | |
|         """
 | |
|         HitTest method for ButtonPanel. Returns the button (if any) and
 | |
|         a flag (if any).
 | |
|         """
 | |
|          
 | |
|         for ii in xrange(len(self._vButtons)):
 | |
|             if not self._vButtons[ii].IsEnabled():
 | |
|                 continue
 | |
|             if self._vButtons[ii].GetRect().Contains(pt):
 | |
|                 return ii, BP_HT_BUTTON
 | |
| 
 | |
|         return -1, BP_HT_NONE
 | |
|  
 | |
| 
 | |
|     def GetBPArt(self):
 | |
|         """ Returns the associated BPArt art provider. """
 | |
| 
 | |
|         return self._art
 | |
|     
 | |
| 
 | |
|     def SetBPArt(self, art):
 | |
|         """ Sets a new BPArt to ButtonPanel. Useful only if another BPArt class is used. """
 | |
|         
 | |
|         self._art = art
 | |
|         self.Refresh()
 | |
| 
 | |
|     if wx.VERSION < (2,7,1,1):
 | |
|         def Freeze(self):
 | |
|             """Freeze ButtonPanel."""
 | |
| 
 | |
|             self._freezeCount = self._freezeCount + 1
 | |
|             wx.PyPanel.Freeze(self)
 | |
| 
 | |
| 
 | |
|         def Thaw(self):
 | |
|             """Thaw ButtonPanel."""
 | |
| 
 | |
|             if self._freezeCount == 0:
 | |
|                 raise "\nERROR: Thawing Unfrozen ButtonPanel?"
 | |
| 
 | |
|             self._freezeCount = self._freezeCount - 1
 | |
|             wx.PyPanel.Thaw(self)        
 | |
|         
 | |
| 
 | |
|         def IsFrozen(self):
 | |
|             """ Returns whether a call to Freeze() has been done. """
 | |
| 
 | |
|             return self._freezeCount != 0
 | |
| 
 | |
|     
 |