From 37840b8bc15f302449e3012ebb9b8c21b6def4d1 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Tue, 17 Oct 2006 19:37:16 +0000 Subject: [PATCH] updates from Andrea, plus some fixes git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42082 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/demo/ButtonPanel.py | 846 ++++++++++++++-- wxPython/wx/lib/buttonpanel.py | 1646 +++++++++++++++++++++++++++----- 2 files changed, 2178 insertions(+), 314 deletions(-) diff --git a/wxPython/demo/ButtonPanel.py b/wxPython/demo/ButtonPanel.py index 749b5be7de..416aa308ef 100644 --- a/wxPython/demo/ButtonPanel.py +++ b/wxPython/demo/ButtonPanel.py @@ -1,15 +1,480 @@ - import wx import wx.lib.buttonpanel as bp import images +import random + +#---------------------------------------------------------------------- + +ID_BackgroundColour = wx.NewId() +ID_GradientFrom = wx.NewId() +ID_GradientTo = wx.NewId() +ID_BorderColour = wx.NewId() +ID_CaptionColour = wx.NewId() +ID_ButtonTextColour = wx.NewId() +ID_SelectionBrush = wx.NewId() +ID_SelectionPen = wx.NewId() +ID_SeparatorColour = wx.NewId() + + +#---------------------------------------------------------------------- +# Some icons for the demo +#---------------------------------------------------------------------- +def GetMondrianData(): + return \ +'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\ +\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00qID\ +ATX\x85\xed\xd6;\n\x800\x10E\xd1{\xc5\x8d\xb9r\x97\x16\x0b\xad$\x8a\x82:\x16\ +o\xda\x84pB2\x1f\x81Fa\x8c\x9c\x08\x04Z{\xcf\xa72\xbcv\xfa\xc5\x08 \x80r\x80\ +\xfc\xa2\x0e\x1c\xe4\xba\xfaX\x1d\xd0\xde]S\x07\x02\xd8>\xe1wa-`\x9fQ\xe9\ +\x86\x01\x04\x10\x00\\(Dk\x1b-\x04\xdc\x1d\x07\x14\x98;\x0bS\x7f\x7f\xf9\x13\ +\x04\x10@\xf9X\xbe\x00\xc9 \x14K\xc1<={\x00\x00\x00\x00IEND\xaeB`\x82' + + +def GetMondrianBitmap(): + return wx.BitmapFromImage(GetMondrianImage()) + + +def GetMondrianImage(): + import cStringIO + stream = cStringIO.StringIO(GetMondrianData()) + return wx.ImageFromStream(stream) + + +def GetMondrianIcon(): + icon = wx.EmptyIcon() + icon.CopyFromBitmap(GetMondrianBitmap()) + return icon + +#---------------------------------------------------------------------- + +class SettingsPanel(wx.MiniFrame): + + def __init__(self, parent, id=wx.ID_ANY, title="Settings Panel", pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.FRAME_NO_TASKBAR + | wx.FRAME_FLOAT_ON_PARENT | wx.CLIP_CHILDREN): + + wx.MiniFrame.__init__(self, parent, id, title, pos, size, style) + + self.targetTitleBar = parent.titleBar + self.parent = parent + self.panel = wx.Panel(self, -1) + + self.coloursizer_staticbox = wx.StaticBox(self.panel, -1, "Colour Options") + self.bottomsizer_staticbox = wx.StaticBox(self.panel, -1, "Size Options") + self.stylesizer_staticbox = wx.StaticBox(self.panel, -1, "ButtonPanel Styles") + self.defaultstyle = wx.RadioButton(self.panel, -1, "Default Style", style=wx.RB_GROUP) + self.gradientstyle = wx.RadioButton(self.panel, -1, "Gradient Style") + self.verticalgradient = wx.RadioButton(self.panel, -1, "Vertical Gradient", style=wx.RB_GROUP) + self.horizontalgradient = wx.RadioButton(self.panel, -1, "Horizontal Gradient") + + b = self.CreateColorBitmap(wx.BLACK) + + self.bakbrush = wx.BitmapButton(self.panel, ID_BackgroundColour, b, size=wx.Size(50,25)) + self.gradientfrom = wx.BitmapButton(self.panel, ID_GradientFrom, b, size=wx.Size(50,25)) + self.gradientto = wx.BitmapButton(self.panel, ID_GradientTo, b, size=wx.Size(50,25)) + self.bordercolour = wx.BitmapButton(self.panel, ID_BorderColour, b, size=wx.Size(50,25)) + self.captioncolour = wx.BitmapButton(self.panel, ID_CaptionColour, b, size=wx.Size(50,25)) + self.textbuttoncolour = wx.BitmapButton(self.panel, ID_ButtonTextColour, b, size=wx.Size(50,25)) + self.selectionbrush = wx.BitmapButton(self.panel, ID_SelectionBrush, b, size=wx.Size(50,25)) + self.selectionpen = wx.BitmapButton(self.panel, ID_SelectionPen, b, size=wx.Size(50,25)) + self.separatorcolour = wx.BitmapButton(self.panel, ID_SeparatorColour, b, size=wx.Size(50,25)) + + self.separatorspin = wx.SpinCtrl(self.panel, -1, "7", min=3, max=20, + style=wx.SP_ARROW_KEYS) + self.marginspin = wx.SpinCtrl(self.panel, -1, "6", min=3, max=20, + style=wx.SP_ARROW_KEYS) + self.paddingspin = wx.SpinCtrl(self.panel, -1, "6", min=3, max=20, + style=wx.SP_ARROW_KEYS) + self.borderspin = wx.SpinCtrl(self.panel, -1, "3", min=3, max=7, + style=wx.SP_ARROW_KEYS) + + self.__set_properties() + self.__do_layout() + + self.Bind(wx.EVT_RADIOBUTTON, self.OnDefaultStyle, self.defaultstyle) + self.Bind(wx.EVT_RADIOBUTTON, self.OnGradientStyle, self.gradientstyle) + self.Bind(wx.EVT_RADIOBUTTON, self.OnVerticalGradient, self.verticalgradient) + self.Bind(wx.EVT_RADIOBUTTON, self.OnHorizontalGradient, self.horizontalgradient) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, id=ID_BackgroundColour) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, id=ID_GradientFrom) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, id=ID_GradientTo) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, id=ID_BorderColour) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, id=ID_CaptionColour) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, id=ID_ButtonTextColour) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, id=ID_SelectionBrush) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, id=ID_SelectionPen) + self.Bind(wx.EVT_BUTTON, self.OnSetColour, id=ID_SeparatorColour) + + self.Bind(wx.EVT_SPINCTRL, self.OnSeparator, self.separatorspin) + self.Bind(wx.EVT_SPINCTRL, self.OnMargins, self.marginspin) + self.Bind(wx.EVT_SPINCTRL, self.OnPadding, self.paddingspin) + self.Bind(wx.EVT_SPINCTRL, self.OnBorder, self.borderspin) + + self.Bind(wx.EVT_CLOSE, self.OnClose) + + + def __set_properties(self): + + self.defaultstyle.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) + self.defaultstyle.SetValue(1) + self.gradientstyle.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) + self.verticalgradient.SetValue(1) + + if self.targetTitleBar.GetStyle() & bp.BP_DEFAULT_STYLE: + self.verticalgradient.Enable(False) + self.horizontalgradient.Enable(False) + self.defaultstyle.SetValue(1) + else: + self.gradientstyle.SetValue(1) + + self.borderspin.SetValue(self.targetTitleBar.GetBPArt().GetMetric(bp.BP_BORDER_SIZE)) + self.separatorspin.SetValue(self.targetTitleBar.GetBPArt().GetMetric(bp.BP_SEPARATOR_SIZE)) + self.marginspin.SetValue(self.targetTitleBar.GetBPArt().GetMetric(bp.BP_MARGINS_SIZE).x) + self.paddingspin.SetValue(self.targetTitleBar.GetBPArt().GetMetric(bp.BP_PADDING_SIZE).x) + + self.UpdateColors() + + + def __do_layout(self): + + mainsizer = wx.BoxSizer(wx.VERTICAL) + buttonsizer = wx.BoxSizer(wx.HORIZONTAL) + bottomsizer = wx.StaticBoxSizer(self.bottomsizer_staticbox, wx.VERTICAL) + sizer_13 = wx.BoxSizer(wx.HORIZONTAL) + sizer_12 = wx.BoxSizer(wx.HORIZONTAL) + sizer_11 = wx.BoxSizer(wx.HORIZONTAL) + sizer_10 = wx.BoxSizer(wx.HORIZONTAL) + coloursizer = wx.StaticBoxSizer(self.coloursizer_staticbox, wx.HORIZONTAL) + rightsizer = wx.BoxSizer(wx.VERTICAL) + sizer_9 = wx.BoxSizer(wx.HORIZONTAL) + sizer_8 = wx.BoxSizer(wx.HORIZONTAL) + sizer_7 = wx.BoxSizer(wx.HORIZONTAL) + sizer_6 = wx.BoxSizer(wx.HORIZONTAL) + leftsizer = wx.BoxSizer(wx.VERTICAL) + sizer_5 = wx.BoxSizer(wx.HORIZONTAL) + sizer_4 = wx.BoxSizer(wx.HORIZONTAL) + sizer_3 = wx.BoxSizer(wx.HORIZONTAL) + sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + sizer_1 = wx.BoxSizer(wx.HORIZONTAL) + stylesizer = wx.StaticBoxSizer(self.stylesizer_staticbox, wx.VERTICAL) + tophsizer = wx.BoxSizer(wx.HORIZONTAL) + tophsizer2 = wx.BoxSizer(wx.VERTICAL) + + stylesizer.Add(self.defaultstyle, 0, wx.ALL|wx.EXPAND|wx.ADJUST_MINSIZE, 5) + + tophsizer.Add(self.gradientstyle, 0, wx.LEFT|wx.RIGHT|wx.EXPAND| + wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + + tophsizer2.Add(self.verticalgradient, 0, wx.BOTTOM|wx.ADJUST_MINSIZE, 3) + tophsizer2.Add(self.horizontalgradient, 0, wx.ADJUST_MINSIZE, 0) + + tophsizer.Add(tophsizer2, 1, wx.LEFT|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 10) + + stylesizer.Add(tophsizer, 1, wx.EXPAND, 0) + + mainsizer.Add(stylesizer, 0, wx.ALL|wx.EXPAND, 5) + + label_1 = wx.StaticText(self.panel, -1, "Background Brush Colour:") + sizer_1.Add(label_1, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_1.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_1.Add(self.bakbrush, 0, wx.ADJUST_MINSIZE, 0) + + leftsizer.Add(sizer_1, 1, wx.EXPAND, 0) + + label_2 = wx.StaticText(self.panel, -1, "Gradient Colour From:") + sizer_2.Add(label_2, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_2.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_2.Add(self.gradientfrom, 0, wx.ADJUST_MINSIZE, 0) + + leftsizer.Add(sizer_2, 1, wx.EXPAND, 0) + + label_3 = wx.StaticText(self.panel, -1, "Gradient Colour To:") + sizer_3.Add(label_3, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_3.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_3.Add(self.gradientto, 0, wx.ADJUST_MINSIZE, 0) + + leftsizer.Add(sizer_3, 1, wx.EXPAND, 0) + + label_4 = wx.StaticText(self.panel, -1, "Border Colour:") + sizer_4.Add(label_4, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_4.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_4.Add(self.bordercolour, 0, wx.ADJUST_MINSIZE, 0) + + leftsizer.Add(sizer_4, 1, wx.EXPAND, 0) + + label_5 = wx.StaticText(self.panel, -1, "Main Caption Colour:") + sizer_5.Add(label_5, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_5.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_5.Add(self.captioncolour, 0, wx.ADJUST_MINSIZE, 0) + + leftsizer.Add(sizer_5, 1, wx.EXPAND, 0) + + coloursizer.Add(leftsizer, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) + coloursizer.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + + label_6 = wx.StaticText(self.panel, -1, "Text Button Colour:") + sizer_6.Add(label_6, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_6.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_6.Add(self.textbuttoncolour, 0, wx.ADJUST_MINSIZE, 0) + + rightsizer.Add(sizer_6, 1, wx.EXPAND, 0) + + label_7 = wx.StaticText(self.panel, -1, "Selection Brush Colour:") + sizer_7.Add(label_7, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_7.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_7.Add(self.selectionbrush, 0, wx.ADJUST_MINSIZE, 0) + + rightsizer.Add(sizer_7, 1, wx.EXPAND, 0) + + label_8 = wx.StaticText(self.panel, -1, "Selection Pen Colour:") + sizer_8.Add(label_8, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_8.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_8.Add(self.selectionpen, 0, wx.ADJUST_MINSIZE, 0) + + rightsizer.Add(sizer_8, 1, wx.EXPAND, 0) + + label_9 = wx.StaticText(self.panel, -1, "Separator Colour:") + sizer_9.Add(label_9, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_9.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_9.Add(self.separatorcolour, 0, wx.ADJUST_MINSIZE, 0) + + rightsizer.Add(sizer_9, 1, wx.EXPAND, 0) + + coloursizer.Add(rightsizer, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5) + + mainsizer.Add(coloursizer, 0, wx.ALL|wx.EXPAND, 5) + + label_10 = wx.StaticText(self.panel, -1, "Separator Size:") + sizer_10.Add(label_10, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_10.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_10.Add(self.separatorspin, 0, wx.ALL|wx.ADJUST_MINSIZE, 5) + + bottomsizer.Add(sizer_10, 1, wx.EXPAND, 0) + + label_11 = wx.StaticText(self.panel, -1, "Margins Size:") + sizer_11.Add(label_11, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_11.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_11.Add(self.marginspin, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ADJUST_MINSIZE, 5) + + bottomsizer.Add(sizer_11, 1, wx.EXPAND, 0) + + label_12 = wx.StaticText(self.panel, -1, "Padding Size:") + sizer_12.Add(label_12, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_12.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_12.Add(self.paddingspin, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ADJUST_MINSIZE, 5) + + bottomsizer.Add(sizer_12, 1, wx.EXPAND, 0) + + label_13 = wx.StaticText(self.panel, -1, "Border Size:") + sizer_13.Add(label_13, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + sizer_13.Add((0, 0), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0) + sizer_13.Add(self.borderspin, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ADJUST_MINSIZE, 5) + + bottomsizer.Add(sizer_13, 1, wx.EXPAND, 0) + + mainsizer.Add(bottomsizer, 0, wx.ALL|wx.EXPAND, 5) + + self.panel.SetSizer(mainsizer) + sizer = wx.BoxSizer() + sizer.Add(self.panel, 1, wx.EXPAND) + self.SetSizer(sizer) + self.Fit() + + + def CreateColorBitmap(self, c): + + image = wx.EmptyImage(25, 14) + + for x in xrange(25): + for y in xrange(14): + pixcol = c + if x == 0 or x == 24 or y == 0 or y == 13: + pixcol = wx.BLACK + + image.SetRGB(x, y, pixcol.Red(), pixcol.Green(), pixcol.Blue()) + + return image.ConvertToBitmap() + + + def UpdateColors(self): + + bk = self.targetTitleBar.GetBPArt().GetColor(bp.BP_BACKGROUND_COLOR) + self.bakbrush.SetBitmapLabel(self.CreateColorBitmap(bk)) + + capfrom = self.targetTitleBar.GetBPArt().GetColor(bp.BP_GRADIENT_COLOR_FROM) + self.gradientfrom.SetBitmapLabel(self.CreateColorBitmap(capfrom)) + + capto = self.targetTitleBar.GetBPArt().GetColor(bp.BP_GRADIENT_COLOR_TO) + self.gradientto.SetBitmapLabel(self.CreateColorBitmap(capto)) + + captxt = self.targetTitleBar.GetBPArt().GetColor(bp.BP_TEXT_COLOR) + self.captioncolour.SetBitmapLabel(self.CreateColorBitmap(captxt)) + + bor = self.targetTitleBar.GetBPArt().GetColor(bp.BP_BORDER_COLOR) + self.bordercolour.SetBitmapLabel(self.CreateColorBitmap(bor)) + + btntext = self.targetTitleBar.GetBPArt().GetColor(bp.BP_BUTTONTEXT_COLOR) + self.textbuttoncolour.SetBitmapLabel(self.CreateColorBitmap(btntext)) + + selb = self.targetTitleBar.GetBPArt().GetColor(bp.BP_SELECTION_BRUSH_COLOR) + self.selectionbrush.SetBitmapLabel(self.CreateColorBitmap(selb)) + + selp = self.targetTitleBar.GetBPArt().GetColor(bp.BP_SELECTION_PEN_COLOR) + self.selectionpen.SetBitmapLabel(self.CreateColorBitmap(selp)) + + sepc = self.targetTitleBar.GetBPArt().GetColor(bp.BP_SEPARATOR_COLOR) + self.separatorcolour.SetBitmapLabel(self.CreateColorBitmap(sepc)) + + + def OnDefaultStyle(self, event): + + self.verticalgradient.Enable(False) + self.horizontalgradient.Enable(False) + self.targetTitleBar.SetStyle(bp.BP_DEFAULT_STYLE) + + self.targetTitleBar.Refresh() + + event.Skip() + + + def OnGradientStyle(self, event): + + self.verticalgradient.Enable(True) + self.horizontalgradient.Enable(True) + self.targetTitleBar.SetStyle(bp.BP_USE_GRADIENT) + self.targetTitleBar.Refresh() + + event.Skip() + + + def OnVerticalGradient(self, event): + + self.targetTitleBar.GetBPArt().SetGradientType(bp.BP_GRADIENT_VERTICAL) + self.targetTitleBar.SetStyle(bp.BP_USE_GRADIENT) + self.targetTitleBar.Refresh() + + event.Skip() + + + def OnHorizontalGradient(self, event): + + self.targetTitleBar.GetBPArt().SetGradientType(bp.BP_GRADIENT_HORIZONTAL) + self.targetTitleBar.SetStyle(bp.BP_USE_GRADIENT) + self.targetTitleBar.Refresh() + + event.Skip() + + + def OnSetColour(self, event): + + dlg = wx.ColourDialog(self.parent) + + dlg.SetTitle("Color Picker") + + if dlg.ShowModal() != wx.ID_OK: + return + + var = 0 + if event.GetId() == ID_BackgroundColour: + var = bp.BP_BACKGROUND_COLOR + elif event.GetId() == ID_GradientFrom: + var = bp.BP_GRADIENT_COLOR_FROM + elif event.GetId() == ID_GradientTo: + var = bp.BP_GRADIENT_COLOR_TO + elif event.GetId() == ID_BorderColour: + var = bp.BP_BORDER_COLOR + elif event.GetId() == ID_CaptionColour: + var = bp.BP_TEXT_COLOR + elif event.GetId() == ID_ButtonTextColour: + var = bp.BP_BUTTONTEXT_COLOR + elif event.GetId() == ID_SelectionBrush: + var = bp.BP_SELECTION_BRUSH_COLOR + elif event.GetId() == ID_SelectionPen: + var = bp.BP_SELECTION_PEN_COLOR + elif event.GetId() == ID_SeparatorColour: + var = bp.BP_SEPARATOR_COLOR + else: + return + + self.targetTitleBar.GetBPArt().SetColor(var, dlg.GetColourData().GetColour()) + self.targetTitleBar.Refresh() + self.UpdateColors() + + self.parent.useredited = True + + + def OnSeparator(self, event): + + self.targetTitleBar.GetBPArt().SetMetric(bp.BP_SEPARATOR_SIZE, + event.GetInt()) + + self.targetTitleBar.DoLayout() + self.parent.mainPanel.Layout() + self.parent.useredited = True + + event.Skip() + + + def OnMargins(self, event): + + self.targetTitleBar.GetBPArt().SetMetric(bp.BP_MARGINS_SIZE, + wx.Size(event.GetInt(), event.GetInt())) + + self.targetTitleBar.DoLayout() + self.parent.mainPanel.Layout() + + self.parent.useredited = True + + event.Skip() + + + def OnPadding(self, event): + + self.targetTitleBar.GetBPArt().SetMetric(bp.BP_PADDING_SIZE, + wx.Size(event.GetInt(), event.GetInt())) + + self.targetTitleBar.DoLayout() + self.parent.mainPanel.Layout() + self.parent.useredited = True + + event.Skip() + + + def OnBorder(self, event): + + self.targetTitleBar.GetBPArt().SetMetric(bp.BP_BORDER_SIZE, + event.GetInt()) + + self.targetTitleBar.DoLayout() + self.parent.mainPanel.Layout() + + self.parent.useredited = True + + event.Skip() + + + def OnClose(self, event): + + self.parent.hassettingpanel = False + self.Destroy() + + + #---------------------------------------------------------------------- + class ButtonPanelDemo(wx.Frame): - def __init__(self, parent, id=wx.ID_ANY, title="ButtonPanel wxPython Demo", *args, **kw): + def __init__(self, parent, id=wx.ID_ANY, title="ButtonPanel wxPython Demo ;-)", + pos=wx.DefaultPosition, size=(640, 400), style=wx.DEFAULT_FRAME_STYLE): - wx.Frame.__init__(self, parent, id, title, *args, **kw) + wx.Frame.__init__(self, parent, id, title, pos, size, style) + self.useredited = False + self.hassettingpanel = False + + self.SetIcon(GetMondrianIcon()) self.CreateMenuBar() self.statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP) @@ -21,122 +486,367 @@ class ButtonPanelDemo(wx.Frame): for i in range(len(statusbar_fields)): self.statusbar.SetStatusText(statusbar_fields[i], i) - mainPanel = wx.Panel(self, -1) - self.logtext = wx.TextCtrl(mainPanel, -1, "", size=(-1, 250), - style=wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2) + self.mainPanel = wx.Panel(self, -1) + self.logtext = wx.TextCtrl(self.mainPanel, -1, "", style=wx.TE_MULTILINE|wx.TE_READONLY) vSizer = wx.BoxSizer(wx.VERTICAL) - mainPanel.SetSizer(vSizer) - - # Create the buttons and place them on the - # tab area of the main book - alignment = bp.BP_ALIGN_RIGHT - self.titleBar = bp.ButtonPanel(mainPanel, -1, "A Simple Test & Demo", - alignment=alignment) - - self.btn1bmp = images.get_bp_btn1Bitmap() - self.btn2bmp = images.get_bp_btn2Bitmap() - self.btn3bmp = images.get_bp_btn3Bitmap() - self.btn4bmp = images.get_bp_btn4Bitmap() - self.CreateButtons(alignment) - - # set the color the text is drawn with - self.titleBar.SetColor(bp.BP_TEXT_COLOR, wx.WHITE) - - # These default to white and whatever is set in the system - # settings for the wx.SYS_COLOUR_ACTIVECAPTION. We'll use - # some specific settings to ensure a consistent look for the - # demo. - self.titleBar.SetColor(bp.BP_CAPTION_BORDER_COLOR, wx.Colour(120,23,224)) - self.titleBar.SetColor(bp.BP_CAPTION_COLOR, wx.Colour(60,11,112)) - self.titleBar.SetColor(bp.BP_CAPTION_GRADIENT_COLOR, wx.Colour(120,23,224)) - - vSizer.Add(self.titleBar, 0, wx.EXPAND) - vSizer.Add(self.logtext, 1, wx.EXPAND|wx.ALL, 5) - vSizer.Layout() + self.mainPanel.SetSizer(vSizer) - sizer = wx.BoxSizer() - sizer.Add(mainPanel, 1, wx.EXPAND) - self.SetSizer(sizer) - self.Fit() + self.alignments = [bp.BP_ALIGN_LEFT, bp.BP_ALIGN_RIGHT, bp.BP_ALIGN_TOP, bp.BP_ALIGN_BOTTOM] + self.alignment = bp.BP_ALIGN_LEFT + self.style = bp.BP_USE_GRADIENT + + self.titleBar = bp.ButtonPanel(self.mainPanel, -1, "A Simple Test & Demo", + style=self.style, alignment=self.alignment) + + self.created = False + self.pngs = [ (images.get_bp_btn1Bitmap(), 'label1'), + (images.get_bp_btn2Bitmap(), 'label2'), + (images.get_bp_btn3Bitmap(), 'label3'), + (images.get_bp_btn4Bitmap(), 'label4'), + ] + self.CreateButtons() + self.SetProperties() + def CreateMenuBar(self): + mb = wx.MenuBar() file_menu = wx.Menu() + item = wx.MenuItem(file_menu, wx.ID_ANY, "&Quit") file_menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnClose, item) edit_menu = wx.Menu() - item = wx.MenuItem(edit_menu, wx.ID_ANY, "BP_ALIGN_RIGHT", kind=wx.ITEM_RADIO) + + item = wx.MenuItem(edit_menu, wx.ID_ANY, "Set Bar Text") edit_menu.AppendItem(item) - self.Bind(wx.EVT_MENU, self.OnAlignRight, item) - item = wx.MenuItem(edit_menu, wx.ID_ANY, "BP_ALIGN_LEFT", kind=wx.ITEM_RADIO) + self.Bind(wx.EVT_MENU, self.OnSetBarText, item) + + edit_menu.AppendSeparator() + + submenu = wx.Menu() + + item = wx.MenuItem(submenu, wx.ID_ANY, "BP_ALIGN_LEFT", kind=wx.ITEM_RADIO) + submenu.AppendItem(item) + item.Check(True) + self.Bind(wx.EVT_MENU, self.OnAlignment, item) + + item = wx.MenuItem(submenu, wx.ID_ANY, "BP_ALIGN_RIGHT", kind=wx.ITEM_RADIO) + submenu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnAlignment, item) + + item = wx.MenuItem(submenu, wx.ID_ANY, "BP_ALIGN_TOP", kind=wx.ITEM_RADIO) + submenu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnAlignment, item) + + item = wx.MenuItem(submenu, wx.ID_ANY, "BP_ALIGN_BOTTOM", kind=wx.ITEM_RADIO) + submenu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnAlignment, item) + + edit_menu.AppendMenu(wx.ID_ANY, "&Alignment", submenu) + + submenu = wx.Menu() + + item = wx.MenuItem(submenu, wx.ID_ANY, "Default Style", kind=wx.ITEM_RADIO) + submenu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnDefaultStyle, item) + + item = wx.MenuItem(submenu, wx.ID_ANY, "Gradient Style", kind=wx.ITEM_RADIO) + submenu.AppendItem(item) + item.Check(True) + self.Bind(wx.EVT_MENU, self.OnGradientStyle, item) + + edit_menu.AppendMenu(wx.ID_ANY, "&Styles", submenu) + + edit_menu.AppendSeparator() + + item = wx.MenuItem(submenu, wx.ID_ANY, "Settings Panel") edit_menu.AppendItem(item) - self.Bind(wx.EVT_MENU, self.OnAlignLeft, item) + self.Bind(wx.EVT_MENU, self.OnSettingsPanel, item) + + demo_menu = wx.Menu() + + item = wx.MenuItem(demo_menu, wx.ID_ANY, "Default Demo", kind=wx.ITEM_RADIO) + demo_menu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnDefaultDemo, item) + item = wx.MenuItem(demo_menu, wx.ID_ANY, "Button Only Demo", kind=wx.ITEM_RADIO) + demo_menu.AppendItem(item) + self.Bind(wx.EVT_MENU, self.OnButtonOnly, item) + help_menu = wx.Menu() + item = wx.MenuItem(help_menu, wx.ID_ANY, "&About...") help_menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnAbout, item) mb.Append(file_menu, "&File") mb.Append(edit_menu, "&Edit") + mb.Append(demo_menu, "&Demo") mb.Append(help_menu, "&Help") self.SetMenuBar(mb) - def CreateButtons(self, alignment=bp.BP_ALIGN_RIGHT): - self.buttons = [] + def CreateButtons(self): + + # Here we (re)create the buttons for the default startup demo self.Freeze() + + if self.created: + sizer = self.mainPanel.GetSizer() + sizer.Detach(0) + self.titleBar.Hide() + wx.CallAfter(self.titleBar.Destroy) + self.titleBar = bp.ButtonPanel(self.mainPanel, -1, "A Simple Test & Demo", + style=self.style, alignment=self.alignment) + self.SetProperties() + + self.indices = [] + for count, png in enumerate(self.pngs): + + if count < 2: + # First 2 buttons are togglebuttons + kind = wx.ITEM_CHECK + else: + kind = wx.ITEM_NORMAL + + btn = bp.ButtonInfo(self.titleBar, wx.NewId(), + png[0], + kind=kind) + + self.titleBar.AddButton(btn) + self.Bind(wx.EVT_BUTTON, self.OnButton, id=btn.GetId()) + + self.indices.append(btn.GetId()) + + if count < 2: + # First 2 buttons have also a text + btn.SetText(png[1]) + + if count == 2: + # Append a separator after the second button + self.titleBar.AddSeparator() + + if count == 1: + # Add a wx.TextCtrl to ButtonPanel + self.titleBar.AddControl(wx.TextCtrl(self.titleBar, -1, "Hello wxPython!")) + btn.SetTextAlignment(bp.BP_BUTTONTEXT_ALIGN_RIGHT) + + # Add a wx.Choice to ButtonPanel + self.titleBar.AddControl(wx.Choice(self.titleBar, -1, + choices=["Hello", "From", "wxPython!"])) + + self.strings = ["First", "Second", "Third", "Fourth"] + + self.ChangeLayout() + self.Thaw() + self.titleBar.DoLayout() + + self.created = True - self.titleBar.RemoveAllButtons() - bmps = [ self.btn1bmp, - self.btn2bmp, - self.btn3bmp, - self.btn4bmp, - ] - if alignment == bp.BP_ALIGN_LEFT: - bmps.reverse() + def ButtonOnly(self): + + # Here we (re)create the buttons for the button-only demo + self.Freeze() + + if self.created: + sizer = self.mainPanel.GetSizer() + sizer.Detach(0) + self.titleBar.Destroy() + self.titleBar = bp.ButtonPanel(self.mainPanel, -1, "A Simple Test & Demo", + style=self.style, alignment=self.alignment) + self.SetProperties() + + # Buttons are created completely random, with random images, toggle behavior + # and text + + self.indices = [] + for count in xrange(8): + + itemImage = random.randint(0, 3) + hasText = random.randint(0, 1) + itemKind = random.randint(0, 1) + + btn = bp.ButtonInfo(self.titleBar, wx.NewId(), self.pngs[itemImage][0], + kind=itemKind) + + if hasText: + btn.SetText(self.pngs[itemImage][1]) + rightText = random.randint(0, 1) + if rightText: + btn.SetTextAlignment(bp.BP_BUTTONTEXT_ALIGN_RIGHT) - for bmp in bmps: - btn = bp.ButtonInfo(wx.NewId(), bmp) self.titleBar.AddButton(btn) - self.Bind(wx.EVT_BUTTON, self.OnButton, btn) - self.buttons.append(btn.GetId()) + self.Bind(wx.EVT_BUTTON, self.OnButton, id=btn.GetId()) + + self.indices.append(btn.GetId()) - self.strings = ["First", "Second", "Third", "Fourth"] - if alignment == bp.BP_ALIGN_RIGHT: - self.strings.reverse() + if count in [0, 3, 5]: + self.titleBar.AddSeparator() + + self.strings = ["First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth"] - self.titleBar.SetAlignment(alignment) + self.ChangeLayout() self.Thaw() + self.titleBar.DoLayout() + + def ChangeLayout(self): - def OnAlignLeft(self, event): - self.CreateButtons(alignment=bp.BP_ALIGN_LEFT) - event.Skip() + # Change the layout after a switch in ButtonPanel alignment + self.Freeze() + + if self.alignment in [bp.BP_ALIGN_LEFT, bp.BP_ALIGN_RIGHT]: + vSizer = wx.BoxSizer(wx.VERTICAL) + else: + vSizer = wx.BoxSizer(wx.HORIZONTAL) + self.mainPanel.SetSizer(vSizer) + + vSizer.Add(self.titleBar, 0, wx.EXPAND) + vSizer.Add((20, 20)) + vSizer.Add(self.logtext, 1, wx.EXPAND|wx.ALL, 5) + + vSizer.Layout() + self.mainPanel.Layout() + self.Thaw() + + + def SetProperties(self): + + # No resetting if the user is using the Settings Panel + if self.useredited: + return + + # Sets the colours for the two demos: called only if the user didn't + # modify the colours and sizes using the Settings Panel + bpArt = self.titleBar.GetBPArt() + + if self.style & bp.BP_USE_GRADIENT: + # set the color the text is drawn with + bpArt.SetColor(bp.BP_TEXT_COLOR, wx.WHITE) + + # These default to white and whatever is set in the system + # settings for the wx.SYS_COLOUR_ACTIVECAPTION. We'll use + # some specific settings to ensure a consistent look for the + # demo. + bpArt.SetColor(bp.BP_BORDER_COLOR, wx.Colour(120,23,224)) + bpArt.SetColor(bp.BP_GRADIENT_COLOR_FROM, wx.Colour(60,11,112)) + bpArt.SetColor(bp.BP_GRADIENT_COLOR_TO, wx.Colour(120,23,224)) + bpArt.SetColor(bp.BP_BUTTONTEXT_COLOR, wx.Colour(70,143,255)) + bpArt.SetColor(bp.BP_SEPARATOR_COLOR, + bp.BrightenColour(wx.Colour(60, 11, 112), 0.85)) + bpArt.SetColor(bp.BP_SELECTION_BRUSH_COLOR, wx.Color(225, 225, 255)) + bpArt.SetColor(bp.BP_SELECTION_PEN_COLOR, wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)) + + else: + + background = self.titleBar.GetBackgroundColour() + bpArt.SetColor(bp.BP_TEXT_COLOR, wx.BLUE) + bpArt.SetColor(bp.BP_BORDER_COLOR, + bp.BrightenColour(background, 0.85)) + bpArt.SetColor(bp.BP_SEPARATOR_COLOR, + bp.BrightenColour(background, 0.85)) + bpArt.SetColor(bp.BP_BUTTONTEXT_COLOR, wx.BLACK) + bpArt.SetColor(bp.BP_SELECTION_BRUSH_COLOR, wx.Colour(242, 242, 235)) + bpArt.SetColor(bp.BP_SELECTION_PEN_COLOR, wx.Colour(206, 206, 195)) + + self.titleBar.SetStyle(self.style) + + + def OnAlignment(self, event): + + # Here we change the alignment property of ButtonPanel + current = event.GetId() + edit_menu = self.GetMenuBar().FindMenu("Edit") + edit_menu = self.GetMenuBar().GetMenu(edit_menu) + menu = edit_menu.FindItem("BP_ALIGN_LEFT") + + alignment = self.alignments[current - menu] + self.alignment = alignment - def OnAlignRight(self, event): - self.CreateButtons(alignment=bp.BP_ALIGN_RIGHT) + self.ChangeLayout() + self.titleBar.SetAlignment(alignment) + self.mainPanel.Layout() + event.Skip() + def OnDefaultStyle(self, event): + + # Restore the ButtonPanel default style (no gradient) + self.style = bp.BP_DEFAULT_STYLE + self.SetProperties() + + event.Skip() + + + def OnGradientStyle(self, event): + + # Use gradients to paint ButtonPanel background + self.style = bp.BP_USE_GRADIENT + self.SetProperties() + + event.Skip() + + + def OnDefaultDemo(self, event): + + # Reload the default startup demo + self.CreateButtons() + event.Skip() + + + def OnButtonOnly(self, event): + + # Reload the button-only demo + self.ButtonOnly() + event.Skip() + + def OnButton(self, event): + btn = event.GetId() - indx = self.buttons.index(btn) + indx = self.indices.index(btn) + self.logtext.AppendText("Event Fired From " + self.strings[indx] + " Button\n") event.Skip() + def OnSetBarText(self, event): + + dlg = wx.TextEntryDialog(self, "Enter The Text You Wish To Display On The Bar (Clear If No Text):", + "Set Text", self.titleBar.GetBarText()) + + if dlg.ShowModal() == wx.ID_OK: + + val = dlg.GetValue() + self.titleBar.SetBarText(val) + self.titleBar.DoLayout() + self.mainPanel.Layout() + + + def OnSettingsPanel(self, event): + + if self.hassettingpanel: + self.settingspanel.Raise() + return + + self.settingspanel = SettingsPanel(self, -1) + self.settingspanel.Show() + self.hassettingpanel = True + + def OnClose(self, event): + self.Destroy() - event.Skip() + event.Skip() def OnAbout(self, event): @@ -154,6 +864,8 @@ class ButtonPanelDemo(wx.Frame): dlg.ShowModal() dlg.Destroy() + + #---------------------------------------------------------------------- class TestPanel(wx.Panel): diff --git a/wxPython/wx/lib/buttonpanel.py b/wxPython/wx/lib/buttonpanel.py index 0b3a26a8b1..dadf04b861 100644 --- a/wxPython/wx/lib/buttonpanel.py +++ b/wxPython/wx/lib/buttonpanel.py @@ -11,7 +11,7 @@ # Python Code By: # # Andrea Gavana, @ 02 Oct 2006 -# Latest Revision: 02 Oct 2006, 17.00 GMT +# Latest Revision: 16 Oct 2006, 17.00 GMT # # # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please @@ -81,8 +81,6 @@ inside a very simple frame:: titleBar.AddButton(btn4) self.Bind(wx.EVT_BUTTON, self.OnButton, btn4) - titleBar.SetColor(BP_TEXT_COLOR, wx.WHITE) - titleBar.SetColor(BP_CAPTION_BORDER_COLOR, wx.WHITE) vSizer.Add(titleBar, 0, wx.EXPAND) vSizer.Add((20, 20)) vSizer.Add(self.logtext, 1, wx.EXPAND|wx.ALL, 5) @@ -104,38 +102,89 @@ License And Version: ButtonPanel Is Freeware And Distributed Under The wxPython License. -Latest Revision: Andrea Gavana @ 02 Oct 2006, 17.00 GMT -Version 0.1. +Latest Revision: Andrea Gavana @ 12 Oct 2006, 17.00 GMT +Version 0.3. """ import wx -# Some constants -BP_CAPTION_COLOR = 0, -BP_CAPTION_GRADIENT_COLOR = 1 -BP_CAPTION_BORDER_COLOR = 2 -BP_TEXT_COLOR = 3 - -# Buttons states -BP_BTN_NONE = 100 -BP_BTN_PRESSED = 101 -BP_BTN_HOVER = 102 +# 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_RIGHT = 1 +BP_ALIGN_LEFT = 2 +BP_ALIGN_TOP = 4 +BP_ALIGN_BOTTOM = 8 + +# ButtonPanel styles +BP_DEFAULT_STYLE = 1 +BP_USE_GRADIENT = 2 -# ButtonPanel default style -BP_DEFAULT_STYLE = 2 + +# 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 BrightenColor(color, factor): +def BrightenColour(color, factor): """ Bright the input colour by a factor.""" val = color.Red()*factor @@ -159,34 +208,803 @@ def BrightenColor(color, factor): 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.width) + + rf += rstep + gf += gstep + bf += bstep + + +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: +class ButtonInfo(Control): - def __init__(self, id=wx.ID_ANY, bmp=wx.NullBitmap, status=-1): + def __init__(self, parent, id=wx.ID_ANY, bmp=wx.NullBitmap, + status="Normal", text="", kind=wx.ITEM_NORMAL): """ Default class constructor. Parameters: + - parent: the parent window (ButtonPanel); - id: the button id; - bmp: the associated bitmap; - status: button status (pressed, hovered, None). + - text to be displayed either below of to the right of the button """ + if id == wx.ID_ANY: id = wx.NewId() - self._id = id - self._bmp = bmp + self._status = status self._rect = wx.Rect() + self._text = text + self._kind = kind + self._toggle = False + self._textAlignment = BP_BUTTONTEXT_ALIGN_BOTTOM + + disabledbmp = GrayOut(bmp) + + self._bitmaps = {"Normal": bmp, "Toggled": None, "Disabled": disabledbmp, + "Hover": None, "Pressed": None} + + Control.__init__(self, parent) - def GetBitmap(self): + 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.Refresh() + + + 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. """ - return self._bmp + 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): @@ -211,18 +1029,52 @@ class ButtonInfo: """ Sets the button rect. """ self._rect = rect - - - def SetBitmap(self, bmp): - """ Sets the associated bitmap. """ - - self._bmp = bmp 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.Refresh() + + + 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._alignment = 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): @@ -230,21 +1082,76 @@ class ButtonInfo: 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 + + 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_RIGHT, name="buttonPanel"): + alignment=BP_ALIGN_LEFT, name="buttonPanel"): """ Default class constructor. @@ -258,22 +1165,31 @@ class ButtonPanel(wx.PyPanel): wx.PyPanel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER, name=name) + self._vButtons = [] + self._vSeparators = [] - self._text = text self._nStyle = style self._alignment = alignment - self._nPadding = 6 - self._nBmpSize = 16 - self._colorFrom = wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION) - self._colorTo = wx.WHITE - self._colorBorder = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE) - self._colorText = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) - self._firsttime = True - self._borderPenWidth = 3 + + 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_SIZE, self.OnSize) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_MOTION, self.OnMouseMove) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) @@ -281,263 +1197,478 @@ class ButtonPanel(wx.PyPanel): 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) - self.Refresh() - + + + 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 = [] - self.Refresh() + + def RemoveAllSeparators(self): + """ Remove all the separators from ButtonPanel. """ + self._vSeparators = [] + + def GetAlignment(self): - """ Returns the button alignment (left, right). """ + """ Returns the button alignment (left, right, top, bottom). """ return self._alignment def SetAlignment(self, alignment): - """ Sets the button alignment (left, right). """ + """ Sets the button alignment (left, right, top, bottom). """ - self._alignment = alignment + if alignment == self._alignment: + return + self.Freeze() + + text = self.GetBarText() + + # Remove the text in any case + self.RemoveText() - def DoGetBestSize(self): - w = h = 0 - if self._text: - dc = wx.ClientDC(self) - boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) - boldFont.SetWeight(wx.FONTWEIGHT_BOLD) - dc.SetFont(boldFont) - tw, th = dc.GetTextExtent(self._text) - h = max(h, th) - w += tw + self._nPadding - - if self._vButtons: - bh = self._vButtons[0].GetBitmap().GetHeight() - bw = self._vButtons[0].GetBitmap().GetWidth() - - bh += 2*self._nPadding + 2*self._borderPenWidth - h = max(h, bh) + # Remove the first and last spacers + self._mainsizer.Remove(0, -1) + self._mainsizer.Remove(len(self._mainsizer.GetChildren())-1, -1) + + self._alignment = alignment - bw = (len(self._vButtons)+1) * (bw + 2*self._nPadding) - w += bw - - return (w, h) + # Recreate the sizer accordingly to the new alignment + self.ReCreateSizer(text) + def IsVertical(self): + """ Returns whether ButtonPanel is vertically aligned or not. """ - def OnPaint(self, event): - """ Handles the wx.EVT_PAINT event for ButtonPanel. """ + return self._alignment not in [BP_ALIGN_RIGHT, BP_ALIGN_LEFT] - dc = wx.BufferedPaintDC(self) - rect = self.GetClientRect() - ##print rect, self.GetRect(), self.GetBestSize(), self.GetMinSize() + 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() - # Draw gradient color in the background of the panel - self.FillGradientColor(dc, rect) - backBrush = wx.TRANSPARENT_BRUSH - borderPen = wx.Pen(self._colorBorder) - size = self.GetSize() - borderPen.SetWidth(self._borderPenWidth) + def ReCreateSizer(self, text): + """ Recreates the ButtonPanel sizer accordingly to the alignment specified. """ + + children = self._mainsizer.GetChildren() + self.RemoveAllButtons() + self.RemoveAllSeparators() - # Draw a rectangle around the panel - dc.SetBrush(backBrush) - dc.SetPen(borderPen) - dc.DrawRectangleRect(rect) + # Create a new sizer depending on the alignment chosen + direction = (self.IsVertical() and [wx.VERTICAL] or [wx.HORIZONTAL])[0] + self._mainsizer = BoxSizer(direction) - # Draw the text - textWidth, textHeight = 0, 0 + 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() - if self._text != "": - - dc.SetTextForeground(self._colorText) - borderPen.SetWidth(2) - boldFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) - boldFont.SetWeight(wx.FONTWEIGHT_BOLD) - dc.SetFont(boldFont) - - textWidth, textHeight = dc.GetTextExtent(self._text) - if self._alignment == BP_ALIGN_RIGHT: - textX = self._nPadding + 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: - textX = rect.width - textWidth - self._nPadding - - textY = (rect.GetHeight() - textHeight)/2 - dc.DrawText(self._text, textX, textY) - - if self._vButtons: - - height = self._vButtons[0].GetBitmap().GetHeight() - self._nBmpSize = self._vButtons[0].GetBitmap().GetWidth() - height += 2*self._nPadding + 2*self._borderPenWidth + w += padding.x + else: + w = h = border - if self._firsttime: # this probably isn't needed anymore now that DoGetBestSize is implemented... - self.GetContainingSizer().Layout() - self._firsttime = False - - # Draw all buttons - # [ Padding | Text | .. Buttons .. | Padding ] + # Add the button's sizes + for btn in self._vButtons: - totalWidth = rect.width - self._nPadding*2 - textWidth + bw, bh = btn.GetBestSize() + btnWidth = max(btnWidth, bw) + btnHeight = max(btnHeight, bh) - # The button is drawn inside a circle with padding of self._nPadding around it - # so the width of each image = imageWidth + 2 * self._nPadding - nImageWidth = self._nBmpSize + 2*self._nPadding + 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) - if self._alignment == BP_ALIGN_RIGHT: + # 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 - leftEndX = self._nPadding + textWidth - posx = rect.width - nImageWidth - self._nPadding - - for ii in xrange(len(self._vButtons)): - - # Make sure we can keep drawing - if posx < leftEndX: - break - - # Draw a rectangle around the buttons - if self._vButtons[ii].GetStatus() == BP_BTN_HOVER: - - dc.SetBrush(wx.Brush(wx.Color(225, 225, 255))) - dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION))) - dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth) - dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True) - - elif self._vButtons[ii].GetStatus() == BP_BTN_PRESSED: - - dc.SetBrush(wx.Brush(wx.Color(225, 225, 255))) - dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION))) - dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth) - dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth + 1, True) - - else: - - dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True) - - self._vButtons[ii].SetRect(wx.Rect(posx, self._borderPenWidth, nImageWidth, nImageWidth)) - posx -= nImageWidth + return wx.Size(w, h) - else: - rightStartX = textX - self._nPadding - nImageWidth - posx = self._nPadding - - for ii in xrange(len(self._vButtons)): - - # Make sure we can keep drawing - if posx > rightStartX: - break - - # Draw a rectangle around the buttons - if self._vButtons[ii].GetStatus() == BP_BTN_HOVER: - - dc.SetBrush(wx.Brush(wx.Color(225, 225, 255))) - dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION))) - dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth) - dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True) - - elif self._vButtons[ii].GetStatus() == BP_BTN_PRESSED: - - dc.SetBrush(wx.Brush(wx.Color(225, 225, 255))) - dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION))) - dc.DrawRectangle(posx, self._borderPenWidth, nImageWidth, nImageWidth) - dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth + 1, True) - - else: - - dc.DrawBitmap(self._vButtons[ii].GetBitmap(), posx + self._nPadding, self._nPadding + self._borderPenWidth, True) - - self._vButtons[ii].SetRect(wx.Rect(posx, self._borderPenWidth, nImageWidth, nImageWidth)) - posx += nImageWidth - - # Update all other buttons that they are not drawn (by setting the rect to 0) - for cur in xrange(ii+1, len(self._vButtons)): - self._vButtons[cur].SetRect(wx.Rect(0, 0, 0, 0)) + 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. """ - - self.Refresh() + + # 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 SetColor(self, switch, color): + + def LayoutItems(self): + """ + Layout the items using a different algorithm depending on the existance + of the main caption. """ - Sets the color depending on switch: - - BP_CAPTION_COLOR: caption color; - - BP_CAPTION_GRADIENT_COLOR: gradient color; - - BP_CAPTION_BORDER_COLOR; border color; - - BP_TEXT_COLOR: text color. - """ - - if switch == BP_CAPTION_COLOR: - self._colorFrom = color - elif switch == BP_CAPTION_GRADIENT_COLOR: - self._colorTo = color - elif switch == BP_CAPTION_BORDER_COLOR: - self._colorBorder = color - elif switch == BP_TEXT_COLOR: - self._colorText = color - - - def FillGradientColor(self, dc, rect): - """ Gradient fill from colour 1 to colour 2 with top to bottom. """ - if rect.height < 1 or rect.width < 1: - return + nonspacers, allchildren = self.GetNonFlexibleChildren() + + if self.HasBarText(): + self.FlexibleLayout(nonspacers, allchildren) + else: + self.SizeLayout(nonspacers, allchildren) + + self._mainsizer.Layout() - size = rect.height - # calculate gradient coefficients - style = self.GetParent().GetWindowStyleFlag() - col2 = self._colorFrom - col1 = self._colorTo + def SizeLayout(self, nonspacers, children): + """ Layout the items when no main caption exists. """ - 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) + 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) + - for y in xrange(rect.y, rect.y + 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)) - dc.DrawLine(rect.x, y, rect.x + rect.width, y) - rf += rstep - gf += gstep - bf += bstep + 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 - for ii in xrange(len(self._vButtons)): - if self._vButtons[ii].GetRect().Inside(event.GetPosition()): - self._vButtons[ii].SetStatus(BP_BTN_HOVER) + for btn in self._vButtons: + + if not btn.IsEnabled(): + continue + + if btn.GetRect().Contains(event.GetPosition()): + btn.SetStatus("Hover") else: - self._vButtons[ii].SetStatus(BP_BTN_NONE) + btn.SetStatus("Normal") self.Refresh() event.Skip() @@ -548,34 +1679,49 @@ class ButtonPanel(wx.PyPanel): tabId, hit = self.HitTest(event.GetPosition()) - if hit == BP_HT_BUTTON: - - self._vButtons[tabId].SetStatus(BP_BTN_PRESSED) + if hit == BP_HT_BUTTON and self._vButtons[tabId].IsEnabled(): + + self._vButtons[tabId].SetStatus("Pressed") self.Refresh() def OnLeftUp(self, event): """ Handles the wx.EVT_LEFT_UP event for ButtonPanel. """ - tabId, hit = self.HitTest(event.GetPosition()) + tabId, flags = self.HitTest(event.GetPosition()) + hit = self._vButtons[tabId] - if hit == BP_HT_BUTTON: - if self._vButtons[tabId].GetStatus() == BP_BTN_PRESSED: + if flags == BP_HT_BUTTON: + + 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, self._vButtons[tabId].GetId()) + 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 - self._vButtons[tabId].SetStatus(BP_BTN_HOVER) + hit.SetStatus("Hover") + hit.SetFocus() + self.Refresh() def OnMouseLeave(self, event): """ Handles the wx.EVT_LEAVE_WINDOW event for ButtonPanel. """ - # Reset all buttons statuses - for ii in xrange(len(self._vButtons)): - self._vButtons[ii].SetStatus(BP_BTN_NONE) + # Reset all buttons statuses + for btn in self._vButtons: + if not btn.IsEnabled(): + continue + btn.SetStatus("Normal") self.Refresh() event.Skip() @@ -596,10 +1742,16 @@ class ButtonPanel(wx.PyPanel): btnIdx = -1 for ii in xrange(len(self._vButtons)): - if self._vButtons[ii].GetRect().Inside(pt): + 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 + -- 2.45.2