如何创建具有可见边框的透明框架?

时间:2011-04-20 17:14:38

标签: python wxpython

我想创建一个带有可见边框的透明框架。我根据hasenj的'shape-frame'(http://hasenj.wordpress.com/2009/04/14/making-a-fancy-window-in-wxpython/)的工作编写的代码如下。但我仍然有问题。我一定错过了什么。有人能指出这个程序有什么问题吗?感谢。


import wx

class FancyFrame(wx.Frame):
    def __init__(self, width, height):
        wx.Frame.__init__(self, None, style = wx.STAY_ON_TOP | 
                          wx.FRAME_NO_TASKBAR | wx.FRAME_SHAPED,
                          size=(width, height))
        self.SetTransparent(50)
        b = wx.EmptyBitmap(width, height)
        dc = wx.MemoryDC()
        dc.SelectObject(b)
        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(wx.Pen('red', 2))
        dc.DrawRectangle(0, 0, width, height)
        dc.SelectObject(wx.NullBitmap)
        self.SetShape(wx.RegionFromBitmap(b))
        self.Bind(wx.EVT_KEY_UP, self.OnKeyDown)
        self.Show(True)

    def OnKeyDown(self, event):
        """quit if user pressEsc"""
        if event.GetKeyCode() == 27: #27 is the Esc key
            self.Close(force=True)
        else:
            event.Skip()

if __name__ == "__main__":
    app = wx.App()
    FancyFrame(300, 300)
    app.MainLoop()

2 个答案:

答案 0 :(得分:2)

在MainLoop之外绘制东西并不是一个好主意。你应该设置你的wx应用程序,因为它是事件驱动的。应处理事件EVT_PAINT和EVT_SIZE。 wx.Timer可用于基本移动。

import wx

#============================================================================= 
class DrawPanelDB(wx.Panel):
    def __init__(self, *args, **kwargs):
        wx.Panel.__init__(self, *args, **kwargs)

        self.ballPosition = [20, 20]
        self.ballDelta = [1, 1]

        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTime, self.timer)
        self.timer.Start(20)

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)

    #-------------------------------------------------------------------------
    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self)
        dc.SetBackground(wx.Brush(wx.BLACK))
        dc.Clear()
        dc.DrawCirclePoint(self.ballPosition, 20)

    #-------------------------------------------------------------------------
    def OnSize(self, event):
        self.Refresh()
        self.Update()

    #-------------------------------------------------------------------------
    def OnEraseBackground(self, event):
        pass # Or None  

    #-------------------------------------------------------------------------
    def OnTime(self, event):
        self.ballPosition[0] += self.ballDelta[0]
        self.ballPosition[1] += self.ballDelta[1]
        w, h = self.GetClientSizeTuple()
        if self.ballPosition[0] > w:
            self.ballPosition[0] = w
            self.ballDelta[0] *= -1
        if self.ballPosition[1] > h:
            self.ballPosition[1] = h
            self.ballDelta[1] *= -1
        if self.ballPosition[0] < 0:
            self.ballPosition[0] = 0
            self.ballDelta[0] *= -1
        if self.ballPosition[1] < 0:
            self.ballPosition[1] = 0
            self.ballDelta[1] *= -1       
        self.Refresh()

#============================================================================= 
class MainWindow(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = DrawPanelDB(self)
        self.Show()

#============================================================================= 
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()

编辑:使用xor函数进行不可破坏绘图的ScreenDC示例。

import wx

#============================================================================= 
class MainWindow(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.ballPosition = [20, 20]
        self.lastPosition = None
        self.ballDelta = [1, 1]

        self.timer = wx.Timer(self)        
        self.Bind(wx.EVT_TIMER, self.OnTime, self.timer)
        self.timer.Start(20)

    #-------------------------------------------------------------------------
    def OnTime(self, event):
        self.ballPosition[0] += self.ballDelta[0]
        self.ballPosition[1] += self.ballDelta[1]
        w, h = wx.DisplaySize()
        if self.ballPosition[0] > w:
            self.ballPosition[0] = w
            self.ballDelta[0] *= -1
        if self.ballPosition[1] > h:
            self.ballPosition[1] = h
            self.ballDelta[1] *= -1
        if self.ballPosition[0] < 0:
            self.ballPosition[0] = 0
            self.ballDelta[0] *= -1
        if self.ballPosition[1] < 0:
            self.ballPosition[1] = 0
            self.ballDelta[1] *= -1       

        dc = wx.ScreenDC()
        dc.StartDrawingOnTop()
        dc.SetLogicalFunction(wx.XOR) 
        if self.lastPosition is not None:
            dc.DrawRectangle(self.lastPosition[0], self.lastPosition[1], 15, 15)
        self.lastPosition = (self.ballPosition[0], self.ballPosition[1])
        dc.DrawRectangle(self.ballPosition[0], self.ballPosition[1], 15, 15)    
        dc.EndDrawingOnTop()

#============================================================================= 
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()

编辑:另一种可能性是保留该部分屏幕的备份副本。

import wx

#============================================================================= 
class MainWindow(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.ballPosition = [20, 20]
        self.lastPosition = None
        self.ballDelta = [1, 1]
        self.patch = wx.EmptyBitmap(20, 20)

        self.timer = wx.Timer(self)        
        self.Bind(wx.EVT_TIMER, self.OnTime, self.timer)
        self.timer.Start(20)

    #-------------------------------------------------------------------------
    def OnTime(self, event):
        self.ballPosition[0] += self.ballDelta[0]
        self.ballPosition[1] += self.ballDelta[1]
        w, h = wx.DisplaySize()
        if self.ballPosition[0] > w:
            self.ballPosition[0] = w
            self.ballDelta[0] *= -1
        if self.ballPosition[1] > h:
            self.ballPosition[1] = h
            self.ballDelta[1] *= -1
        if self.ballPosition[0] < 0:
            self.ballPosition[0] = 0
            self.ballDelta[0] *= -1
        if self.ballPosition[1] < 0:
            self.ballPosition[1] = 0
            self.ballDelta[1] *= -1       

        dc = wx.ScreenDC()
        dc.StartDrawingOnTop()

        bdc = wx.MemoryDC(self.patch)
        if self.lastPosition is not None:
            dc.Blit(self.lastPosition[0] - 2, self.lastPosition[1] - 2, 20, 20, bdc, 0, 0)
        bdc.Blit(0, 0, 20, 20, dc, self.ballPosition[0] - 2, self.ballPosition[1] - 2)
        self.lastPosition = (self.ballPosition[0], self.ballPosition[1])

        dc.DrawRectangle(self.ballPosition[0], self.ballPosition[1], 15, 15)    
        dc.EndDrawingOnTop()

#============================================================================= 
app = wx.App(False)
win = MainWindow(None)
app.MainLoop()

答案 1 :(得分:0)

我建议您创建一个透明窗口(window.SetTransparent(0)),并在其上放置一个您自己绘制的位图(例如,请参阅this answer)。

然后,您将为鼠标移动设置一个事件处理程序,并使用光标在窗口中移动。还要设置一个计时器,这样如果鼠标不移动x秒,你就可以淡出位图。