X-Git-Url: http://iramuteq.org/git?p=iramuteq;a=blobdiff_plain;f=aui%2Faui_utilities.py;fp=aui%2Faui_utilities.py;h=d6c701ab30a5cf187bb6f8137af8eb7f5eea1302;hp=0000000000000000000000000000000000000000;hb=7fb5b2b86f6c9a0617208ee85211177c23d12f47;hpb=22f93a602f3584ddc6ba68114556212c90307a50 diff --git a/aui/aui_utilities.py b/aui/aui_utilities.py new file mode 100644 index 0000000..d6c701a --- /dev/null +++ b/aui/aui_utilities.py @@ -0,0 +1,678 @@ +""" +This module contains some common functions used by wxPython-AUI to +manipulate colours, bitmaps, text, gradient shadings and custom +dragging images for AuiNotebook tabs. +""" + +__author__ = "Andrea Gavana " +__date__ = "31 March 2009" + + +import wx + +from aui_constants import * + + +if wx.Platform == "__WXMAC__": + import Carbon.Appearance + + +def BlendColour(fg, bg, alpha): + """ + Blends the two colour component `fg` and `bg` into one colour component, adding + an optional alpha channel. + + :param `fg`: the first colour component; + :param `bg`: the second colour component; + :param `alpha`: an optional transparency value. + """ + + result = bg + (alpha*(fg - bg)) + + if result < 0.0: + result = 0.0 + if result > 255: + result = 255 + + return result + + +def StepColour(c, ialpha): + """ + Darken/lighten the input colour `c`. + + :param `c`: a colour to darken/lighten; + :param `ialpha`: a transparency value. + """ + + if ialpha == 100: + return c + + r, g, b = c.Red(), c.Green(), c.Blue() + + # ialpha is 0..200 where 0 is completely black + # and 200 is completely white and 100 is the same + # convert that to normal alpha 0.0 - 1.0 + ialpha = min(ialpha, 200) + ialpha = max(ialpha, 0) + alpha = (ialpha - 100.0)/100.0 + + if ialpha > 100: + + # blend with white + bg = 255 + alpha = 1.0 - alpha # 0 = transparent fg 1 = opaque fg + + else: + + # blend with black + bg = 0 + alpha = 1.0 + alpha # 0 = transparent fg 1 = opaque fg + + r = BlendColour(r, bg, alpha) + g = BlendColour(g, bg, alpha) + b = BlendColour(b, bg, alpha) + + return wx.Colour(r, g, b) + + +def LightContrastColour(c): + """ + Creates a new, lighter colour based on the input colour `c`. + + :param `c`: the input colour to analyze. + """ + + amount = 120 + + # if the colour is especially dark, then + # make the contrast even lighter + if c.Red() < 128 and c.Green() < 128 and c.Blue() < 128: + amount = 160 + + return StepColour(c, amount) + + +def ChopText(dc, text, max_size): + """ + Chops the input `text` if its size does not fit in `max_size`, by cutting the + text and adding ellipsis at the end. + + :param `dc`: a `wx.DC` device context; + :param `text`: the text to chop; + :param `max_size`: the maximum size in which the text should fit. + """ + + # first check if the text fits with no problems + x, y, dummy = dc.GetMultiLineTextExtent(text) + + if x <= max_size: + return text + + textLen = len(text) + last_good_length = 0 + + for i in xrange(textLen, -1, -1): + s = text[0:i] + s += "..." + + x, y = dc.GetTextExtent(s) + last_good_length = i + + if x < max_size: + break + + ret = text[0:last_good_length] + "..." + return ret + + +def BitmapFromBits(bits, w, h, colour): + """ + BitmapFromBits() is a utility function that creates a + masked bitmap from raw bits (XBM format). + + :param `bits`: a string containing the raw bits of the bitmap; + :param `w`: the bitmap width; + :param `h`: the bitmap height; + :param `colour`: the colour which will replace all white pixels in the + raw bitmap. + """ + + img = wx.BitmapFromBits(bits, w, h).ConvertToImage() + img.Replace(0, 0, 0, 123, 123, 123) + img.Replace(255, 255, 255, colour.Red(), colour.Green(), colour.Blue()) + img.SetMaskColour(123, 123, 123) + return wx.BitmapFromImage(img) + + +def IndentPressedBitmap(rect, button_state): + """ + Indents the input rectangle `rect` based on the value of `button_state`. + + :param `rect`: an instance of wx.Rect; + :param `button_state`: an L{AuiNotebook} button state. + """ + + if button_state == AUI_BUTTON_STATE_PRESSED: + rect.x += 1 + rect.y += 1 + + return rect + + +def GetBaseColour(): + """ + Returns the face shading colour on push buttons/backgrounds, mimicking as closely + as possible the platform UI colours. + """ + + if wx.Platform == "__WXMAC__": + + if hasattr(wx, 'MacThemeColour'): + base_colour = wx.MacThemeColour(Carbon.Appearance.kThemeBrushToolbarBackground) + else: + brush = wx.Brush(wx.BLACK) + brush.MacSetTheme(Carbon.Appearance.kThemeBrushToolbarBackground) + base_colour = brush.GetColour() + + else: + + base_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE) + + # the base_colour is too pale to use as our base colour, + # so darken it a bit + if ((255-base_colour.Red()) + + (255-base_colour.Green()) + + (255-base_colour.Blue()) < 60): + + base_colour = StepColour(base_colour, 92) + + return base_colour + + +def MakeDisabledBitmap(bitmap): + """ + Convert the given image (in place) to a grayed-out version, + appropriate for a 'disabled' appearance. + + :param `bitmap`: the bitmap to gray-out. + """ + + anImage = bitmap.ConvertToImage() + factor = 0.7 # 0 < f < 1. Higher Is Grayer + + if anImage.HasMask(): + maskColour = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue()) + else: + maskColour = 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, maskColour) + + for x in range(3): + data[i+x] = pixel[x] + + anImage.SetData(''.join(map(chr, data))) + + return anImage.ConvertToBitmap() + + +def MakeGray(rgbTuple, factor, maskColour): + """ + Make a pixel grayed-out. If the pixel matches the `maskColour`, it won't be + changed. + + :param `rgbTuple`: a tuple representing a pixel colour; + :param `factor`: a graying-out factor; + :param `maskColour`: a colour mask. + """ + + if rgbTuple != maskColour: + r, g, b = rgbTuple + return map(lambda x: int((230 - x) * factor) + x, (r, g, b)) + else: + return rgbTuple + + +def Clip(a, b, c): + """ + Clips the value in `a` based on the extremes `b` and `c`. + + :param `a`: the value to analyze; + :param `b`: a minimum value; + :param `c`: a maximum value. + """ + + return ((a < b and [b]) or [(a > c and [c] or [a])[0]])[0] + + +def LightColour(colour, percent): + """ + Brighten input `colour` by `percent`. + + :param `colour`: the colour to be brightened; + :param `percent`: brightening percentage. + """ + + 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(r, g, b) + + +def PaneCreateStippleBitmap(): + """ + Creates a stipple bitmap to be used in a `wx.Brush`. + This is used to draw sash resize hints. + """ + + data = [0, 0, 0, 192, 192, 192, 192, 192, 192, 0, 0, 0] + img = wx.EmptyImage(2, 2) + counter = 0 + + for ii in xrange(2): + for jj in xrange(2): + img.SetRGB(ii, jj, data[counter], data[counter+1], data[counter+2]) + counter = counter + 3 + + return img.ConvertToBitmap() + + +def DrawMACCloseButton(colour, backColour=None): + """ + Draws the wxMAC tab close button using `wx.GraphicsContext`. + + :param `colour`: the colour to use to draw the circle; + :param `backColour`: the optional background colour for the circle. + """ + + bmp = wx.EmptyBitmapRGBA(16, 16) + dc = wx.MemoryDC() + dc.SelectObject(bmp) + + gc = wx.GraphicsContext.Create(dc) + gc.SetBrush(wx.Brush(colour)) + path = gc.CreatePath() + path.AddCircle(6.5, 7, 6.5) + path.CloseSubpath() + gc.FillPath(path) + + path = gc.CreatePath() + if backColour is not None: + pen = wx.Pen(backColour, 2) + else: + pen = wx.Pen("white", 2) + + pen.SetCap(wx.CAP_BUTT) + pen.SetJoin(wx.JOIN_BEVEL) + gc.SetPen(pen) + path.MoveToPoint(3.5, 4) + path.AddLineToPoint(9.5, 10) + path.MoveToPoint(3.5, 10) + path.AddLineToPoint(9.5, 4) + path.CloseSubpath() + gc.DrawPath(path) + + dc.SelectObject(wx.NullBitmap) + return bmp + + +def DarkenBitmap(bmp, caption_colour, new_colour): + """ + Darkens the input bitmap on wxMAC using the input colour. + + :param `bmp`: the bitmap to be manipulated; + :param `caption_colour`: the colour of the pane caption; + :param `new_colour`: the colour used to darken the bitmap. + """ + + image = bmp.ConvertToImage() + red = caption_colour.Red()/float(new_colour.Red()) + green = caption_colour.Green()/float(new_colour.Green()) + blue = caption_colour.Blue()/float(new_colour.Blue()) + image = image.AdjustChannels(red, green, blue) + return image.ConvertToBitmap() + + +def DrawGradientRectangle(dc, rect, start_colour, end_colour, direction, offset=0, length=0): + """ + Draws a gradient-shaded rectangle. + + :param `dc`: a `wx.DC` device context; + :param `rect`: the rectangle in which to draw the gradient; + :param `start_colour`: the first colour of the gradient; + :param `end_colour`: the second colour of the gradient; + :param `direction`: the gradient direction (horizontal or vertical). + """ + + if direction == AUI_GRADIENT_VERTICAL: + dc.GradientFillLinear(rect, start_colour, end_colour, wx.SOUTH) + else: + dc.GradientFillLinear(rect, start_colour, end_colour, wx.EAST) + + +def FindFocusDescendant(ancestor): + """ + Find a window with the focus, that is also a descendant of the given window. + This is used to determine the window to initially send commands to. + + :param `ancestor`: the window to check for ancestry. + """ + + # Process events starting with the window with the focus, if any. + focusWin = wx.Window.FindFocus() + win = focusWin + + # Check if this is a descendant of this frame. + # If not, win will be set to NULL. + while win: + if win == ancestor: + break + else: + win = win.GetParent() + + if win is None: + focusWin = None + + return focusWin + + +def GetLabelSize(dc, label, vertical): + """ + Returns the L{AuiToolBar} item label size. + + :param `label`: the toolbar tool label; + :param `vertical`: whether the toolbar tool orientation is vertical or not. + """ + + text_width = text_height = 0 + + # get the text height + dummy, text_height = dc.GetTextExtent("ABCDHgj") + # get the text width + if label.strip(): + text_width, dummy = dc.GetTextExtent(label) + + if vertical: + tmp = text_height + text_height = text_width + text_width = tmp + + return wx.Size(text_width, text_height) + + +#--------------------------------------------------------------------------- +# TabDragImage implementation +# This class handles the creation of a custom image when dragging +# AuiNotebook tabs +#--------------------------------------------------------------------------- + +class TabDragImage(wx.DragImage): + """ + This class handles the creation of a custom image in case of drag and + drop of a notebook tab. + """ + + def __init__(self, notebook, page, button_state, tabArt): + """ + Default class constructor. + + For internal use: do not call it in your code! + + :param `notebook`: an instance of L{AuiNotebook}; + :param `page`: the dragged L{AuiNotebook} page; + :param `button_state`: the state of the close button on the tab; + :param `tabArt`: an instance of L{AuiDefaultTabArt} or one of its derivations. + """ + + self._backgroundColour = wx.NamedColour("pink") + self._bitmap = self.CreateBitmap(notebook, page, button_state, tabArt) + wx.DragImage.__init__(self, self._bitmap) + + + def CreateBitmap(self, notebook, page, button_state, tabArt): + """ + Actually creates the drag and drop bitmap. + + :param `notebook`: an instance of L{AuiNotebook}; + :param `page`: the dragged L{AuiNotebook} page; + :param `button_state`: the state of the close button on the tab; + :param `tabArt`: an instance of L{AuiDefaultTabArt} or one of its derivations. + """ + + control = page.control + memory = wx.MemoryDC(wx.EmptyBitmap(1, 1)) + + tab_size, x_extent = tabArt.GetTabSize(memory, notebook, page.caption, page.bitmap, page.active, + button_state, control) + + tab_width, tab_height = tab_size + rect = wx.Rect(0, 0, tab_width, tab_height) + + bitmap = wx.EmptyBitmap(tab_width+1, tab_height+1) + memory.SelectObject(bitmap) + + if wx.Platform == "__WXMAC__": + memory.SetBackground(wx.TRANSPARENT_BRUSH) + else: + memory.SetBackground(wx.Brush(self._backgroundColour)) + + memory.SetBackgroundMode(wx.TRANSPARENT) + memory.Clear() + + paint_control = wx.Platform != "__WXMAC__" + tabArt.DrawTab(memory, notebook, page, rect, button_state, paint_control=paint_control) + + memory.SetBrush(wx.TRANSPARENT_BRUSH) + memory.SetPen(wx.BLACK_PEN) + memory.DrawRoundedRectangle(0, 0, tab_width+1, tab_height+1, 2) + + memory.SelectObject(wx.NullBitmap) + + # Gtk and Windows unfortunatly don't do so well with transparent + # drawing so this hack corrects the image to have a transparent + # background. + if wx.Platform != '__WXMAC__': + timg = bitmap.ConvertToImage() + if not timg.HasAlpha(): + timg.InitAlpha() + for y in xrange(timg.GetHeight()): + for x in xrange(timg.GetWidth()): + pix = wx.Colour(timg.GetRed(x, y), + timg.GetGreen(x, y), + timg.GetBlue(x, y)) + if pix == self._backgroundColour: + timg.SetAlpha(x, y, 0) + bitmap = timg.ConvertToBitmap() + return bitmap + + +def GetDockingImage(direction, useAero, center): + """ + Returns the correct name of the docking bitmap depending on the input parameters. + + :param `useAero`: whether L{AuiManager} is using Aero-style or Whidbey-style docking + images or not; + :param `center`: whether we are looking for the center diamond-shaped bitmap or not. + """ + + suffix = (center and [""] or ["_single"])[0] + prefix = "" + if useAero == 2: + # Whidbey docking guides + prefix = "whidbey_" + elif useAero == 1: + # Aero docking style + prefix = "aero_" + + if direction == wx.TOP: + bmp_unfocus = eval("%sup%s"%(prefix, suffix)).GetBitmap() + bmp_focus = eval("%sup_focus%s"%(prefix, suffix)).GetBitmap() + elif direction == wx.BOTTOM: + bmp_unfocus = eval("%sdown%s"%(prefix, suffix)).GetBitmap() + bmp_focus = eval("%sdown_focus%s"%(prefix, suffix)).GetBitmap() + elif direction == wx.LEFT: + bmp_unfocus = eval("%sleft%s"%(prefix, suffix)).GetBitmap() + bmp_focus = eval("%sleft_focus%s"%(prefix, suffix)).GetBitmap() + elif direction == wx.RIGHT: + bmp_unfocus = eval("%sright%s"%(prefix, suffix)).GetBitmap() + bmp_focus = eval("%sright_focus%s"%(prefix, suffix)).GetBitmap() + else: + bmp_unfocus = eval("%stab%s"%(prefix, suffix)).GetBitmap() + bmp_focus = eval("%stab_focus%s"%(prefix, suffix)).GetBitmap() + + return bmp_unfocus, bmp_focus + + +def TakeScreenShot(rect): + """ + Takes a screenshot of the screen at given position and size (rect). + + :param `rect`: the screen rectangle for which we want to take a screenshot. + """ + + # 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.EmptyBitmap(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) + + return bmp + + +def RescaleScreenShot(bmp, thumbnail_size=200): + """ + Rescales a bitmap to be 300 pixels wide (or tall) at maximum. + + :param `bmp`: the bitmap to rescale; + :param `thumbnail_size`: the maximum size of every page thumbnail. + """ + + bmpW, bmpH = bmp.GetWidth(), bmp.GetHeight() + img = bmp.ConvertToImage() + + newW, newH = bmpW, bmpH + + if bmpW > bmpH: + if bmpW > thumbnail_size: + ratio = bmpW/float(thumbnail_size) + newW, newH = int(bmpW/ratio), int(bmpH/ratio) + img.Rescale(newW, newH, wx.IMAGE_QUALITY_HIGH) + else: + if bmpH > thumbnail_size: + ratio = bmpH/float(thumbnail_size) + newW, newH = int(bmpW/ratio), int(bmpH/ratio) + img.Rescale(newW, newH, wx.IMAGE_QUALITY_HIGH) + + newBmp = img.ConvertToBitmap() + otherBmp = wx.EmptyBitmap(newW+5, newH+5) + + memDC = wx.MemoryDC() + memDC.SelectObject(otherBmp) + memDC.SetBackground(wx.WHITE_BRUSH) + memDC.Clear() + + memDC.SetPen(wx.TRANSPARENT_PEN) + + pos = 0 + for i in xrange(5, 0, -1): + brush = wx.Brush(wx.Colour(50*i, 50*i, 50*i)) + memDC.SetBrush(brush) + memDC.DrawRoundedRectangle(0, 0, newW+5-pos, newH+5-pos, 2) + pos += 1 + + memDC.DrawBitmap(newBmp, 0, 0, True) + + # Select the Bitmap out of the memory DC by selecting a new + # uninitialized Bitmap + memDC.SelectObject(wx.NullBitmap) + + return otherBmp + + +def GetSlidingPoints(rect, size, direction): + """ + Returns the point at which the sliding in and out of a minimized pane begins. + + :param `rect`: the L{AuiToolBar} tool screen rectangle; + :param `size`: the pane window size; + :param `direction`: the pane docking direction. + """ + + if direction == AUI_DOCK_LEFT: + startX, startY = rect.x + rect.width + 2, rect.y + elif direction == AUI_DOCK_TOP: + startX, startY = rect.x, rect.y + rect.height + 2 + elif direction == AUI_DOCK_RIGHT: + startX, startY = rect.x - size.x - 2, rect.y + elif direction == AUI_DOCK_BOTTOM: + startX, startY = rect.x, rect.y - size.y - 2 + else: + raise Exception("How did we get here?") + + caption_height = wx.SystemSettings.GetMetric(wx.SYS_CAPTION_Y) + frame_border_x = wx.SystemSettings.GetMetric(wx.SYS_FRAMESIZE_X) + frame_border_y = wx.SystemSettings.GetMetric(wx.SYS_FRAMESIZE_Y) + + stopX = size.x + caption_height + frame_border_x + stopY = size.x + frame_border_y + + return startX, startY, stopX, stopY + + +def CopyAttributes(newArt, oldArt): + """ + Copies pens, brushes, colours and fonts from the old tab art to the new one. + + :param `newArt`: the new instance of L{AuiDefaultTabArt}; + :param `oldArt`: the old instance of L{AuiDefaultTabArt}. + """ + + attrs = dir(oldArt) + + for attr in attrs: + if attr.startswith("_") and (attr.endswith("_colour") or attr.endswith("_font") or \ + attr.endswith("_font") or attr.endswith("_brush") or \ + attr.endswith("Pen") or attr.endswith("_pen")): + setattr(newArt, attr, getattr(oldArt, attr)) + + return newArt +