2014年12月8日 星期一

wxPython transparent 一些簡單的透明元件

最近玩 wxPython 3時覺得沒有透明元件覺得就用不下去了, 所以設計了些個簡單的透明元件.


TPanel
TStaticText
TButton

但是寫完之後, 發現還很多元件待寫...等有用到再寫了...囧

#-------------------------------------------------------------------------------
# Name:        ExtComponent
# Purpose:     simple transparent components for Python 2.7
#
# Author:      nanoha
#
# Created:     01/01/3000
# Copyright:   (c) nanoha 3000
# Licence:     no licence
#-------------------------------------------------------------------------------
import wx
################################################################################
class TPanel(wx.Panel):
    def __init__(self, parent, id=-1, image=None, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=wx.TAB_TRAVERSAL, name="panel"):
        style |= (wx.CLIP_CHILDREN | wx.TRANSPARENT_WINDOW)
        wx.Panel.__init__(self, parent, id, pos, size, style, name)
        self.SetBackedImage(image)
        self.Bind(wx.EVT_PAINT,self.OnPaint)
        self.Bind(wx.EVT_SIZE, self.OnSize)
    # 設定背景圖片
    def SetBackedImage(self, image=None):
        if image is not None:
            self.BackedImage = image
            tmpImg = self.BackedImage.Copy()
            W, H = self.GetClientSize()
            tmpImg.Rescale(W, H)
            self.BackedBmp = tmpImg.ConvertToBitmap()
        else:
            self.BackedBmp = None
            self.BackedImage = None
        self.Refresh()

    def OnPaint(self,event):
        event.Skip()
        (width, height) = self.GetClientSizeTuple()
        dc = wx.PaintDC(self)
        self.DrawBackedImage(dc, width, height)

    def DrawBackedImage(self, dc, width, height):
        # 建立繪圖緩衝區
        bmp = wx.EmptyBitmap(width, height)
        memDC = wx.MemoryDC()
        memDC.SelectObject(bmp)
        # 塗滿透明色
        memDC.SetPen(wx.Pen('#010101'))
        memDC.SetBrush(wx.Brush('#010101'))
        memDC.DrawRectangle(0, 0, width, height)
        # 加入父窗口背景
        Parent = self.GetParent()
        if Parent:
            # 加入父窗口背景色
            ParentClr = Parent.GetBackgroundColour()
            memDC.SetPen(wx.Pen(ParentClr))
            memDC.SetBrush(wx.Brush(ParentClr))
            memDC.DrawRectangle(0, 0, width, height)
            # 加入父窗口背景圖
            if hasattr(Parent, 'BackedBmp'):
                if Parent.BackedBmp:
                    # get position
                    scr_x,scr_y = self.GetScreenPositionTuple()
                    x,y = Parent.ScreenToClientXY(scr_x,scr_y)
                    pbmp_W = Parent.BackedBmp.GetWidth()
                    pbmp_H = Parent.BackedBmp.GetHeight()
                    w1 = pbmp_W-x
                    if width < w1:
                        w1 = width
                    h1 = pbmp_H-y
                    if height < h1:
                        h1 = height
                    if w1>0 and h1>0:
                        pbackDC = wx.MemoryDC(Parent.BackedBmp)
                        memDC.Blit(0, 0, w1, h1, pbackDC, x, y)
                        pbackDC.SelectObject(wx.NullBitmap)
        # 加入自帶背景
        if self.BackedBmp:
            bmp_W = self.BackedBmp.GetWidth()
            bmp_H = self.BackedBmp.GetHeight()
            backDC = wx.MemoryDC(self.BackedBmp)
            memDC.Blit(0, 0, bmp_W, bmp_H, backDC, 0, 0)
            backDC.SelectObject(wx.NullBitmap)
        memDC.SelectObject(wx.NullBitmap)
        # 設定透明色
        mask = wx.Mask(bmp, wx.Colour(1,1,1))
        bmp.SetMask(mask)
        # 畫下去
        dc.DrawBitmap(bmp, 0, 0, True)
    # 將背景圖填滿 panel
    def OnSize(self,event):
        if self.BackedImage is None:
            return
        W, H = self.GetClientSize()
        if W == 0 or H == 0:
            return
        bmp_W = self.BackedBmp.GetWidth()
        bmp_H = self.BackedBmp.GetHeight()
        if W != bmp_W or H != bmp_H:
            tmpImg = self.BackedImage.Copy()
            tmpImg.Rescale(W, H)
            self.BackedBmp = tmpImg.ConvertToBitmap()
            self.Refresh()
