Poodletooth-iLand/panda/python/Lib/site-packages/wx/lib/agw/artmanager.py
Master Jumblespeed d882959bfa switch to remote
2015-05-18 22:11:33 -04:00

2109 lines
71 KiB
Python
Executable file

"""
This module contains drawing routines and customizations for the AGW widgets
:class:`~lib.agw.labelbook.LabelBook` and :class:`~lib.agw.flatmenu.FlatMenu`.
"""
import wx
import random
from wx.lib.six import BytesIO
from .fmresources import *
# ---------------------------------------------------------------------------- #
# Class DCSaver
# ---------------------------------------------------------------------------- #
_ = wx.GetTranslation
_libimported = None
if wx.Platform == "__WXMSW__":
osVersion = wx.GetOsVersion()
# Shadows behind menus are supported only in XP
if osVersion[1] == 5 and osVersion[2] == 1:
try:
import win32api
import win32con
import winxpgui
_libimported = "MH"
except:
try:
import ctypes
_libimported = "ctypes"
except:
pass
else:
_libimported = None
class DCSaver(object):
"""
Construct a DC saver. The dc is copied as-is.
"""
def __init__(self, pdc):
"""
Default class constructor.
:param `pdc`: an instance of :class:`DC`.
"""
self._pdc = pdc
self._pen = pdc.GetPen()
self._brush = pdc.GetBrush()
def __del__(self):
""" While destructing, restores the dc pen and brush. """
if self._pdc:
self._pdc.SetPen(self._pen)
self._pdc.SetBrush(self._brush)
# ---------------------------------------------------------------------------- #
# Class RendererBase
# ---------------------------------------------------------------------------- #
class RendererBase(object):
""" Base class for all theme renderers. """
def __init__(self):
""" Default class constructor. Intentionally empty. """
pass
def DrawButtonBorders(self, dc, rect, penColour, brushColour):
"""
Draws borders for buttons.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the button's client rectangle;
:param `penColour`: a valid :class:`Colour` for the pen border;
:param `brushColour`: a valid :class:`Colour` for the brush.
"""
# Keep old pen and brush
dcsaver = DCSaver(dc)
dc.SetPen(wx.Pen(penColour))
dc.SetBrush(wx.Brush(brushColour))
dc.DrawRectangle(rect)
def DrawBitmapArea(self, dc, xpm_name, rect, baseColour, flipSide):
"""
Draws the area below a bitmap and the bitmap itself using a gradient shading.
:param `dc`: an instance of :class:`DC`;
:param string `xpm_name`: a name of a XPM bitmap;
:param Rect `rect`: the bitmap client rectangle;
:param `baseColour`: a valid :class:`Colour` for the bitmap background;
:param bool `flipSide`: ``True`` to flip the gradient direction, ``False`` otherwise.
"""
# draw the gradient area
if not flipSide:
ArtManager.Get().PaintDiagonalGradientBox(dc, rect, wx.WHITE,
ArtManager.Get().LightColour(baseColour, 20),
True, False)
else:
ArtManager.Get().PaintDiagonalGradientBox(dc, rect, ArtManager.Get().LightColour(baseColour, 20),
wx.WHITE, True, False)
# draw arrow
arrowDown = wx.Bitmap(xpm_name)
arrowDown.SetMask(wx.Mask(arrowDown, wx.WHITE))
dc.DrawBitmap(arrowDown, rect.x + 1 , rect.y + 1, True)
def DrawBitmapBorders(self, dc, rect, penColour, bitmapBorderUpperLeftPen):
"""
Draws borders for a bitmap.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the button's client rectangle;
:param `penColour`: a valid :class:`Colour` for the pen border;
:param `bitmapBorderUpperLeftPen`: a valid :class:`Colour` for the pen upper
left border.
"""
# Keep old pen and brush
dcsaver = DCSaver(dc)
# lower right size
dc.SetPen(wx.Pen(penColour))
dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1)
dc.DrawLine(rect.x + rect.width - 1, rect.y, rect.x + rect.width - 1, rect.y + rect.height)
# upper left side
dc.SetPen(wx.Pen(bitmapBorderUpperLeftPen))
dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y)
dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height)
def GetMenuFaceColour(self):
"""
Returns the foreground colour for the menu.
:return: An instance of :class:`Colour`.
"""
return ArtManager.Get().LightColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE), 80)
def GetTextColourEnable(self):
"""
Returns the colour used for text colour when enabled.
:return: An instance of :class:`Colour`.
"""
return wx.BLACK
def GetTextColourDisable(self):
"""
Returns the colour used for text colour when disabled.
:return: An instance of :class:`Colour`.
"""
return ArtManager.Get().LightColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT), 30)
def GetFont(self):
"""
Returns the font used for text.
:return: An instance of :class:`Font`.
"""
return wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
# ---------------------------------------------------------------------------- #
# Class RendererXP
# ---------------------------------------------------------------------------- #
class RendererXP(RendererBase):
""" Xp-Style renderer. """
def __init__(self):
""" Default class constructor. """
RendererBase.__init__(self)
def DrawButton(self, dc, rect, state, input=None):
"""
Draws a button using the XP theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the button's client rectangle;
:param integer `state`: the button state;
:param `input`: a flag used to call the right method.
"""
if input is None or type(input) == type(False):
self.DrawButtonTheme(dc, rect, state, input)
else:
self.DrawButtonColour(dc, rect, state, input)
def DrawButtonTheme(self, dc, rect, state, useLightColours=None):
"""
Draws a button using the XP theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the button's client rectangle;
:param integer `state`: the button state;
:param bool `useLightColours`: ``True`` to use light colours, ``False`` otherwise.
"""
# switch according to the status
if state == ControlFocus:
penColour = ArtManager.Get().FrameColour()
brushColour = ArtManager.Get().BackgroundColour()
elif state == ControlPressed:
penColour = ArtManager.Get().FrameColour()
brushColour = ArtManager.Get().HighlightBackgroundColour()
else:
penColour = ArtManager.Get().FrameColour()
brushColour = ArtManager.Get().BackgroundColour()
# Draw the button borders
self.DrawButtonBorders(dc, rect, penColour, brushColour)
def DrawButtonColour(self, dc, rect, state, colour):
"""
Draws a button using the XP theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the button's client rectangle;
:param integer `state`: the button state;
:param `colour`: a valid :class:`Colour` instance.
"""
# switch according to the status
if statet == ControlFocus:
penColour = colour
brushColour = ArtManager.Get().LightColour(colour, 75)
elif state == ControlPressed:
penColour = colour
brushColour = ArtManager.Get().LightColour(colour, 60)
else:
penColour = colour
brushColour = ArtManager.Get().LightColour(colour, 75)
# Draw the button borders
self.DrawButtonBorders(dc, rect, penColour, brushColour)
def DrawMenuBarBg(self, dc, rect):
"""
Draws the menu bar background according to the active theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the menu bar's client rectangle.
"""
# For office style, we simple draw a rectangle with a gradient colouring
artMgr = ArtManager.Get()
vertical = artMgr.GetMBVerticalGradient()
dcsaver = DCSaver(dc)
# fill with gradient
startColour = artMgr.GetMenuBarFaceColour()
if artMgr.IsDark(startColour):
startColour = artMgr.LightColour(startColour, 50)
endColour = artMgr.LightColour(startColour, 90)
artMgr.PaintStraightGradientBox(dc, rect, startColour, endColour, vertical)
# Draw the border
if artMgr.GetMenuBarBorder():
dc.SetPen(wx.Pen(startColour))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRectangle(rect)
def DrawToolBarBg(self, dc, rect):
"""
Draws the toolbar background according to the active theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the toolbar's client rectangle.
"""
artMgr = ArtManager.Get()
if not artMgr.GetRaiseToolbar():
return
# For office style, we simple draw a rectangle with a gradient colouring
vertical = artMgr.GetMBVerticalGradient()
dcsaver = DCSaver(dc)
# fill with gradient
startColour = artMgr.GetMenuBarFaceColour()
if artMgr.IsDark(startColour):
startColour = artMgr.LightColour(startColour, 50)
startColour = artMgr.LightColour(startColour, 20)
endColour = artMgr.LightColour(startColour, 90)
artMgr.PaintStraightGradientBox(dc, rect, startColour, endColour, vertical)
artMgr.DrawBitmapShadow(dc, rect)
def GetTextColourEnable(self):
"""
Returns the colour used for text colour when enabled.
:return: An instance of :class:`Colour`.
"""
return wx.BLACK
# ---------------------------------------------------------------------------- #
# Class RendererMSOffice2007
# ---------------------------------------------------------------------------- #
class RendererMSOffice2007(RendererBase):
""" Windows MS Office 2007 style. """
def __init__(self):
""" Default class constructor. """
RendererBase.__init__(self)
def GetColoursAccordingToState(self, state):
"""
Returns a :class:`Colour` according to the menu item state.
:param integer `state`: one of the following bits:
==================== ======= ==========================
Item State Value Description
==================== ======= ==========================
``ControlPressed`` 0 The item is pressed
``ControlFocus`` 1 The item is focused
``ControlDisabled`` 2 The item is disabled
``ControlNormal`` 3 Normal state
==================== ======= ==========================
:return: An instance of :class:`Colour`.
"""
# switch according to the status
if state == ControlFocus:
upperBoxTopPercent = 95
upperBoxBottomPercent = 50
lowerBoxTopPercent = 40
lowerBoxBottomPercent = 90
concaveUpperBox = True
concaveLowerBox = True
elif state == ControlPressed:
upperBoxTopPercent = 75
upperBoxBottomPercent = 90
lowerBoxTopPercent = 90
lowerBoxBottomPercent = 40
concaveUpperBox = True
concaveLowerBox = True
elif state == ControlDisabled:
upperBoxTopPercent = 100
upperBoxBottomPercent = 100
lowerBoxTopPercent = 70
lowerBoxBottomPercent = 70
concaveUpperBox = True
concaveLowerBox = True
else:
upperBoxTopPercent = 90
upperBoxBottomPercent = 50
lowerBoxTopPercent = 30
lowerBoxBottomPercent = 75
concaveUpperBox = True
concaveLowerBox = True
return upperBoxTopPercent, upperBoxBottomPercent, lowerBoxTopPercent, lowerBoxBottomPercent, \
concaveUpperBox, concaveLowerBox
def DrawButton(self, dc, rect, state, useLightColours):
"""
Draws a button using the MS Office 2007 theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the button's client rectangle;
:param integer `state`: the button state;
:param bool `useLightColours`: ``True`` to use light colours, ``False`` otherwise.
"""
self.DrawButtonColour(dc, rect, state, ArtManager.Get().GetThemeBaseColour(useLightColours))
def DrawButtonColour(self, dc, rect, state, colour):
"""
Draws a button using the MS Office 2007 theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the button's client rectangle;
:param integer `state`: the button state;
:param `colour`: a valid :class:`Colour` instance.
"""
artMgr = ArtManager.Get()
# Keep old pen and brush
dcsaver = DCSaver(dc)
# Define the rounded rectangle base on the given rect
# we need an array of 9 points for it
baseColour = colour
# Define the middle points
leftPt = wx.Point(rect.x, rect.y + (rect.height / 2))
rightPt = wx.Point(rect.x + rect.width-1, rect.y + (rect.height / 2))
# Define the top region
top = wx.Rect((rect.GetLeft(), rect.GetTop()), rightPt)
bottom = wx.Rect(leftPt, (rect.GetRight(), rect.GetBottom()))
upperBoxTopPercent, upperBoxBottomPercent, lowerBoxTopPercent, lowerBoxBottomPercent, \
concaveUpperBox, concaveLowerBox = self.GetColoursAccordingToState(state)
topStartColour = artMgr.LightColour(baseColour, upperBoxTopPercent)
topEndColour = artMgr.LightColour(baseColour, upperBoxBottomPercent)
bottomStartColour = artMgr.LightColour(baseColour, lowerBoxTopPercent)
bottomEndColour = artMgr.LightColour(baseColour, lowerBoxBottomPercent)
artMgr.PaintStraightGradientBox(dc, top, topStartColour, topEndColour)
artMgr.PaintStraightGradientBox(dc, bottom, bottomStartColour, bottomEndColour)
rr = wx.Rect(rect.x, rect.y, rect.width, rect.height)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
frameColour = artMgr.LightColour(baseColour, 60)
dc.SetPen(wx.Pen(frameColour))
dc.DrawRectangle(rr)
wc = artMgr.LightColour(baseColour, 80)
dc.SetPen(wx.Pen(wc))
rr.Deflate(1, 1)
dc.DrawRectangle(rr)
def DrawMenuBarBg(self, dc, rect):
"""
Draws the menu bar background according to the active theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the menu bar's client rectangle.
"""
# Keep old pen and brush
dcsaver = DCSaver(dc)
artMgr = ArtManager.Get()
baseColour = artMgr.GetMenuBarFaceColour()
dc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)))
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)))
dc.DrawRectangle(rect)
# Define the rounded rectangle base on the given rect
# we need an array of 9 points for it
regPts = [wx.Point() for ii in range(9)]
radius = 2
regPts[0] = wx.Point(rect.x, rect.y + radius)
regPts[1] = wx.Point(rect.x+radius, rect.y)
regPts[2] = wx.Point(rect.x+rect.width-radius-1, rect.y)
regPts[3] = wx.Point(rect.x+rect.width-1, rect.y + radius)
regPts[4] = wx.Point(rect.x+rect.width-1, rect.y + rect.height - radius - 1)
regPts[5] = wx.Point(rect.x+rect.width-radius-1, rect.y + rect.height-1)
regPts[6] = wx.Point(rect.x+radius, rect.y + rect.height-1)
regPts[7] = wx.Point(rect.x, rect.y + rect.height - radius - 1)
regPts[8] = regPts[0]
# Define the middle points
factor = artMgr.GetMenuBgFactor()
leftPt1 = wx.Point(rect.x, rect.y + (rect.height / factor))
leftPt2 = wx.Point(rect.x, rect.y + (rect.height / factor)*(factor-1))
rightPt1 = wx.Point(rect.x + rect.width, rect.y + (rect.height / factor))
rightPt2 = wx.Point(rect.x + rect.width, rect.y + (rect.height / factor)*(factor-1))
# Define the top region
topReg = [wx.Point() for ii in range(7)]
topReg[0] = regPts[0]
topReg[1] = regPts[1]
topReg[2] = wx.Point(regPts[2].x+1, regPts[2].y)
topReg[3] = wx.Point(regPts[3].x + 1, regPts[3].y)
topReg[4] = wx.Point(rightPt1.x, rightPt1.y+1)
topReg[5] = wx.Point(leftPt1.x, leftPt1.y+1)
topReg[6] = topReg[0]
# Define the middle region
middle = wx.Rect(leftPt1, wx.Point(rightPt2.x - 2, rightPt2.y))
# Define the bottom region
bottom = wx.Rect(leftPt2, wx.Point(rect.GetRight() - 1, rect.GetBottom()))
topStartColour = artMgr.LightColour(baseColour, 90)
topEndColour = artMgr.LightColour(baseColour, 60)
bottomStartColour = artMgr.LightColour(baseColour, 40)
bottomEndColour = artMgr.LightColour(baseColour, 20)
topRegion = wx.Region(topReg)
artMgr.PaintGradientRegion(dc, topRegion, topStartColour, topEndColour)
artMgr.PaintStraightGradientBox(dc, bottom, bottomStartColour, bottomEndColour)
artMgr.PaintStraightGradientBox(dc, middle, topEndColour, bottomStartColour)
def DrawToolBarBg(self, dc, rect):
"""
Draws the toolbar background according to the active theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the toolbar's client rectangle.
"""
artMgr = ArtManager.Get()
if not artMgr.GetRaiseToolbar():
return
# Keep old pen and brush
dcsaver = DCSaver(dc)
baseColour = artMgr.GetMenuBarFaceColour()
baseColour = artMgr.LightColour(baseColour, 20)
dc.SetBrush(wx.Brush(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)))
dc.SetPen(wx.Pen(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)))
dc.DrawRectangle(rect)
radius = 2
# Define the rounded rectangle base on the given rect
# we need an array of 9 points for it
regPts = [None]*9
regPts[0] = wx.Point(rect.x, rect.y + radius)
regPts[1] = wx.Point(rect.x+radius, rect.y)
regPts[2] = wx.Point(rect.x+rect.width-radius-1, rect.y)
regPts[3] = wx.Point(rect.x+rect.width-1, rect.y + radius)
regPts[4] = wx.Point(rect.x+rect.width-1, rect.y + rect.height - radius - 1)
regPts[5] = wx.Point(rect.x+rect.width-radius-1, rect.y + rect.height-1)
regPts[6] = wx.Point(rect.x+radius, rect.y + rect.height-1)
regPts[7] = wx.Point(rect.x, rect.y + rect.height - radius - 1)
regPts[8] = regPts[0]
# Define the middle points
factor = artMgr.GetMenuBgFactor()
leftPt1 = wx.Point(rect.x, rect.y + (rect.height / factor))
rightPt1 = wx.Point(rect.x + rect.width, rect.y + (rect.height / factor))
leftPt2 = wx.Point(rect.x, rect.y + (rect.height / factor)*(factor-1))
rightPt2 = wx.Point(rect.x + rect.width, rect.y + (rect.height / factor)*(factor-1))
# Define the top region
topReg = [None]*7
topReg[0] = regPts[0]
topReg[1] = regPts[1]
topReg[2] = wx.Point(regPts[2].x+1, regPts[2].y)
topReg[3] = wx.Point(regPts[3].x + 1, regPts[3].y)
topReg[4] = wx.Point(rightPt1.x, rightPt1.y+1)
topReg[5] = wx.Point(leftPt1.x, leftPt1.y+1)
topReg[6] = topReg[0]
# Define the middle region
middle = wx.Rect(leftPt1, wx.Point(rightPt2.x - 2, rightPt2.y))
# Define the bottom region
bottom = wx.Rect(leftPt2, wx.Point(rect.GetRight() - 1, rect.GetBottom()))
topStartColour = artMgr.LightColour(baseColour, 90)
topEndColour = artMgr.LightColour(baseColour, 60)
bottomStartColour = artMgr.LightColour(baseColour, 40)
bottomEndColour = artMgr.LightColour(baseColour, 20)
topRegion = wx.Region(topReg)
artMgr.PaintGradientRegion(dc, topRegion, topStartColour, topEndColour)
artMgr.PaintStraightGradientBox(dc, bottom, bottomStartColour, bottomEndColour)
artMgr.PaintStraightGradientBox(dc, middle, topEndColour, bottomStartColour)
artMgr.DrawBitmapShadow(dc, rect)
def GetTextColourEnable(self):
"""
Returns the colour used for text colour when enabled.
:return: An instance of :class:`Colour`.
"""
return wx.Colour("MIDNIGHT BLUE")
# ---------------------------------------------------------------------------- #
# Class ArtManager
# ---------------------------------------------------------------------------- #
class ArtManager(wx.EvtHandler):
"""
This class provides various art utilities, such as creating shadow, providing
lighter / darker colours for a given colour, etc...
"""
_alignmentBuffer = 7
_menuTheme = StyleXP
_verticalGradient = False
_renderers = {StyleXP: None, Style2007: None}
_bmpShadowEnabled = False
_ms2007sunken = False
_drowMBBorder = True
_menuBgFactor = 5
_menuBarColourScheme = _("Default")
_raiseTB = True
_bitmaps = {}
_transparency = 255
def __init__(self):
""" Default class constructor. """
wx.EvtHandler.__init__(self)
self._menuBarBgColour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)
# connect an event handler to the system colour change event
self.Bind(wx.EVT_SYS_COLOUR_CHANGED, self.OnSysColourChange)
def SetTransparency(self, amount):
"""
Sets the alpha channel value for transparent windows.
:param integer `amount`: the actual transparency value (between 0 and 255).
:raise: `Exception` if the `amount` parameter is lower than ``0`` or greater than ``255``.
"""
if self._transparency == amount:
return
if amount < 0 or amount > 255:
raise Exception("Invalid transparency value")
self._transparency = amount
def GetTransparency(self):
"""
Returns the alpha channel value for transparent windows.
:return: An integer representing the alpha channel value.
"""
return self._transparency
def ConvertToBitmap(self, xpm, alpha=None):
"""
Convert the given image to a bitmap, optionally overlaying an alpha
channel to it.
:param `xpm`: a list of strings formatted as XPM;
:type `xpm`: list of strings
:param `alpha`: a list of alpha values, the same size as the xpm bitmap.
:type `alpha`: list of integers
:return: An instance of :class:`Bitmap`.
"""
if alpha is not None:
img = wx.Bitmap(xpm)
img = img.ConvertToImage()
x, y = img.GetWidth(), img.GetHeight()
img.InitAlpha()
for jj in range(y):
for ii in range(x):
img.SetAlpha(ii, jj, alpha[jj*x+ii])
else:
stream = BytesIO(xpm)
img = wx.Image(stream)
return wx.Bitmap(img)
def Initialize(self):
""" Initializes the bitmaps and colours. """
# create wxBitmaps from the xpm's
self._rightBottomCorner = self.ConvertToBitmap(shadow_center_xpm, shadow_center_alpha)
self._bottom = self.ConvertToBitmap(shadow_bottom_xpm, shadow_bottom_alpha)
self._bottomLeft = self.ConvertToBitmap(shadow_bottom_left_xpm, shadow_bottom_left_alpha)
self._rightTop = self.ConvertToBitmap(shadow_right_top_xpm, shadow_right_top_alpha)
self._right = self.ConvertToBitmap(shadow_right_xpm, shadow_right_alpha)
# initialise the colour map
self.InitColours()
self.SetMenuBarColour(self._menuBarColourScheme)
# Create common bitmaps
self.FillStockBitmaps()
def FillStockBitmaps(self):
""" Initializes few standard bitmaps. """
bmp = self.ConvertToBitmap(arrow_down, alpha=None)
bmp.SetMask(wx.Mask(bmp, wx.Colour(0, 128, 128)))
self._bitmaps.update({"arrow_down": bmp})
bmp = self.ConvertToBitmap(arrow_up, alpha=None)
bmp.SetMask(wx.Mask(bmp, wx.Colour(0, 128, 128)))
self._bitmaps.update({"arrow_up": bmp})
def GetStockBitmap(self, name):
"""
Returns a bitmap from a stock.
:param string `name`: the bitmap name.
:return: The stock bitmap, if `name` was found in the stock bitmap dictionary.
Othewise, :class:`NullBitmap` is returned.
"""
return self._bitmaps.get(name, wx.NullBitmap)
def Get(self):
"""
Accessor to the unique art manager object.
:return: A unique instance of :class:`ArtManager`.
"""
if not hasattr(self, "_instance"):
self._instance = ArtManager()
self._instance.Initialize()
# Initialize the renderers map
self._renderers[StyleXP] = RendererXP()
self._renderers[Style2007] = RendererMSOffice2007()
return self._instance
Get = classmethod(Get)
def Free(self):
""" Destructor for the unique art manager object. """
if hasattr(self, "_instance"):
del self._instance
Free = classmethod(Free)
def OnSysColourChange(self, event):
"""
Handles the ``wx.EVT_SYS_COLOUR_CHANGED`` event for :class:`ArtManager`.
:param `event`: a :class:`SysColourChangedEvent` event to be processed.
"""
# reinitialise the colour map
self.InitColours()
def LightColour(self, colour, percent):
"""
Return light contrast of `colour`. The colour returned is from the scale of
`colour` ==> white.
:param `colour`: the input colour to be brightened, an instance of :class:`Colour`;
:param integer `percent`: determines how light the colour will be. `percent` = ``100``
returns white, `percent` = ``0`` returns `colour`.
:return: A light contrast of the input `colour`, an instance of :class:`Colour`.
"""
end_colour = wx.WHITE
rd = end_colour.Red() - colour.Red()
gd = end_colour.Green() - colour.Green()
bd = end_colour.Blue() - colour.Blue()
high = 100
# We take the percent way of the colour from colour -. white
i = percent
r = colour.Red() + ((i*rd*100)/high)/100
g = colour.Green() + ((i*gd*100)/high)/100
b = colour.Blue() + ((i*bd*100)/high)/100
return wx.Colour(int(r), int(g), int(b))
def DarkColour(self, colour, percent):
"""
Like the :meth:`~ArtManager.LightColour` function, but create the colour darker by `percent`.
:param `colour`: the input colour to be darkened, an instance of :class:`Colour`;
:param integer `percent`: determines how dark the colour will be. `percent` = ``100``
returns black, `percent` = ``0`` returns `colour`.
:return: A dark contrast of the input `colour`, an instance of :class:`Colour`.
"""
end_colour = wx.BLACK
rd = end_colour.Red() - colour.Red()
gd = end_colour.Green() - colour.Green()
bd = end_colour.Blue() - colour.Blue()
high = 100
# We take the percent way of the colour from colour -. white
i = percent
r = colour.Red() + ((i*rd*100)/high)/100
g = colour.Green() + ((i*gd*100)/high)/100
b = colour.Blue() + ((i*bd*100)/high)/100
return wx.Colour(int(r), int(g), int(b))
def PaintStraightGradientBox(self, dc, rect, startColour, endColour, vertical=True):
"""
Paint the rectangle with gradient colouring; the gradient lines are either
horizontal or vertical.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the rectangle to be filled with gradient shading;
:param Colour `startColour`: the first colour of the gradient shading;
:param Colour `endColour`: the second colour of the gradient shading;
:param bool `vertical`: ``True`` for gradient colouring in the vertical direction,
``False`` for horizontal shading.
"""
dcsaver = DCSaver(dc)
if vertical:
high = rect.GetHeight()-1
direction = wx.SOUTH
else:
high = rect.GetWidth()-1
direction = wx.EAST
if high < 1:
return
dc.GradientFillLinear(rect, startColour, endColour, direction)
def PaintGradientRegion(self, dc, region, startColour, endColour, vertical=True):
"""
Paint a region with gradient colouring.
:param `dc`: an instance of :class:`DC`;
:param `region`: a region to be filled with gradient shading (an instance of
:class:`Region`);
:param Colour `startColour`: the first colour of the gradient shading;
:param Colour `endColour`: the second colour of the gradient shading;
:param bool `vertical`: ``True`` for gradient colouring in the vertical direction,
``False`` for horizontal shading.
"""
# The way to achieve non-rectangle
memDC = wx.MemoryDC()
rect = region.GetBox()
bitmap = wx.Bitmap(rect.width, rect.height)
memDC.SelectObject(bitmap)
# Colour the whole rectangle with gradient
rr = wx.Rect(0, 0, rect.width, rect.height)
self.PaintStraightGradientBox(memDC, rr, startColour, endColour, vertical)
# Convert the region to a black and white bitmap with the white pixels being inside the region
# we draw the bitmap over the gradient coloured rectangle, with mask set to white,
# this will cause our region to be coloured with the gradient, while area outside the
# region will be painted with black. then we simply draw the bitmap to the dc with mask set to
# black
tmpRegion = wx.Region(rect.x, rect.y, rect.width, rect.height)
tmpRegion.Offset(-rect.x, -rect.y)
regionBmp = tmpRegion.ConvertToBitmap()
regionBmp.SetMask(wx.Mask(regionBmp, wx.WHITE))
# The function ConvertToBitmap() return a rectangle bitmap
# which is shorter by 1 pixl on the height and width (this is correct behavior, since
# DrawLine does not include the second point as part of the line)
# we fix this issue by drawing our own line at the bottom and left side of the rectangle
memDC.SetPen(wx.BLACK_PEN)
memDC.DrawBitmap(regionBmp, 0, 0, True)
memDC.DrawLine(0, rr.height - 1, rr.width, rr.height - 1)
memDC.DrawLine(rr.width - 1, 0, rr.width - 1, rr.height)
memDC.SelectObject(wx.NullBitmap)
bitmap.SetMask(wx.Mask(bitmap, wx.BLACK))
dc.DrawBitmap(bitmap, rect.x, rect.y, True)
def PaintDiagonalGradientBox(self, dc, rect, startColour, endColour,
startAtUpperLeft=True, trimToSquare=True):
"""
Paint rectangle with gradient colouring; the gradient lines are diagonal
and may start from the upper left corner or from the upper right corner.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the rectangle to be filled with gradient shading;
:param Colour `startColour`: the first colour of the gradient shading;
:param Colour `endColour`: the second colour of the gradient shading;
:param bool `startAtUpperLeft`: ``True`` to start the gradient lines at the upper
left corner of the rectangle, ``False`` to start at the upper right corner;
:param bool `trimToSquare`: ``True`` to trim the gradient lines in a square.
"""
# Save the current pen and brush
savedPen = dc.GetPen()
savedBrush = dc.GetBrush()
# gradient fill from colour 1 to colour 2 with top to bottom
if rect.height < 1 or rect.width < 1:
return
# calculate some basic numbers
size = rect.width
sizeX = sizeY = 0
proportion = 1
if rect.width > rect.height:
if trimToSquare:
size = rect.height
sizeX = sizeY = rect.height - 1
else:
proportion = float(rect.height)/float(rect.width)
size = rect.width
sizeX = rect.width - 1
sizeY = rect.height -1
else:
if trimToSquare:
size = rect.width
sizeX = sizeY = rect.width - 1
else:
sizeX = rect.width - 1
size = rect.height
sizeY = rect.height - 1
proportion = float(rect.width)/float(rect.height)
# calculate gradient coefficients
col2 = endColour
col1 = startColour
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)
# draw the upper triangle
for i in range(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 startAtUpperLeft:
if rect.width > rect.height:
dc.DrawLine(rect.x + i, rect.y, rect.x, int(rect.y + proportion*i))
dc.DrawPoint(rect.x, int(rect.y + proportion*i))
else:
dc.DrawLine(int(rect.x + proportion*i), rect.y, rect.x, rect.y + i)
dc.DrawPoint(rect.x, rect.y + i)
else:
if rect.width > rect.height:
dc.DrawLine(rect.x + sizeX - i, rect.y, rect.x + sizeX, int(rect.y + proportion*i))
dc.DrawPoint(rect.x + sizeX, int(rect.y + proportion*i))
else:
xTo = (int(rect.x + sizeX - proportion * i) > rect.x and [int(rect.x + sizeX - proportion*i)] or [rect.x])[0]
dc.DrawLine(xTo, rect.y, rect.x + sizeX, rect.y + i)
dc.DrawPoint(rect.x + sizeX, rect.y + i)
rf += rstep/2
gf += gstep/2
bf += bstep/2
# draw the lower triangle
for i in range(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 startAtUpperLeft:
if rect.width > rect.height:
dc.DrawLine(rect.x + i, rect.y + sizeY, rect.x + sizeX, int(rect.y + proportion * i))
dc.DrawPoint(rect.x + sizeX, int(rect.y + proportion * i))
else:
dc.DrawLine(int(rect.x + proportion * i), rect.y + sizeY, rect.x + sizeX, rect.y + i)
dc.DrawPoint(rect.x + sizeX, rect.y + i)
else:
if rect.width > rect.height:
dc.DrawLine(rect.x, (int)(rect.y + proportion * i), rect.x + sizeX - i, rect.y + sizeY)
dc.DrawPoint(rect.x + sizeX - i, rect.y + sizeY)
else:
xTo = (int(rect.x + sizeX - proportion*i) > rect.x and [int(rect.x + sizeX - proportion*i)] or [rect.x])[0]
dc.DrawLine(rect.x, rect.y + i, xTo, rect.y + sizeY)
dc.DrawPoint(xTo, rect.y + sizeY)
rf += rstep/2
gf += gstep/2
bf += bstep/2
# Restore the pen and brush
dc.SetPen( savedPen )
dc.SetBrush( savedBrush )
def PaintCrescentGradientBox(self, dc, rect, startColour, endColour, concave=True):
"""
Paint a region with gradient colouring. The gradient is in crescent shape
which fits the 2007 style.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the rectangle to be filled with gradient shading;
:param Colour `startColour`: the first colour of the gradient shading;
:param Colour `endColour`: the second colour of the gradient shading;
:param bool `concave`: ``True`` for a concave effect, ``False`` for a convex one.
"""
diagonalRectWidth = rect.GetWidth()/4
spare = rect.width - 4*diagonalRectWidth
leftRect = wx.Rect(rect.x, rect.y, diagonalRectWidth, rect.GetHeight())
rightRect = wx.Rect(rect.x + 3 * diagonalRectWidth + spare, rect.y, diagonalRectWidth, rect.GetHeight())
if concave:
self.PaintStraightGradientBox(dc, rect, self.MixColours(startColour, endColour, 50), endColour)
self.PaintDiagonalGradientBox(dc, leftRect, startColour, endColour, True, False)
self.PaintDiagonalGradientBox(dc, rightRect, startColour, endColour, False, False)
else:
self.PaintStraightGradientBox(dc, rect, endColour, self.MixColours(endColour, startColour, 50))
self.PaintDiagonalGradientBox(dc, leftRect, endColour, startColour, False, False)
self.PaintDiagonalGradientBox(dc, rightRect, endColour, startColour, True, False)
def FrameColour(self):
"""
Return the surrounding colour for a control.
:return: An instance of :class:`Colour`.
"""
return wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
def BackgroundColour(self):
"""
Returns the background colour of a control when not in focus.
:return: An instance of :class:`Colour`.
"""
return self.LightColour(self.FrameColour(), 75)
def HighlightBackgroundColour(self):
"""
Returns the background colour of a control when it is in focus.
:return: An instance of :class:`Colour`.
"""
return self.LightColour(self.FrameColour(), 60)
def MixColours(self, firstColour, secondColour, percent):
"""
Return mix of input colours.
:param `firstColour`: the first colour to be mixed, an instance of :class:`Colour`;
:param `secondColour`: the second colour to be mixed, an instance of :class:`Colour`;
:param integer `percent`: the relative percentage of `firstColour` with respect to
`secondColour`.
:return: An instance of :class:`Colour`.
"""
# calculate gradient coefficients
redOffset = float((secondColour.Red() * (100 - percent) / 100) - (firstColour.Red() * percent / 100))
greenOffset = float((secondColour.Green() * (100 - percent) / 100) - (firstColour.Green() * percent / 100))
blueOffset = float((secondColour.Blue() * (100 - percent) / 100) - (firstColour.Blue() * percent / 100))
return wx.Colour(firstColour.Red() + redOffset, firstColour.Green() + greenOffset,
firstColour.Blue() + blueOffset)
def RandomColour(self):
"""
Creates a random colour.
:return: An instance of :class:`Colour`.
"""
r = random.randint(0, 255) # Random value betweem 0-255
g = random.randint(0, 255) # Random value betweem 0-255
b = random.randint(0, 255) # Random value betweem 0-255
return wx.Colour(r, g, b)
def IsDark(self, colour):
"""
Returns whether a colour is dark or light.
:param `colour`: an instance of :class:`Colour`.
:return: ``True`` if the average RGB values are dark, ``False`` otherwise.
"""
evg = (colour.Red() + colour.Green() + colour.Blue())/3
if evg < 127:
return True
return False
def TruncateText(self, dc, text, maxWidth):
"""
Truncates a given string to fit given width size. if the text does not fit
into the given width it is truncated to fit. The format of the fixed text
is ``truncate text ...``.
:param `dc`: an instance of :class:`DC`;
:param string `text`: the text to be (eventually) truncated;
:param integer `maxWidth`: the maximum width allowed for the text.
:return: A new string containining the (possibly) truncated text.
"""
textLen = len(text)
tempText = text
rectSize = maxWidth
fixedText = ""
textW, textH = dc.GetTextExtent(text)
if rectSize >= textW:
return text
# The text does not fit in the designated area,
# so we need to truncate it a bit
suffix = ".."
w, h = dc.GetTextExtent(suffix)
rectSize -= w
for i in range(textLen, -1, -1):
textW, textH = dc.GetTextExtent(tempText)
if rectSize >= textW:
fixedText = tempText
fixedText += ".."
return fixedText
tempText = tempText[:-1]
def DrawButton(self, dc, rect, theme, state, input=None):
"""
Colour rectangle according to the theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the rectangle to be filled with gradient shading;
:param string `theme`: the theme to use to draw the button;
:param integer `state`: the button state;
:param `input`: a flag used to call the right method.
"""
if input is None or type(input) == type(False):
self.DrawButtonTheme(dc, rect, theme, state, input)
else:
self.DrawButtonColour(dc, rect, theme, state, input)
def DrawButtonTheme(self, dc, rect, theme, state, useLightColours=True):
"""
Draws a button using the appropriate theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the button's client rectangle;
:param string `theme`: the theme to use to draw the button;
:param integer `state`: the button state;
:param bool `useLightColours`: ``True`` to use light colours, ``False`` otherwise.
"""
renderer = self._renderers[theme]
# Set background colour if non given by caller
renderer.DrawButton(dc, rect, state, useLightColours)
def DrawButtonColour(self, dc, rect, theme, state, colour):
"""
Draws a button using the appropriate theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the button's client rectangle;
:param string `theme`: the theme to use to draw the button;
:param integer `state`: the button state;
:param `colour`: a valid :class:`Colour` instance.
"""
renderer = self._renderers[theme]
renderer.DrawButton(dc, rect, state, colour)
def CanMakeWindowsTransparent(self):
"""
Used internally.
:return: ``True`` if the system supports transparency of toplevel windows,
otherwise returns ``False``.
"""
if wx.Platform == "__WXMSW__":
version = wx.GetOsDescription()
found = version.find("XP") >= 0 or version.find("2000") >= 0 or version.find("NT") >= 0
return found
elif wx.Platform == "__WXMAC__":
return True
else:
return False
# on supported windows systems (Win2000 and greater), this function
# will make a frame window transparent by a certain amount
def MakeWindowTransparent(self, wnd, amount):
"""
Used internally. Makes a toplevel window transparent if the system supports it.
:param `wnd`: the toplevel window to make transparent, an instance of :class:`TopLevelWindow`;
:param integer `amount`: the window transparency to apply.
"""
if wnd.GetSize() == (0, 0):
return
# this API call is not in all SDKs, only the newer ones, so
# we will runtime bind this
if wx.Platform == "__WXMSW__":
hwnd = wnd.GetHandle()
if not hasattr(self, "_winlib"):
if _libimported == "MH":
self._winlib = win32api.LoadLibrary("user32")
elif _libimported == "ctypes":
self._winlib = ctypes.windll.user32
if _libimported == "MH":
pSetLayeredWindowAttributes = win32api.GetProcAddress(self._winlib,
"SetLayeredWindowAttributes")
if pSetLayeredWindowAttributes == None:
return
exstyle = win32api.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
if 0 == (exstyle & 0x80000):
win32api.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, exstyle | 0x80000)
winxpgui.SetLayeredWindowAttributes(hwnd, 0, amount, 2)
elif _libimported == "ctypes":
style = self._winlib.GetWindowLongA(hwnd, 0xffffffec)
style |= 0x00080000
self._winlib.SetWindowLongA(hwnd, 0xffffffec, style)
self._winlib.SetLayeredWindowAttributes(hwnd, 0, amount, 2)
else:
if not wnd.CanSetTransparent():
return
wnd.SetTransparent(amount)
return
# assumption: the background was already drawn on the dc
def DrawBitmapShadow(self, dc, rect, where=BottomShadow|RightShadow):
"""
Draws a shadow using background bitmap.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the bitmap's client rectangle;
:param integer `where`: where to draw the shadow. This can be any combination of the
following bits:
========================== ======= ================================
Shadow Settings Value Description
========================== ======= ================================
``RightShadow`` 1 Right side shadow
``BottomShadow`` 2 Not full bottom shadow
``BottomShadowFull`` 4 Full bottom shadow
========================== ======= ================================
"""
shadowSize = 5
# the rect must be at least 5x5 pixles
if rect.height < 2*shadowSize or rect.width < 2*shadowSize:
return
# Start by drawing the right bottom corner
if where & BottomShadow or where & BottomShadowFull:
dc.DrawBitmap(self._rightBottomCorner, rect.x+rect.width, rect.y+rect.height, True)
# Draw right side shadow
xx = rect.x + rect.width
yy = rect.y + rect.height - shadowSize
if where & RightShadow:
while yy - rect.y > 2*shadowSize:
dc.DrawBitmap(self._right, xx, yy, True)
yy -= shadowSize
dc.DrawBitmap(self._rightTop, xx, yy - shadowSize, True)
if where & BottomShadow:
xx = rect.x + rect.width - shadowSize
yy = rect.height + rect.y
while xx - rect.x > 2*shadowSize:
dc.DrawBitmap(self._bottom, xx, yy, True)
xx -= shadowSize
dc.DrawBitmap(self._bottomLeft, xx - shadowSize, yy, True)
if where & BottomShadowFull:
xx = rect.x + rect.width - shadowSize
yy = rect.height + rect.y
while xx - rect.x >= 0:
dc.DrawBitmap(self._bottom, xx, yy, True)
xx -= shadowSize
dc.DrawBitmap(self._bottom, xx, yy, True)
def DropShadow(self, wnd, drop=True):
"""
Adds a shadow under the window (Windows only).
:param `wnd`: the window for which we are dropping a shadow, an instance of :class:`TopLevelWindow`;
:param bool `drop`: ``True`` to drop a shadow, ``False`` to remove it.
"""
if not self.CanMakeWindowsTransparent() or not _libimported:
return
if "__WXMSW__" in wx.Platform:
hwnd = wnd.GetHandle()
if not hasattr(self, "_winlib"):
if _libimported == "MH":
self._winlib = win32api.LoadLibrary("user32")
elif _libimported == "ctypes":
self._winlib = ctypes.windll.user32
if _libimported == "MH":
csstyle = win32api.GetWindowLong(hwnd, win32con.GCL_STYLE)
else:
csstyle = self._winlib.GetWindowLongA(hwnd, win32con.GCL_STYLE)
if drop:
if csstyle & CS_DROPSHADOW:
return
else:
csstyle |= CS_DROPSHADOW #Nothing to be done
else:
if csstyle & CS_DROPSHADOW:
csstyle &= ~(CS_DROPSHADOW)
else:
return #Nothing to be done
win32api.SetWindowLong(hwnd, win32con.GCL_STYLE, csstyle)
def GetBitmapStartLocation(self, dc, rect, bitmap, text="", style=0):
"""
Returns the top left `x` and `y` cordinates of the bitmap drawing.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the bitmap's client rectangle;
:param Bitmap `bitmap`: the bitmap associated with the button;
:param string `text`: the button label;
:param integer `style`: the button style. This can be one of the following bits:
============================== ======= ================================
Button style Value Description
============================== ======= ================================
``BU_EXT_XP_STYLE`` 1 A button with a XP style
``BU_EXT_2007_STYLE`` 2 A button with a MS Office 2007 style
``BU_EXT_LEFT_ALIGN_STYLE`` 4 A left-aligned button
``BU_EXT_CENTER_ALIGN_STYLE`` 8 A center-aligned button
``BU_EXT_RIGHT_ALIGN_STYLE`` 16 A right-aligned button
``BU_EXT_RIGHT_TO_LEFT_STYLE`` 32 A button suitable for right-to-left languages
============================== ======= ================================
:return: A tuple containining the top left `x` and `y` cordinates of the bitmap drawing.
"""
alignmentBuffer = self.GetAlignBuffer()
# get the startLocationY
fixedTextWidth = fixedTextHeight = 0
if not text:
fixedTextHeight = bitmap.GetHeight()
else:
fixedTextWidth, fixedTextHeight = dc.GetTextExtent(text)
startLocationY = rect.y + (rect.height - fixedTextHeight)/2
# get the startLocationX
if style & BU_EXT_RIGHT_TO_LEFT_STYLE:
startLocationX = rect.x + rect.width - alignmentBuffer - bitmap.GetWidth()
else:
if style & BU_EXT_RIGHT_ALIGN_STYLE:
maxWidth = rect.x + rect.width - (2 * alignmentBuffer) - bitmap.GetWidth() # the alignment is for both sides
# get the truncated text. The text may stay as is, it is not a must that is will be trancated
fixedText = self.TruncateText(dc, text, maxWidth)
# get the fixed text dimentions
fixedTextWidth, fixedTextHeight = dc.GetTextExtent(fixedText)
# calculate the start location
startLocationX = maxWidth - fixedTextWidth
elif style & BU_EXT_LEFT_ALIGN_STYLE:
# calculate the start location
startLocationX = alignmentBuffer
else: # meaning BU_EXT_CENTER_ALIGN_STYLE
maxWidth = rect.x + rect.width - (2 * alignmentBuffer) - bitmap.GetWidth() # the alignment is for both sides
# get the truncated text. The text may stay as is, it is not a must that is will be trancated
fixedText = self.TruncateText(dc, text, maxWidth)
# get the fixed text dimentions
fixedTextWidth, fixedTextHeight = dc.GetTextExtent(fixedText)
if maxWidth > fixedTextWidth:
# calculate the start location
startLocationX = (maxWidth - fixedTextWidth) / 2
else:
# calculate the start location
startLocationX = maxWidth - fixedTextWidth
# it is very important to validate that the start location is not less than the alignment buffer
if startLocationX < alignmentBuffer:
startLocationX = alignmentBuffer
return startLocationX, startLocationY
def GetTextStartLocation(self, dc, rect, bitmap, text, style=0):
"""
Returns the top left `x` and `y` cordinates of the text drawing.
In case the text is too long, the text is being fixed (the text is cut and
a '...' mark is added in the end).
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the text's client rectangle;
:param Bitmap `bitmap`: the bitmap associated with the button;
:param string `text`: the button label;
:param integer `style`: the button style.
:return: A tuple containining the top left `x` and `y` cordinates of the text drawing, plus
the truncated version of the input `text`.
:see: :meth:`~ArtManager.GetBitmapStartLocation` for a list of valid button styles.
"""
alignmentBuffer = self.GetAlignBuffer()
# get the bitmap offset
bitmapOffset = 0
if bitmap != wx.NullBitmap:
bitmapOffset = bitmap.GetWidth()
# get the truncated text. The text may stay as is, it is not a must that is will be trancated
maxWidth = rect.x + rect.width - (2 * alignmentBuffer) - bitmapOffset # the alignment is for both sides
fixedText = self.TruncateText(dc, text, maxWidth)
# get the fixed text dimentions
fixedTextWidth, fixedTextHeight = dc.GetTextExtent(fixedText)
startLocationY = (rect.height - fixedTextHeight) / 2 + rect.y
# get the startLocationX
if style & BU_EXT_RIGHT_TO_LEFT_STYLE:
startLocationX = maxWidth - fixedTextWidth + alignmentBuffer
else:
if style & BU_EXT_LEFT_ALIGN_STYLE:
# calculate the start location
startLocationX = bitmapOffset + alignmentBuffer
elif style & BU_EXT_RIGHT_ALIGN_STYLE:
# calculate the start location
startLocationX = maxWidth - fixedTextWidth + bitmapOffset + alignmentBuffer
else: # meaning wxBU_EXT_CENTER_ALIGN_STYLE
# calculate the start location
startLocationX = (maxWidth - fixedTextWidth) / 2 + bitmapOffset + alignmentBuffer
# it is very important to validate that the start location is not less than the alignment buffer
if startLocationX < alignmentBuffer:
startLocationX = alignmentBuffer
return startLocationX, startLocationY, fixedText
def DrawTextAndBitmap(self, dc, rect, text, enable=True, font=wx.NullFont,
fontColour=wx.BLACK, bitmap=wx.NullBitmap,
grayBitmap=wx.NullBitmap, style=0):
"""
Draws the text & bitmap on the input dc.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the text and bitmap client rectangle;
:param string `text`: the button label;
:param bool `enable`: ``True`` if the button is enabled, ``False`` otherwise;
:param `font`: the font to use to draw the text, an instance of :class:`Font`;
:param `fontColour`: the colour to use to draw the text, an instance of
:class:`Colour`;
:param `bitmap`: the bitmap associated with the button, an instance of :class:`Bitmap`;
:param `grayBitmap`: a greyed-out version of the input `bitmap` representing
a disabled bitmap, an instance of :class:`Bitmap`;
:param integer `style`: the button style.
:see: :meth:`~ArtManager.GetBitmapStartLocation` for a list of valid button styles.
"""
# enable colours
if enable:
dc.SetTextForeground(fontColour)
else:
dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
# set the font
if font == wx.NullFont:
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
dc.SetFont(font)
startLocationX = startLocationY = 0
if bitmap != wx.NullBitmap:
# calculate the bitmap start location
startLocationX, startLocationY = self.GetBitmapStartLocation(dc, rect, bitmap, text, style)
# draw the bitmap
if enable:
dc.DrawBitmap(bitmap, startLocationX, startLocationY, True)
else:
dc.DrawBitmap(grayBitmap, startLocationX, startLocationY, True)
# calculate the text start location
location, labelOnly = self.GetAccelIndex(text)
startLocationX, startLocationY, fixedText = self.GetTextStartLocation(dc, rect, bitmap, labelOnly, style)
# after all the caculations are finished, it is time to draw the text
# underline the first letter that is marked with a '&'
if location == -1 or font.GetUnderlined() or location >= len(fixedText):
# draw the text
dc.DrawText(fixedText, startLocationX, startLocationY)
else:
# underline the first '&'
before = fixedText[0:location]
underlineLetter = fixedText[location]
after = fixedText[location+1:]
# before
dc.DrawText(before, startLocationX, startLocationY)
# underlineLetter
if "__WXGTK__" not in wx.Platform:
w1, h = dc.GetTextExtent(before)
font.SetUnderlined(True)
dc.SetFont(font)
dc.DrawText(underlineLetter, startLocationX + w1, startLocationY)
else:
w1, h = dc.GetTextExtent(before)
dc.DrawText(underlineLetter, startLocationX + w1, startLocationY)
# Draw the underline ourselves since using the Underline in GTK,
# causes the line to be too close to the letter
uderlineLetterW, uderlineLetterH = dc.GetTextExtent(underlineLetter)
curPen = dc.GetPen()
dc.SetPen(wx.BLACK_PEN)
dc.DrawLine(startLocationX + w1, startLocationY + uderlineLetterH - 2,
startLocationX + w1 + uderlineLetterW, startLocationY + uderlineLetterH - 2)
dc.SetPen(curPen)
# after
w2, h = dc.GetTextExtent(underlineLetter)
font.SetUnderlined(False)
dc.SetFont(font)
dc.DrawText(after, startLocationX + w1 + w2, startLocationY)
def CalcButtonBestSize(self, label, bmp):
"""
Returns the best fit size for the supplied label & bitmap.
:param string `label`: the button label;
:param `bmp`: the bitmap associated with the button, an instance of :class:`Bitmap`.
:return: An instance of :class:`Size`, representing the best fit size for the supplied label & bitmap.
"""
if "__WXMSW__" in wx.Platform:
HEIGHT = 22
else:
HEIGHT = 26
dc = wx.MemoryDC()
dc.SelectBitmap(wx.Bitmap(1, 1))
dc.SetFont(wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT))
width, height, dummy = dc.GetFullMultiLineTextExtent(label)
width += 2*self.GetAlignBuffer()
if bmp.IsOk():
# allocate extra space for the bitmap
heightBmp = bmp.GetHeight() + 2
if height < heightBmp:
height = heightBmp
width += bmp.GetWidth() + 2
if height < HEIGHT:
height = HEIGHT
dc.SelectBitmap(wx.NullBitmap)
return wx.Size(width, height)
def GetMenuFaceColour(self):
"""
Returns the colour used for the menu foreground.
:return: An instance of :class:`Colour`.
"""
renderer = self._renderers[self.GetMenuTheme()]
return renderer.GetMenuFaceColour()
def GetTextColourEnable(self):
"""
Returns the colour used for enabled menu items.
:return: An instance of :class:`Colour`.
"""
renderer = self._renderers[self.GetMenuTheme()]
return renderer.GetTextColourEnable()
def GetTextColourDisable(self):
"""
Returns the colour used for disabled menu items.
:return: An instance of :class:`Colour`.
"""
renderer = self._renderers[self.GetMenuTheme()]
return renderer.GetTextColourDisable()
def GetFont(self):
"""
Returns the font used by this theme.
:return: An instance of :class:`Font`.
"""
renderer = self._renderers[self.GetMenuTheme()]
return renderer.GetFont()
def GetAccelIndex(self, label):
"""
Returns the mnemonic index of the label and the label stripped of the ampersand mnemonic
(e.g. 'lab&el' ==> will result in 3 and labelOnly = label).
:param string `label`: a string containining an ampersand.
:return: A tuple containining the mnemonic index of the label and the label
stripped of the ampersand mnemonic.
"""
indexAccel = 0
while True:
indexAccel = label.find("&", indexAccel)
if indexAccel == -1:
return indexAccel, label
if label[indexAccel:indexAccel+2] == "&&":
label = label[0:indexAccel] + label[indexAccel+1:]
indexAccel += 1
else:
break
labelOnly = label[0:indexAccel] + label[indexAccel+1:]
return indexAccel, labelOnly
def GetThemeBaseColour(self, useLightColours=True):
"""
Returns the theme (Blue, Silver, Green etc.) base colour, if no theme is active
it return the active caption colour, lighter in 30%.
:param bool `useLightColours`: ``True`` to use light colours, ``False`` otherwise.
:return: An instance of :class:`Colour`.
"""
if not useLightColours and not self.IsDark(self.FrameColour()):
return wx.Colour("GOLD")
else:
return self.LightColour(self.FrameColour(), 30)
def GetAlignBuffer(self):
"""
Return the padding buffer for a text or bitmap.
:return: An integer representing the padding buffer.
"""
return self._alignmentBuffer
def SetMenuTheme(self, theme):
"""
Set the menu theme, possible values (Style2007, StyleXP, StyleVista).
:param string `theme`: a rendering theme class, either `StyleXP`, `Style2007` or `StyleVista`.
"""
self._menuTheme = theme
def GetMenuTheme(self):
"""
Returns the currently used menu theme.
:return: A string containining the currently used theme for the menu.
"""
return self._menuTheme
def AddMenuTheme(self, render):
"""
Adds a new theme to the stock.
:param `render`: a rendering theme class, which must be derived from
:class:`RendererBase`.
:return: An integer representing the size of the renderers dictionary.
"""
# Add new theme
lastRenderer = len(self._renderers)
self._renderers[lastRenderer] = render
return lastRenderer
def SetMS2007ButtonSunken(self, sunken):
"""
Sets MS 2007 button style sunken or not.
:param bool `sunken`: ``True`` to have a sunken border effect, ``False`` otherwise.
"""
self._ms2007sunken = sunken
def GetMS2007ButtonSunken(self):
"""
Returns the sunken flag for MS 2007 buttons.
:return: ``True`` if the MS 2007 buttons are sunken, ``False`` otherwise.
"""
return self._ms2007sunken
def GetMBVerticalGradient(self):
""" Returns ``True`` if the menu bar should be painted with vertical gradient. """
return self._verticalGradient
def SetMBVerticalGradient(self, v):
"""
Sets the menu bar gradient style.
:param bool `v`: ``True`` for a vertical shaded gradient, ``False`` otherwise.
"""
self._verticalGradient = v
def DrawMenuBarBorder(self, border):
"""
Enables menu border drawing (XP style only).
:param bool `border`: ``True`` to draw the menubar border, ``False`` otherwise.
"""
self._drowMBBorder = border
def GetMenuBarBorder(self):
"""
Returns menu bar border drawing flag.
:return: ``True`` if the menu bar border is to be drawn, ``False`` otherwise.
"""
return self._drowMBBorder
def GetMenuBgFactor(self):
"""
Gets the visibility depth of the menu in Metallic style.
The higher the value, the menu bar will look more raised
"""
return self._menuBgFactor
def DrawDragSash(self, rect):
"""
Draws resize sash.
:param Rect `rect`: the sash client rectangle.
"""
dc = wx.ScreenDC()
mem_dc = wx.MemoryDC()
bmp = wx.Bitmap(rect.width, rect.height)
mem_dc.SelectObject(bmp)
mem_dc.SetBrush(wx.WHITE_BRUSH)
mem_dc.SetPen(wx.Pen(wx.WHITE, 1))
mem_dc.DrawRectangle(0, 0, rect.width, rect.height)
dc.Blit(rect.x, rect.y, rect.width, rect.height, mem_dc, 0, 0, wx.XOR)
def TakeScreenShot(self, rect, bmp):
"""
Takes a screenshot of the screen at given position & size (rect).
:param Rect `rect`: the screen rectangle we wish to capture;
:param Bitmap `bmp`: currently unused.
"""
# Create a DC for the whole screen area
dcScreen = wx.ScreenDC()
# Create a Bitmap that will later on hold the screenshot image
# Note that the Bitmap must have a size big enough to hold the screenshot
# -1 means using the current default colour depth
bmp = wx.Bitmap(rect.width, rect.height)
# Create a memory DC that will be used for actually taking the screenshot
memDC = wx.MemoryDC()
# Tell the memory DC to use our Bitmap
# all drawing action on the memory DC will go to the Bitmap now
memDC.SelectObject(bmp)
# Blit (in this case copy) the actual screen on the memory DC
# and thus the Bitmap
memDC.Blit( 0, # Copy to this X coordinate
0, # Copy to this Y coordinate
rect.width, # Copy this width
rect.height, # Copy this height
dcScreen, # From where do we copy?
rect.x, # What's the X offset in the original DC?
rect.y # What's the Y offset in the original DC?
)
# Select the Bitmap out of the memory DC by selecting a new
# uninitialized Bitmap
memDC.SelectObject(wx.NullBitmap)
def DrawToolBarBg(self, dc, rect):
"""
Draws the toolbar background according to the active theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the toolbar's client rectangle.
"""
renderer = self._renderers[self.GetMenuTheme()]
# Set background colour if non given by caller
renderer.DrawToolBarBg(dc, rect)
def DrawMenuBarBg(self, dc, rect):
"""
Draws the menu bar background according to the active theme.
:param `dc`: an instance of :class:`DC`;
:param Rect `rect`: the menubar's client rectangle.
"""
renderer = self._renderers[self.GetMenuTheme()]
# Set background colour if non given by caller
renderer.DrawMenuBarBg(dc, rect)
def SetMenuBarColour(self, scheme):
"""
Sets the menu bar colour scheme to use.
:param string `scheme`: a string representing a colour scheme (i.e., 'Default',
'Dark', 'Dark Olive Green', 'Generic').
"""
self._menuBarColourScheme = scheme
# set default colour
if scheme in self._colourSchemeMap:
self._menuBarBgColour = self._colourSchemeMap[scheme]
def GetMenuBarColourScheme(self):
"""
Returns the current colour scheme.
:return: A string representing the current colour scheme.
"""
return self._menuBarColourScheme
def GetMenuBarFaceColour(self):
"""
Returns the menu bar face colour.
:return: An instance of :class:`Colour`.
"""
return self._menuBarBgColour
def GetMenuBarSelectionColour(self):
"""
Returns the menu bar selection colour.
:return: An instance of :class:`Colour`.
"""
return self._menuBarSelColour
def InitColours(self):
""" Initialise the colour map. """
self._colourSchemeMap = {_("Default"): wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE),
_("Dark"): wx.BLACK,
_("Dark Olive Green"): wx.Colour("DARK OLIVE GREEN"),
_("Generic"): wx.SystemSettings.GetColour(wx.SYS_COLOUR_ACTIVECAPTION)}
def GetColourSchemes(self):
"""
Returns the available colour schemes.
:return: A list of strings representing the available colour schemes.
"""
return list(self._colourSchemeMap.keys())
def CreateGreyBitmap(self, bmp):
"""
Creates a grey bitmap image from the input bitmap.
:param `bmp`: a valid :class:`Bitmap` object to be greyed out.
:return: A greyed-out representation of the input bitmap, an instance of :class:`Bitmap`.
"""
img = bmp.ConvertToImage()
return wx.Bitmap(img.ConvertToGreyscale())
def GetRaiseToolbar(self):
""" Returns ``True`` if we are dropping a shadow under a toolbar. """
return self._raiseTB
def SetRaiseToolbar(self, rais):
"""
Enables/disables toobar shadow drop.
:param bool `rais`: ``True`` to drop a shadow below a toolbar, ``False`` otherwise.
"""
self._raiseTB = rais