In most applications, frames are rectangles because rectangles have a nice regular shape and are relatively simple for an application to draw and maintain. Sometimes, though, you need to break out of the straight line straitjacket. In wxPython, you can give a frame an arbitrary shape. If an alternate shape is defined, the parts of the frame that are outside the shape are not drawn, and do not respond to mouse events; as far as the user is concerned, they are not part of the frame. Figure 8.9 displays a sample shaped window, displayed against a backdrop of the code in the text editor.
Events are set up so that a double-click toggles the non-standard shape on and off, and a right-click closes the window. This example uses the images module from the wxPython demo as the source of the image of Vippi, the wxPython mascot.
Listing 8.10 displays the code behind the non-rectangular frame (assuming that you can't read it behind the mascot in figure 8.9). This example is slightly more elaborate than some of the others we've seen, to display how to manage things like window closing in the absence of typical window interface decorations.
Listing 8.10 Drawing the shaped window import wx import images
"Shaped Window", wx.SIMPLE BORDER
wx.Frame._init_(self, None, -1, style = wx.FRAME_SHAPED | wx.FRAME_NO_TASKBAR) self.hasShape = False self.bmp = images.getVippiBitmap() self.SetClientSize((self.bmp.GetWidth() 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_RIGHT_UP, self.OnExit) self.Bind(wx.EVT_PAINT, self.OnPaint)
Acquiring the image ho self.bmp.GetHeight()))
Drawing the image
def SetWindowShape(self, evt=None): r = wx.RegionFromBitmap(self.bmp) self.hasShape = self.SetShape(r)
def OnDoubleClick(self, evt): if self.hasShape:
Setting the shape d
Binding the window create event self.SetShape(wx.Region()) <1—i „ ...
self_hasShape = False © the'shlpe else:
def OnPaint(self, evt): dc = wx.PaintDC(self) dc.DrawBitmap(self.bmp, 0,0, True)
def OnExit(self, evt): self.Close()
if __name__ == '__main__': app = wx.PySimpleApp() ShapedFrame().Show() app.MainLoop()
O After getting the image from the images module, we set the size of the inside portion of the window to the size of the bitmap. You can also create the wxPython bitmap from a regular image file, which will be discussed in more detail in chapter 16.
C In this case, we're drawing the image in the window. This is by no means an inevitable choice. You can place widgets and text inside a shaped window just like any other one (although they must be inside the shape region).
d This event, which forces a call to SetWindowShape() after the window is created, is redundant on most platforms. However, the GTK implementation requires that the native UI object for the window be created and finalized before the shape is set, so we use the window create event to be notified when that happens and set the shape in its handler.
O We use the global method wx.RegionFromBitmap to create the wx.Region object needed to set the shape. This is the easiest way to create an irregular shape. You can also create a wx.Region from a list of points that define a polygon. The transparent portion of the image's mask is used as the boundary for the purpose of defining the region.
© The double-click event toggles the shape of the window. To return the shape to the normal rectangle, call SetShape() with an empty wx.Region as the argument.
Except for the behavior around the edges and the fact that it doesn't have a normal close box or title bar, the shaped frame behaves like an ordinary frame. Any frame can change its shape, since the SetShape() method is part of the wx.Frame class, it would be inherited by any subclass. A shaped frame is particularly effective in a wx.SplashScreen.
Was this article helpful?