################################################################################
TST_TEXT_LEFT   = 0
TST_TEXT_CENTER = 1
TST_TEXT_RIGHT  = 2

class TStaticText(wx.StaticText):
    def __init__(self,parent,id,label='',
                 pos=wx.DefaultPosition,
                 size=wx.DefaultSize,
                 style=0,
                 labelpos=TST_TEXT_LEFT,
                 name = 'TPStaticText'):
        style |= wx.TRANSPARENT_WINDOW
        wx.StaticText.__init__(self,parent,id,label,pos,size,style=style)
        self.labelpos=labelpos
        self.Bind(wx.EVT_PAINT,self.OnPaint)

    def OnPaint(self,event):
        event.Skip()
        (width, height) = self.GetClientSizeTuple()
        # 建立繪圖緩衝區
        bmp = wx.EmptyBitmap(width, height)
        memDC = wx.MemoryDC()
        memDC.SelectObject(bmp)
        # 塗滿透明色
        memDC.SetPen(wx.Pen('#010101'))
        memDC.SetBrush(wx.Brush('#010101'))
        memDC.DrawRectangle(0, 0, width, height)
        # 加入父窗口背景
        Parent = self.GetParent()
        if Parent:
            # 加入父窗口背景色
            ParentClr = Parent.GetBackgroundColour()
            memDC.SetPen(wx.Pen(ParentClr))
            memDC.SetBrush(wx.Brush(ParentClr))
            memDC.DrawRectangle(0, 0, width, height)
            # 加入父窗口背景圖
            if hasattr(Parent, 'BackedBmp'):
                if Parent.BackedBmp:
                    # get position
                    scr_x,scr_y = self.GetScreenPositionTuple()
                    x,y = Parent.ScreenToClientXY(scr_x,scr_y)
                    pbmp_W = Parent.BackedBmp.GetWidth()
                    pbmp_H = Parent.BackedBmp.GetHeight()
                    w1 = pbmp_W-x
                    if width < w1:
                        w1 = width
                    h1 = pbmp_H-y
                    if height < h1:
                        h1 = height
                    if w1>0 and h1>0:
                        pbackDC = wx.MemoryDC(Parent.BackedBmp)
                        memDC.Blit(0, 0, w1, h1, pbackDC, x, y)
                        pbackDC.SelectObject(wx.NullBitmap)
        # 加入文字
        textXpos = textYpos = 0
        textWidth, textHeight = self.GetTextExtent(self.GetLabel())
        textYpos = (height-textHeight)/2
        if self.labelpos == TST_TEXT_CENTER:
            textXpos = (width-textWidth)/2
        elif self.labelpos == TST_TEXT_RIGHT:
            textXpos = width-textWidth
        memDC.SetFont(self.GetFont())
        #if self.IsEnabled():
        memDC.SetTextForeground(self.GetForegroundColour())
        #else:
        #    memDC.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
        memDC.DrawText(self.GetLabel(), textXpos, textYpos)
        memDC.SelectObject(wx.NullBitmap)
        # 設定透明色
        mask = wx.Mask(bmp, wx.Colour(1,1,1))
        bmp.SetMask(mask)
        # 畫下去
        dc = wx.PaintDC(self)
        dc.DrawBitmap(bmp, 0, 0, True)
################################################################################
class TButtonEvent(wx.PyCommandEvent):
    def __init__(self, eventType, id):
        wx.PyCommandEvent.__init__(self, eventType, id)
        self.isDown = False
        self.theButton = None
    def SetIsDown(self, isDown):
        self.isDown = isDown
    def GetIsDown(self):
        return self.isDown
    def SetButtonObj(self, btn):
        self.theButton = btn
    def GetButtonObj(self):
        return self.theButton
################################################################################
TBB_IMAGE_CENTER = 0
TBB_IMAGE_LEFT   = 1
TBB_IMAGE_RIGHT  = 2
TBB_IMAGE_TOP    = 3
TBB_IMAGE_BOTTOM = 4

TBB_LABEL_CENTER = 0
TBB_LABEL_LEFT   = 1
TBB_LABEL_RIGHT  = 2
TBB_LABEL_TOP    = 3
TBB_LABEL_BOTTOM = 4

