How to create a transparent frame with visible borders?
Question: 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)
            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()
    

* * *


Answer: 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()
    

