-# --------------------------------------------------------------------------- #
-# 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
-
- if bmp:
- disabledbmp = GrayOut(bmp)
- else:
- disabledbmp = wx.NullBitmap
-
- 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()):
- if btn.GetStatus() != "Pressed":
- 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":
- hit.SetToggled(not hit.GetToggled())
-
- # Update the button status to be hovered
- hit.SetStatus("Hover")
- hit.SetFocus()
- self._currentButton = tabId
-
- # Fire a button click event
- btnEvent = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, hit.GetId())
- self.GetEventHandler().ProcessEvent(btnEvent)
-
-
- 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
-
-