pythonwxpythonshaped-window

What is the simplest way to create a shaped window in wxPython?


I'd like to create a simple shaped window in wxPython. More or less I want to do the wx equivalent to Tkinter's self.overrideredirect(1) (It get's rid of the default OS boarder), then round the corners on the window.


Solution

  • There's a shaped frame demo in the wxPython demos. I apologize for the indirect source. They originally came as a windows installer here:

    source code

    You'll want to look at shaped_frame_mobile.py or shaped_frame.py, which both call images.py from that listing for the sample window bitmap. It's not the exact equivalent to overrideredirect since you will have to provide an image to be drawn for the frame, but it could still help you accomplish something similar.

    The important parts are the functions that set the window shape based on the bitmap and handle the wx.EVT_PAINT event:

    def SetWindowShape(self, evt=None):
        r = wx.RegionFromBitmap(self.bmp)
        self.hasShape = self.SetShape(r)
    
    def OnPaint(self, evt):
        dc = wx.PaintDC(self)
        dc.DrawBitmap(self.bmp, 0,0, True)
    

    Edit - Here's an altered shaped_frame_mobile.py that loads the .png image specified in the IMAGE_PATH variable. Change that to point to your image:

    import wx
    
    # Create a .png image with something drawn on a white background
    # and put the path to it here.
    IMAGE_PATH = '/python26/projects/shapedwin/image.png'
    
    
    class ShapedFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, -1, "Shaped Window",
                    style = wx.FRAME_SHAPED | wx.SIMPLE_BORDER )
            self.hasShape = False
            self.delta = wx.Point(0,0)
    
            # Load the image
            image = wx.Image(IMAGE_PATH, wx.BITMAP_TYPE_PNG)
            image.SetMaskColour(255,255,255)
            image.SetMask(True)            
            self.bmp = wx.BitmapFromImage(image)
    
            self.SetClientSize((self.bmp.GetWidth(), self.bmp.GetHeight()))
            dc = wx.ClientDC(self)
            dc.DrawBitmap(self.bmp, 0,0, True)
            self.SetWindowShape()
            self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
            self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
            self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
            self.Bind(wx.EVT_MOTION, self.OnMouseMove)
            self.Bind(wx.EVT_RIGHT_UP, self.OnExit)
            self.Bind(wx.EVT_PAINT, self.OnPaint)
            self.Bind(wx.EVT_WINDOW_CREATE, self.SetWindowShape)
    
        def SetWindowShape(self, evt=None):
            r = wx.RegionFromBitmap(self.bmp)
            self.hasShape = self.SetShape(r)
    
        def OnDoubleClick(self, evt):
            if self.hasShape:
                self.SetShape(wx.Region())
                self.hasShape = False
            else:
                self.SetWindowShape()
    
        def OnPaint(self, evt):
            dc = wx.PaintDC(self)
            dc.DrawBitmap(self.bmp, 0,0, True)
    
        def OnExit(self, evt):
            self.Close()
    
        def OnLeftDown(self, evt):
            self.CaptureMouse()
            pos = self.ClientToScreen(evt.GetPosition())
            origin = self.GetPosition()
            self.delta = wx.Point(pos.x - origin.x, pos.y - origin.y)
    
        def OnMouseMove(self, evt):
            if evt.Dragging() and evt.LeftIsDown():
                pos = self.ClientToScreen(evt.GetPosition())
                newPos = (pos.x - self.delta.x, pos.y - self.delta.y)
                self.Move(newPos)
    
        def OnLeftUp(self, evt):
            if self.HasCapture():
                self.ReleaseMouse()
    
    
    
    if __name__ == '__main__':
        app = wx.PySimpleApp()
        ShapedFrame().Show()
        app.MainLoop()