How to create a transparent frame with visible borders?
I would like to create a transparent frame with visible borders. The code I've written based on hasenj's work of 'shaped-frame' (http://hasenj.wordpress.com/2009/04/14/making-a-fancy-window-in-wxpython/) is as follows. But I still have problem making one. I must have missed something. Could anybody point out what's wrong with the program? Thanks.
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)
开发者_StackOverflow中文版 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()
It is not very good idea to draw stuff outside MainLoop. You should setup your wx application so it is Event driven. The events EVT_PAINT and EVT_SIZE should be handled. The wx.Timer
can be used for basic movement.
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()
Edit: Example with ScreenDC using xor function for non-destructible drawing.
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()
Edit: Another possibility is to keep backup copy of that part of the screen.
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()
I would recommend creating a transparent window (window.SetTransparent(0)
), and placing a bitmap on it that you draw to yourself (see this answer for example).
Then you would set an event handler for mouse move, and move around the window with the cursor. Also set a timer, so that you can fade out the bitmap if the mouse doesn't move for x seconds.
精彩评论