class TBitmapButton(wx.PyControl):
    def __init__(self, parent=None, id=-1, image=None, label='',
                 pos = wx.DefaultPosition, size = wx.DefaultSize,
                 style=0, imagepos = TBB_IMAGE_CENTER,
                 labelpos = TBB_LABEL_CENTER,
                 validator = wx.DefaultValidator,
                 name = "Nanohabutton"):
        cstyle = style# | wx.BORDER_NONE
        wx.PyControl.__init__(self, parent, id, pos, size, cstyle, validator, name)

        self.up = True
        self.hasFocus = False
        self.style = cstyle
        if cstyle & wx.BORDER_NONE:
            self.bezelWidth = 0
            self.useFocusInd = False
        else:
            self.bezelWidth = 2
            self.useFocusInd = True

        self.ImagePos = imagepos
        self.LabelPos = labelpos

        self.BackedBmp = None
        if image is not None:
            self.BackedBmp = image.ConvertToBitmap()

        self.MaskClr = wx.Colour(1,1,1)

        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, self.OnEraseBackgound)
        self.Bind(wx.EVT_SIZE,             self.OnSize)

    def SetInitialSize(self, size=None):
        if size is None:
            size = wx.DefaultSize
        wx.PyControl.SetInitialSize(self, size)
    SetBestSize = SetInitialSize

    def DoGetBestSize(self):
        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 (width, height)

    def AcceptsFocus(self):
        return self.IsShown() and self.IsEnabled()

    def GetDefaultAttributes(self):
        return wx.Button.GetClassDefaultAttributes()

    def ShouldInheritColours(self):
        return False

    def Enable(self, enable=True):
        if enable != self.IsEnabled():
            wx.PyControl.Enable(self, enable)
            self.Refresh()

    def SetBezelWidth(self, width):
        self.bezelWidth = width

    def GetBezelWidth(self):
        return self.bezelWidth

    def SetUseFocusIndicator(self, flag):
        self.useFocusInd = flag

    def GetUseFocusIndicator(self):
        return self.useFocusInd

    def InitColours(self):
        faceClr = self.GetBackgroundColour()
        r, g, b = faceClr.Get()
        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):
        wx.PyControl.SetBackgroundColour(self, colour)
        self.InitColours()

    def SetForegroundColour(self, colour):
        wx.PyControl.SetForegroundColour(self, colour)
        self.InitColours()

    def SetDefault(self):
        tlw = wx.GetTopLevelParent(self)
        if hasattr(tlw, 'SetDefaultItem'):
            tlw.SetDefaultItem(self)

    def _GetLabelSize(self):
        w, h = self.GetTextExtent(self.GetLabel())
        if not self.BackedBmp:
            return w, h, True

        w_bmp = self.BackedBmp.GetWidth() + 2
        h_bmp = self.BackedBmp.GetHeight() + 2
        height = h + h_bmp + 12
        if w_bmp > w:
            width = w_bmp
        else:
            width = w
        return width, height, True

    def Notify(self):
        evt = ExtButtonEvent(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, wx.SOLID))
        else:
            dc.SetPen(wx.Pen(self.shadowPenClr, 1, wx.SOLID))
        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, wx.SOLID))
        else:
            dc.SetPen(wx.Pen(self.highlightPenClr, 1, wx.SOLID))
        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 DrawBackedImage(self, dc, width, height, dx=0, dy=0):
        x1 = y1 = 0
         # 建立繪圖緩衝區
        bmp = wx.EmptyBitmap(width, height)
        memDC = wx.MemoryDC()
        memDC.SelectObject(bmp)
        # 塗滿透明色
        memDC.SetPen(wx.Pen(self.MaskClr))
        memDC.SetBrush(wx.Brush(self.MaskClr))
        memDC.DrawRectangle(0, 0, width, height)
        # 加入父窗口背景
        Parent = self.GetParent()
        if Parent:
            # 加入父窗口背景色
            ParentClr = Parent.GetBackgroundColour()
            memDC.SetPen(wx.Pen(ParentClr))
            memDC.SetBrush(wx.Brush(ParentClr))
            memDC.DrawRectangle(0, 0, width, height)
            # 加入父窗口背景圖
            if hasattr(Parent, 'BackedBmp'):
                if Parent.BackedBmp:
                    # get position
                    scr_x,scr_y = self.GetScreenPositionTuple()
                    x,y = Parent.ScreenToClientXY(scr_x,scr_y)
                    pbmp_W = Parent.BackedBmp.GetWidth()
                    pbmp_H = Parent.BackedBmp.GetHeight()
                    w1 = pbmp_W-x
                    if width < w1:
                        w1 = width
                    h1 = pbmp_H-y
                    if height < h1:
                        h1 = height
                    if w1>0 and h1>0:
                        pbackDC = wx.MemoryDC(Parent.BackedBmp)
                        memDC.Blit(0, 0, w1, h1, pbackDC, x, y)
                        pbackDC.SelectObject(wx.NullBitmap)
        # 加入自帶背景
        if self.BackedBmp:
            bmp_W = self.BackedBmp.GetWidth()
            bmp_H = self.BackedBmp.GetHeight()
            backDC = wx.MemoryDC(self.BackedBmp)
            # 背景位置
            if TBB_IMAGE_LEFT == self.ImagePos:
                x1 = dx
                y1 = (height-bmp_H)/2+dy
            elif TBB_IMAGE_RIGHT == self.ImagePos:
                x1 = width-bmp_W+dx
                y1 = (height-bmp_H)/2+dy
            elif TBB_IMAGE_TOP == self.ImagePos:
                x1 = (width-bmp_W)/2+dx
                y1 = dy
            elif TBB_IMAGE_BOTTOM == self.ImagePos:
               x1 = (width-bmp_W)/2+dx
               y1 = height-bmp_H+dy
            else:
               x1 = (width-bmp_W)/2+dx
               y1 = (height-bmp_H)/2+dy
            memDC.Blit(x1, y1, bmp_W, bmp_H, backDC, 0, 0)
            backDC.SelectObject(wx.NullBitmap)
            self.DrawBackedImage2(memDC, dx, dy)
        # 邊框
        x1 = y1 = 0
        x2 = width-1
        y2 = height-1
        self.DrawBezel(memDC, x1, y1, x2, y2)
        # 加入文字
        memDC.SetFont(self.GetFont())
        if self.IsEnabled():
            memDC.SetTextForeground(self.GetForegroundColour())
        else:
            memDC.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
        label = self.GetLabel()
        tw, th = memDC.GetTextExtent(label)
        # 文字位置
        if TBB_LABEL_LEFT == self.LabelPos:
            x1 = dx
            y1 = (height-th)/2+dy
        elif TBB_LABEL_RIGHT == self.LabelPos:
            x1 = width-tw+dx
            y1 = (height-th)/2+dy
        elif TBB_LABEL_TOP == self.LabelPos:
            x1 = (width-tw)/2+dx
            y1 = dy
        elif TBB_LABEL_BOTTOM == self.LabelPos:
            x1 = (width-tw)/2+dx
            y1 = height-th+dy
        else:
            x1 = (width-tw)/2+dx
            y1 = (height-th)/2+dy
        memDC.DrawText(label, x1, y1)
        # 加入Focus效果
        if self.hasFocus and self.useFocusInd:
            self.DrawFocusIndicator(memDC, width, height)
        memDC.SelectObject(wx.NullBitmap)
        # 設定透明色
        mask = wx.Mask(bmp, self.MaskClr)
        bmp.SetMask(mask)
        # 畫下去
        hasMask = bmp.GetMask() is not None
        dc.DrawBitmap(bmp, 0, 0, hasMask)

    def DrawBackedImage2(self, dc, dx=0, dy=0):
        pass

    def DrawFocusIndicator(self, dc, w, h):
        bw = self.bezelWidth
        textClr = self.GetForegroundColour()
        focusIndPen = wx.Pen(textClr, 1, wx.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):
        (width, height) = self.GetClientSizeTuple()
        dx = dy = 0
        if not self.up:
            dx = dy = 1
        dc = wx.PaintDC(self)
        #dc.SetBackground(wx.TRANSPARENT_BRUSH)
        #dc.Clear()
        self.DrawBackedImage(dc, width, height, dx, dy)

    def OnSize(self, event):
        self.Refresh()
        event.Skip()

    def OnEraseBackgound(self, event):
        pass

    def OnLeftDown(self, event):
        if not self.IsEnabled():
            return
        self.up = False
        self.CaptureMouse()
        self.SetFocus()
        self.Refresh()
        event.Skip()

    def OnLeftUp(self, event):
        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):
        if not self.IsEnabled() or not self.HasCapture():
            return
        if event.LeftIsDown() and self.HasCapture():
            x,y = event.GetPositionTuple()
            w,h = self.GetClientSizeTuple()
            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):
        self.hasFocus = True
        self.Refresh()
        self.Update()

    def OnLoseFocus(self, event):
        self.hasFocus = False
        self.Refresh()
        self.Update()

    def OnKeyDown(self, event):
        if self.hasFocus and event.GetKeyCode() == ord(" "):
            self.up = False
            self.Refresh()
        event.Skip()

    def OnKeyUp(self, event):
        if self.hasFocus and event.GetKeyCode() == ord(" "):
            self.up = True
            self.Notify()
            self.Refresh()
        event.Skip()
################################################################################

沒有留言:

張貼留言