Poodletooth-iLand/panda/python/Lib/site-packages/wx/lib/buttons.py
2015-03-06 06:11:40 -06:00

1112 lines
33 KiB
Python

#----------------------------------------------------------------------
# Name: wx.lib.buttons
# Purpose: Various kinds of generic buttons, (not native controls but
# self-drawn.)
#
# Author: Robin Dunn
#
# Created: 9-Dec-1999
# Copyright: (c) 1999 by Total Control Software
# Licence: wxWindows license
# Tags: phoenix-port, unittest, documented
#----------------------------------------------------------------------
# 11/30/2003 - Jeff Grimmett (grimmtooth@softhome.net)
#
# o Updated for wx namespace
# o Tested with updated demo
#
"""
This module implements various forms of generic buttons, meaning that
they are not built on native controls but are self-drawn.
Description
===========
This module implements various forms of generic buttons, meaning that
they are not built on native controls but are self-drawn.
They act like normal buttons but you are able to better control how they look,
bevel width, colours, etc...
Usage
=====
Sample usage::
import wx
import wx.lib.buttons as buttons
class MyFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, wx.ID_ANY, title, size=(400, 300))
panel = wx.Panel(self)
# Build a bitmap button and a normal one
bmp = wx.ArtProvider.GetBitmap(wx.ART_INFORMATION, wx.ART_OTHER, (16, 16))
btn1 = buttons.ThemedGenBitmapButton(panel, -1, bmp, pos=(50, 50))
btn2 = buttons.GenButton(panel, -1, "Hello World!", pos=(50, 100))
app = wx.App()
frame = MyFrame(None, 'wx.lib.buttons Test')
frame.Show()
app.MainLoop()
"""
import wx
import wx.lib.imageutils as imageutils
#----------------------------------------------------------------------
class GenButtonEvent(wx.CommandEvent):
""" Event sent from the generic buttons when the button is activated. """
def __init__(self, eventType, id):
"""
Default class constructor.
:param integer `eventType`: the event type;
:param integer `id`: the event identifier.
"""
wx.CommandEvent.__init__(self, eventType, id)
self.isDown = False
self.theButton = None
def SetIsDown(self, isDown):
"""
Set the button toggle status as 'down' or 'up'.
:param bool `isDown`: ``True`` if the button is clicked, ``False`` otherwise.
"""
self.isDown = isDown
def GetIsDown(self):
"""
Returns the button toggle status as ``True`` if the button is down, ``False``
otherwise.
:rtype: bool
"""
return self.isDown
def SetButtonObj(self, btn):
"""
Sets the event object for the event.
:param `btn`: the button object, an instance of :class:`GenButton`.
"""
self.theButton = btn
def GetButtonObj(self):
"""
Returns the object associated with this event.
:return: An instance of :class:`GenButton`.
"""
return self.theButton
#----------------------------------------------------------------------
class GenButton(wx.Control):
""" A generic button, and base class for the other generic buttons. """
labelDelta = 1
def __init__(self, parent, id=-1, label='',
pos = wx.DefaultPosition, size = wx.DefaultSize,
style = 0, validator = wx.DefaultValidator,
name = "genbutton"):
"""
Default class constructor.
:param Window `parent`: parent window. Must not be ``None``;
:param integer `id`: window identifier. A value of -1 indicates a default value;
:param string `label`: the button text label;
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
chosen by either the windowing system or wxPython, depending on platform;
:type `pos`: tuple or :class:`Point`
:param `size`: the control size. A value of (-1, -1) indicates a default size,
chosen by either the windowing system or wxPython, depending on platform;
:type `size`: tuple or :class:`Size`
:param integer `style`: the button style;
:param Validator `validator`: the validator associated with the button;
:param string `name`: the button name.
.. seealso:: :class:`Button` for a list of valid window styles.
"""
cstyle = style
if cstyle & wx.BORDER_MASK == 0:
cstyle |= wx.BORDER_NONE
wx.Control.__init__(self, parent, id, pos, size, cstyle, validator, name)
self.up = True
self.hasFocus = False
self.style = style
if style & wx.BORDER_NONE:
self.bezelWidth = 0
self.useFocusInd = False
else:
self.bezelWidth = 2
self.useFocusInd = True
self.SetLabel(label)
self.InheritAttributes()
self.SetInitialSize(size)
self.InitColours()
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDown)
self.Bind(wx.EVT_MOTION, self.OnMotion)
self.Bind(wx.EVT_SET_FOCUS, self.OnGainFocus)
self.Bind(wx.EVT_KILL_FOCUS, self.OnLoseFocus)
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, lambda evt: None)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.InitOtherEvents()
def InitOtherEvents(self):
"""
Override this method in a subclass to initialize any other events that
need to be bound. Added so :meth:`__init__` doesn't need to be
overriden, which is complicated with multiple inheritance.
"""
pass
def SetInitialSize(self, size=None):
"""
Given the current font and bezel width settings, calculate
and set a good size.
:param `size`: an instance of :class:`Size` or ``None``, in which case the wxPython
``DefaultSize`` is used instead.
"""
if size is None:
size = wx.DefaultSize
wx.Control.SetInitialSize(self, size)
SetBestSize = SetInitialSize
def DoGetBestSize(self):
"""
Overridden base class virtual. Determines the best size of the
button based on the label and bezel size.
:return: An instance of :class:`Size`.
.. note:: Overridden from :class:`Control`.
"""
w, h, useMin = self._GetLabelSize()
if self.style & wx.BU_EXACTFIT:
width = w + 2 + 2 * self.bezelWidth + 4 * int(self.useFocusInd)
height = h + 2 + 2 * self.bezelWidth + 4 * int(self.useFocusInd)
else:
defSize = wx.Button.GetDefaultSize()
width = 12 + w
if useMin and width < defSize.width:
width = defSize.width
height = 11 + h
if useMin and height < defSize.height:
height = defSize.height
width = width + self.bezelWidth - 1
height = height + self.bezelWidth - 1
return wx.Size(width, height)
def AcceptsFocus(self):
"""
Can this window be given focus by mouse click?
.. note:: Overridden from :class:`Control`.
"""
return self.IsShown() and self.IsEnabled()
def GetDefaultAttributes(self):
"""
Overridden base class virtual. By default we should use
the same font/colour attributes as the native :class:`Button`.
:return: an instance of :class:`VisualAttributes`.
.. note:: Overridden from :class:`Control`.
"""
return wx.Button.GetClassDefaultAttributes()
def ShouldInheritColours(self):
"""
Overridden base class virtual. Buttons usually don't inherit
the parent's colours.
.. note:: Overridden from :class:`Control`.
"""
return False
def Enable(self, enable=True):
"""
Enables/disables the button.
:param bool `enable`: ``True`` to enable the button, ``False`` to disable it.
.. note:: Overridden from :class:`Control`.
"""
if enable != self.IsEnabled():
wx.Control.Enable(self, enable)
self.Refresh()
def SetBezelWidth(self, width):
"""
Sets the width of the 3D effect.
:param integer `width`: the 3D border width, in pixels.
"""
self.bezelWidth = width
def GetBezelWidth(self):
"""
Returns the width of the 3D effect, in pixels.
:rtype: integer
"""
return self.bezelWidth
def SetUseFocusIndicator(self, flag):
"""
Specifies if a focus indicator (dotted line) should be used.
:param bool `flag`: ``True`` to draw a focus ring, ``False`` otherwise.
"""
self.useFocusInd = flag
def GetUseFocusIndicator(self):
"""
Returns the focus indicator flag, specifying if a focus indicator
(dotted line) is being used.
:rtype: bool
"""
return self.useFocusInd
def InitColours(self):
"""
Calculate a new set of highlight and shadow colours based on
the background colour. Works okay if the colour is dark...
"""
faceClr = self.GetBackgroundColour()
r, g, b, a = faceClr
fr, fg, fb = min(255,r+32), min(255,g+32), min(255,b+32)
self.faceDnClr = wx.Colour(fr, fg, fb)
sr, sg, sb = max(0,r-32), max(0,g-32), max(0,b-32)
self.shadowPenClr = wx.Colour(sr,sg,sb)
hr, hg, hb = min(255,r+64), min(255,g+64), min(255,b+64)
self.highlightPenClr = wx.Colour(hr,hg,hb)
self.focusClr = wx.Colour(hr, hg, hb)
def SetBackgroundColour(self, colour):
"""
Sets the :class:`GenButton` background colour.
:param `colour`: a valid :class:`Colour` object.
.. note:: Overridden from :class:`Control`.
"""
wx.Control.SetBackgroundColour(self, colour)
self.InitColours()
def SetForegroundColour(self, colour):
"""
Sets the :class:`GenButton` foreground colour.
:param `colour`: a valid :class:`Colour` object.
.. note:: Overridden from :class:`Control`.
"""
wx.Control.SetForegroundColour(self, colour)
self.InitColours()
def SetDefault(self):
"""
This sets the :class:`GenButton` to be the default item for the panel or dialog box.
.. note:: Under Windows, only dialog box buttons respond to this function. As normal
under Windows and Motif, pressing return causes the default button to be depressed
when the return key is pressed. See also :meth:`Window.SetFocus` which sets the
keyboard focus for windows and text panel items, and :meth:`TopLevelWindow.SetDefaultItem`.
.. note:: Note that under Motif, calling this function immediately after creation of a button
and before the creation of other buttons will cause misalignment of the row of buttons,
since default buttons are larger. To get around this, call :meth:`SetDefault` after you
have created a row of buttons: wxPython will then set the size of all buttons currently
on the panel to the same size.
"""
tlw = wx.GetTopLevelParent(self)
if hasattr(tlw, 'SetDefaultItem'):
tlw.SetDefaultItem(self)
def _GetLabelSize(self):
""" Used internally. """
w, h = self.GetTextExtent(self.GetLabel())
return w, h, True
def Notify(self):
""" Actually sends a ``wx.EVT_BUTTON`` event to the listener (if any). """
evt = GenButtonEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
evt.SetIsDown(not self.up)
evt.SetButtonObj(self)
evt.SetEventObject(self)
self.GetEventHandler().ProcessEvent(evt)
def DrawBezel(self, dc, x1, y1, x2, y2):
# draw the upper left sides
if self.up:
dc.SetPen(wx.Pen(self.highlightPenClr, 1))
else:
dc.SetPen(wx.Pen(self.shadowPenClr, 1))
for i in range(self.bezelWidth):
dc.DrawLine(x1+i, y1, x1+i, y2-i)
dc.DrawLine(x1, y1+i, x2-i, y1+i)
# draw the lower right sides
if self.up:
dc.SetPen(wx.Pen(self.shadowPenClr, 1))
else:
dc.SetPen(wx.Pen(self.highlightPenClr, 1))
for i in range(self.bezelWidth):
dc.DrawLine(x1+i, y2-i, x2+1, y2-i)
dc.DrawLine(x2-i, y1+i, x2-i, y2)
def DrawLabel(self, dc, width, height, dx=0, dy=0):
dc.SetFont(self.GetFont())
if self.IsEnabled():
dc.SetTextForeground(self.GetForegroundColour())
else:
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
label = self.GetLabel()
tw, th = dc.GetTextExtent(label)
if not self.up:
dx = dy = self.labelDelta
dc.DrawText(label, (width-tw)/2+dx, (height-th)/2+dy)
def DrawFocusIndicator(self, dc, w, h):
bw = self.bezelWidth
textClr = self.GetForegroundColour()
focusIndPen = wx.Pen(textClr, 1, wx.PENSTYLE_USER_DASH)
focusIndPen.SetDashes([1,1])
focusIndPen.SetCap(wx.CAP_BUTT)
if wx.Platform == "__WXMAC__":
dc.SetLogicalFunction(wx.XOR)
else:
focusIndPen.SetColour(self.focusClr)
dc.SetLogicalFunction(wx.INVERT)
dc.SetPen(focusIndPen)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRectangle(bw+2,bw+2, w-bw*2-4, h-bw*2-4)
dc.SetLogicalFunction(wx.COPY)
def OnPaint(self, event):
"""
Handles the ``wx.EVT_PAINT`` event for :class:`GenButton`.
:param `event`: a :class:`PaintEvent` event to be processed.
"""
(width, height) = self.GetClientSize()
x1 = y1 = 0
x2 = width-1
y2 = height-1
dc = wx.PaintDC(self)
brush = self.GetBackgroundBrush(dc)
if brush is not None:
dc.SetBackground(brush)
dc.Clear()
self.DrawBezel(dc, x1, y1, x2, y2)
self.DrawLabel(dc, width, height)
if self.hasFocus and self.useFocusInd:
self.DrawFocusIndicator(dc, width, height)
def OnSize(self, event):
"""
Handles the ``wx.EVT_SIZE`` event for :class:`GenButton`.
:param `event`: a :class:`SizeEvent` event to be processed.
"""
self.Refresh()
event.Skip()
def GetBackgroundBrush(self, dc):
"""
Returns the current :class:`Brush` to be used to draw the button background.
:param DC `dc`: the device context used to draw the button background.
"""
if self.up:
colBg = self.GetBackgroundColour()
brush = wx.Brush(colBg)
if self.style & wx.BORDER_NONE:
myAttr = self.GetDefaultAttributes()
parAttr = self.GetParent().GetDefaultAttributes()
myDef = colBg == myAttr.colBg
parDef = self.GetParent().GetBackgroundColour() == parAttr.colBg
if myDef and parDef:
if wx.Platform == "__WXMAC__":
c = wx.MacThemeColour(1) # 1 == kThemeBrushDialogBackgroundActive
brush = wx.Brush(c)
elif wx.Platform == "__WXMSW__":
if hasattr(self, 'DoEraseBackground') and self.DoEraseBackground(dc):
brush = None
elif myDef and not parDef:
colBg = self.GetParent().GetBackgroundColour()
brush = wx.Brush(colBg)
else:
# this line assumes that a pressed button should be hilighted with
# a solid colour even if the background is supposed to be transparent
brush = wx.Brush(self.faceDnClr)
return brush
def OnLeftDown(self, event):
"""
Handles the ``wx.EVT_LEFT_DOWN`` event for :class:`GenButton`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if not self.IsEnabled():
return
self.up = False
self.CaptureMouse()
self.SetFocus()
self.Refresh()
event.Skip()
def OnLeftUp(self, event):
"""
Handles the ``wx.EVT_LEFT_UP`` event for :class:`GenButton`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if not self.IsEnabled() or not self.HasCapture():
return
if self.HasCapture():
self.ReleaseMouse()
if not self.up: # if the button was down when the mouse was released...
self.Notify()
self.up = True
if self: # in case the button was destroyed in the eventhandler
self.Refresh()
event.Skip()
def OnMotion(self, event):
"""
Handles the ``wx.EVT_MOTION`` event for :class:`GenButton`.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if not self.IsEnabled() or not self.HasCapture():
return
if event.LeftIsDown() and self.HasCapture():
x,y = event.GetPosition()
w,h = self.GetClientSize()
if self.up and x<w and x>=0 and y<h and y>=0:
self.up = False
self.Refresh()
return
if not self.up and (x<0 or y<0 or x>=w or y>=h):
self.up = True
self.Refresh()
return
event.Skip()
def OnGainFocus(self, event):
"""
Handles the ``wx.EVT_SET_FOCUS`` event for :class:`GenButton`.
:param `event`: a :class:`FocusEvent` event to be processed.
"""
self.hasFocus = True
self.Refresh()
self.Update()
def OnLoseFocus(self, event):
"""
Handles the ``wx.EVT_KILL_FOCUS`` event for :class:`GenButton`.
:param `event`: a :class:`FocusEvent` event to be processed.
"""
self.hasFocus = False
self.Refresh()
self.Update()
def OnKeyDown(self, event):
"""
Handles the ``wx.EVT_KEY_DOWN`` event for :class:`GenButton`.
:param `event`: a :class:`KeyEvent` event to be processed.
"""
if self.hasFocus and event.GetKeyCode() == ord(" "):
self.up = False
self.Refresh()
event.Skip()
def OnKeyUp(self, event):
"""
Handles the ``wx.EVT_KEY_UP`` event for :class:`GenButton`.
:param `event`: a :class:`KeyEvent` event to be processed.
"""
if self.hasFocus and event.GetKeyCode() == ord(" "):
self.up = True
self.Notify()
self.Refresh()
event.Skip()
#----------------------------------------------------------------------
class GenBitmapButton(GenButton):
""" A generic bitmap button. """
def __init__(self, parent, id=-1, bitmap=wx.NullBitmap,
pos = wx.DefaultPosition, size = wx.DefaultSize,
style = 0, validator = wx.DefaultValidator,
name = "genbutton"):
"""
Default class constructor.
:param Window `parent`: parent window. Must not be ``None``;
:param integer `id`: window identifier. A value of -1 indicates a default value;
:param Bitmap `bitmap`: the button bitmap;
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
chosen by either the windowing system or wxPython, depending on platform;
:type `pos`: tuple or :class:`Point`
:param `size`: the control size. A value of (-1, -1) indicates a default size,
chosen by either the windowing system or wxPython, depending on platform;
:type `size`: tuple or :class:`Size`
:param integer `style`: the button style;
:param Validator `validator`: the validator associated to the button;
:param string `name`: the button name.
.. seealso:: :class:`Button` for a list of valid window styles.
"""
self.bmpDisabled = None
self.bmpFocus = None
self.bmpSelected = None
self.SetBitmapLabel(bitmap)
GenButton.__init__(self, parent, id, "", pos, size, style, validator, name)
def GetBitmapLabel(self):
"""
Returns the bitmap for the button's normal state.
:rtype: :class:`Bitmap`
.. seealso:: :meth:`SetBitmapLabel`
"""
return self.bmpLabel
def GetBitmapDisabled(self):
"""
Returns the bitmap for the button's disabled state, which may be invalid.
:rtype: :class:`Bitmap`
.. seealso:: :meth:`SetBitmapDisabled`
"""
return self.bmpDisabled
def GetBitmapFocus(self):
"""
Returns the bitmap for the button's focused state, which may be invalid.
:rtype: :class:`Bitmap`
.. seealso:: :meth:`SetBitmapFocus`
"""
return self.bmpFocus
def GetBitmapSelected(self):
"""
Returns the bitmap for the button's pressed state, which may be invalid.
:rtype: :class:`Bitmap`
.. seealso:: :meth:`SetBitmapSelected`
"""
return self.bmpSelected
def SetBitmapDisabled(self, bitmap):
"""
Sets the bitmap for the disabled button appearance.
:param Bitmap `bitmap`: the bitmap for the disabled button appearance.
.. seealso::
:meth:`GetBitmapDisabled`, :meth:`SetBitmapLabel`,
:meth:`SetBitmapSelected`, :meth:`SetBitmapFocus`
"""
self.bmpDisabled = bitmap
def SetBitmapFocus(self, bitmap):
"""
Sets the bitmap for the focused button appearance.
:param Bitmap `bitmap`: the bitmap for the focused button appearance.
.. seealso::
:meth:`GetBitmapFocus`, :meth:`SetBitmapLabel`,
:meth:`SetBitmapSelected`, :meth:`SetBitmapDisabled`
"""
self.bmpFocus = bitmap
self.SetUseFocusIndicator(False)
def SetBitmapSelected(self, bitmap):
"""
Sets the bitmap for the selected (depressed) button appearance.
:param Bitmap `bitmap`: the bitmap for the selected (depressed) button appearance.
.. seealso::
:meth:`GetBitmapSelected`, :meth:`SetBitmapLabel`,
:meth:`SetBitmapDisabled`, :meth:`SetBitmapFocus`
"""
self.bmpSelected = bitmap
def SetBitmapLabel(self, bitmap, createOthers=True):
"""
Set the bitmap to display normally.
This is the only one that is required.
If `createOthers` is ``True``, then the other bitmaps will be generated
on the fly. Currently, only the disabled bitmap is generated.
:param Bitmap `bitmap`: the bitmap for the normal button appearance.
.. note:: This is the bitmap used for the unselected state, and for all other
states if no other bitmaps are provided.
"""
self.bmpLabel = bitmap
if bitmap is not None and createOthers:
image = bitmap.ConvertToImage()
imageutils.grayOut(image)
self.SetBitmapDisabled(wx.Bitmap(image))
def _GetLabelSize(self):
""" Used internally. """
if not self.bmpLabel:
return -1, -1, False
return self.bmpLabel.GetWidth()+2, self.bmpLabel.GetHeight()+2, False
def DrawLabel(self, dc, width, height, dx=0, dy=0):
bmp = self.bmpLabel
if self.bmpDisabled and not self.IsEnabled():
bmp = self.bmpDisabled
if self.bmpFocus and self.hasFocus:
bmp = self.bmpFocus
if self.bmpSelected and not self.up:
bmp = self.bmpSelected
bw,bh = bmp.GetWidth(), bmp.GetHeight()
if not self.up:
dx = dy = self.labelDelta
hasMask = bmp.GetMask() != None
dc.DrawBitmap(bmp, (width-bw)/2+dx, (height-bh)/2+dy, hasMask)
#----------------------------------------------------------------------
class GenBitmapTextButton(GenBitmapButton):
""" A generic bitmapped button with text label. """
def __init__(self, parent, id=-1, bitmap=wx.NullBitmap, label='',
pos = wx.DefaultPosition, size = wx.DefaultSize,
style = 0, validator = wx.DefaultValidator,
name = "genbutton"):
"""
Default class constructor.
:param Window `parent`: parent window. Must not be ``None``;
:param integer `id`: window identifier. A value of -1 indicates a default value;
:param Bitmap `bitmap`: the button bitmap;
:param string `label`: the button text label;
:param `pos`: the control position. A value of (-1, -1) indicates a default position,
chosen by either the windowing system or wxPython, depending on platform;
:type `pos`: tuple or :class:`Point`
:param `size`: the control size. A value of (-1, -1) indicates a default size,
chosen by either the windowing system or wxPython, depending on platform;
:type `size`: tuple or :class:`Size`
:param integer `style`: the button style;
:param Validator `validator`: the validator associated to the button;
:param string `name`: the button name.
.. seealso:: :class:`Button` for a list of valid window styles.
"""
GenBitmapButton.__init__(self, parent, id, bitmap, pos, size, style, validator, name)
self.SetLabel(label)
def _GetLabelSize(self):
""" Used internally. """
w, h = self.GetTextExtent(self.GetLabel())
if not self.bmpLabel:
return w, h, True # if there isn't a bitmap use the size of the text
w_bmp = self.bmpLabel.GetWidth()+2
h_bmp = self.bmpLabel.GetHeight()+2
width = w + w_bmp
if h_bmp > h:
height = h_bmp
else:
height = h
return width, height, True
def DrawLabel(self, dc, width, height, dx=0, dy=0):
bmp = self.bmpLabel
if bmp is not None: # if the bitmap is used
if self.bmpDisabled and not self.IsEnabled():
bmp = self.bmpDisabled
if self.bmpFocus and self.hasFocus:
bmp = self.bmpFocus
if self.bmpSelected and not self.up:
bmp = self.bmpSelected
bw,bh = bmp.GetWidth(), bmp.GetHeight()
if not self.up:
dx = dy = self.labelDelta
hasMask = bmp.GetMask() is not None
else:
bw = bh = 0 # no bitmap -> size is zero
dc.SetFont(self.GetFont())
if self.IsEnabled():
dc.SetTextForeground(self.GetForegroundColour())
else:
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
label = self.GetLabel()
tw, th = dc.GetTextExtent(label) # size of text
if not self.up:
dx = dy = self.labelDelta
pos_x = (width-bw-tw)/2+dx # adjust for bitmap and text to centre
if bmp is not None:
dc.DrawBitmap(bmp, pos_x, (height-bh)/2+dy, hasMask) # draw bitmap if available
pos_x = pos_x + 2 # extra spacing from bitmap
dc.DrawText(label, pos_x + dx+bw, (height-th)/2+dy) # draw the text
#----------------------------------------------------------------------
class __ToggleMixin(object):
"""
A mixin that allows to transform :class:`GenButton` in the corresponding toggle button.
"""
def SetToggle(self, flag):
"""
Sets the button as toggled/not toggled.
:param bool `flag`: ``True`` to set the button as toggled, ``False`` otherwise.
"""
self.up = not flag
self.Refresh()
SetValue = SetToggle
def GetToggle(self):
"""
Returns the toggled state of a button.
:return: ``True`` is the button is toggled, ``False`` if it is not toggled.
"""
return not self.up
GetValue = GetToggle
def OnLeftDown(self, event):
"""
Handles the ``wx.EVT_LEFT_DOWN`` event for :class:`GenButton` when used as toggle button.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if not self.IsEnabled():
return
self.saveUp = self.up
self.up = not self.up
self.CaptureMouse()
self.SetFocus()
self.Refresh()
def OnLeftUp(self, event):
"""
Handles the ``wx.EVT_LEFT_UP`` event for :class:`GenButton` when used as toggle button.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if not self.IsEnabled() or not self.HasCapture():
return
if self.HasCapture():
self.ReleaseMouse()
self.Refresh()
if self.up != self.saveUp:
self.Notify()
def OnKeyDown(self, event):
"""
Handles the ``wx.EVT_KEY_DOWN`` event for :class:`GenButton` when used as toggle button.
:param `event`: a :class:`KeyEvent` event to be processed.
"""
event.Skip()
def OnMotion(self, event):
"""
Handles the ``wx.EVT_MOTION`` event for :class:`GenButton` when used as toggle button.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
if not self.IsEnabled():
return
if event.LeftIsDown() and self.HasCapture():
x,y = event.GetPosition()
w,h = self.GetClientSize()
if x<w and x>=0 and y<h and y>=0:
self.up = not self.saveUp
self.Refresh()
return
if (x<0 or y<0 or x>=w or y>=h):
self.up = self.saveUp
self.Refresh()
return
event.Skip()
def OnKeyUp(self, event):
"""
Handles the ``wx.EVT_KEY_UP`` event for :class:`GenButton` when used as toggle button.
:param `event`: a :class:`KeyEvent` event to be processed.
"""
if self.hasFocus and event.GetKeyCode() == ord(" "):
self.up = not self.up
self.Notify()
self.Refresh()
event.Skip()
class GenToggleButton(__ToggleMixin, GenButton):
""" A generic toggle button. """
pass
class GenBitmapToggleButton(__ToggleMixin, GenBitmapButton):
""" A generic toggle bitmap button. """
pass
class GenBitmapTextToggleButton(__ToggleMixin, GenBitmapTextButton):
""" A generic toggle bitmap button with text label. """
pass
#----------------------------------------------------------------------
class __ThemedMixin(object):
""" Uses the native renderer to draw the bezel, also handle mouse-overs. """
def InitOtherEvents(self):
""" Initializes other events needed for themed buttons. """
self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouse)
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouse)
def OnMouse(self, event):
"""
Handles the ``wx.EVT_ENTER_WINDOW`` and ``wx.EVT_LEAVE_WINDOW`` events for
:class:`GenButton` when used as a themed button.
:param `event`: a :class:`MouseEvent` event to be processed.
"""
self.Refresh()
event.Skip()
def DrawBezel(self, dc, x1, y1, x2, y2):
rect = wx.Rect(x1, y1, x2, y2)
if self.up:
state = 0
else:
state = wx.CONTROL_PRESSED | wx.CONTROL_SELECTED
if not self.IsEnabled():
state = wx.CONTROL_DISABLED
pt = self.ScreenToClient(wx.GetMousePosition())
if self.GetClientRect().Contains(pt):
state |= wx.CONTROL_CURRENT
wx.RendererNative.Get().DrawPushButton(self, dc, rect, state)
class ThemedGenButton(__ThemedMixin, GenButton):
""" A themed generic button. """
pass
class ThemedGenBitmapButton(__ThemedMixin, GenBitmapButton):
""" A themed generic bitmap button. """
pass
class ThemedGenBitmapTextButton(__ThemedMixin, GenBitmapTextButton):
""" A themed generic bitmapped button with text label. """
pass
class ThemedGenToggleButton(__ThemedMixin, GenToggleButton):
""" A themed generic toggle button. """
pass
class ThemedGenBitmapToggleButton(__ThemedMixin, GenBitmapToggleButton):
""" A themed generic toggle bitmap button. """
pass
class ThemedGenBitmapTextToggleButton(__ThemedMixin, GenBitmapTextToggleButton):
""" A themed generic toggle bitmap button with text label. """
pass
#----------------------------------------------------------------------