wxPython is a wrapper around the wxWidgets C++ framework. This relationship means that inside most wxPython objects there is a C++ object. Because of this, methods that belong to wxPython classes cannot always be overridden in the same way as they can with a normal Python object.
To demonstrate this behavior, this recipe will show how to create a class that will automatically add its children windows to its Sizer
layout. This will be contrasted to a class that does not expose its virtual methods to the Python layer of the class.
To demonstrate the difference in overriding methods, we will create two similar classes first starting with one that derives from the standard Panel
class:
import wx class MyPanel(wx.Pael): def __init__(self, parent): super(MyPanel, self).__init__(parent) sizer = wx.BoxSizer() self.SetSizer(sizer) def AddChild(self, child): sizer = self.GetSizer() sizer.Add(child, 0, wx.ALIGN_LEFT|wx.ALL, 8) return super(MyPanel, self).AddChild(child)
Now we will create a class that is exactly the same except that it derives from the Py
version of the class:
class MyVirtualPanel(wx.PyPanel): """Class that automatically adds children controls to sizer layout. """ def __init__(self, parent): super(MyVirtualPanel, self).__init__(parent) sizer = wx.BoxSizer() self.SetSizer(izer) def AddChild(self, child): sizer = self.GetSizer() sizer.Add(child, 0, wx.ALIGN_LEFT|wx.ALL, 8) return super(MyVirtualPanel, self).AddChild(child)
Now below we have a little sample application that uses the above two classes:
class MyFrame(wx.Frame): def __init__(self, parent, *args, **kwargs): super(MyFrame, self).__init__(parent, *args, **kwargs) # Attributes self.mypanel = MyPanel(self) self.mypanel.SetBackgroundColour(wx.BLACK) self.virtpanel = MyVirtualPanel(self) self.virtpanel.SetBackgroundColour(wx.WHITE) # Setup self.__DoLayout() def __DoLayout(self): """Layout the window""" # Layout the controls using a sizer sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.mypanel, 1, wx.EXPAND) sizer.Add(self.virtpanel, 1, wx.EXPAND) self.SetSizer(sizer) # Create 3 children for the top panel for x in range(3): wx.Button(self.mypanel, label="MyPanel %d" % x) # Create 3 children for the bottom panel for x in range(3): wx.Button(self.virtpanel, label="VirtPanel %d" % x) self.SetInitialSize(size=(300, 200)) class MyApp(wx.App): def OnInit(self): self.frame = MyFrame(None, title="Virtualized Methods") self.SetTopWindow(self.frame) self.frame.Show() return True if __name__ == "__main__": app = MyApp(False) app.MainLoop()
Running this code will result in a window like the following one being displayed:
In each version of our Panel
class we override the AddChild
method, which is called every time that a window has a new child window created. AddChild
is called inside the C++ part of the class when this happens, so in order to be able to override the method in our Python version of the class, we need to use the special version that provides access to overriding the virtualized method from the C++ class.
The classes in wxPython that have a version of the class prefixed with Py
have the virtualized versions of many of the methods exposed, so that when they are overridden in a Python subclass they get bound to the method in the C++ layer of the object and will be called by the framework instead of the base class's implementation.
This can be seen in the screenshot of our recipe application that was shown above. The top version of the class that does not derive from PyPanel
has all three of its Button
s stacked on top of each other in the top left-hand corner of the window, because its overridden AddChild
method is never called. On the other hand, the version of the class that does derive from PyPanel
has its AddChild
method called and is able to lay out the Button
s in its Sizer
.
It is not well documented as to which methods are exposed as virtual methods and which ones are not. Here is a little trick that can help you to identify which virtual methods are available in a given class. Just run the following code inside the Python interpreter:
import wx for method in dir(wx.PyPanel): if method.startswith('base_'): print method
The argument in the dir()
call can be changed to whatever class you want to inspect. Running this will print out a list of all of the methods in the class that are virtualized. The base_
methods are generated by SWIG as a part of the wxPython bindings to wxWidgets, and should not be used directly in your code. Instead, the methods without the base_
prefix should be used.
The Creating a custom control recipe in Chapter 10, Creating Components and Extending Functionality, shows more usage examples of overriding virtual methods.
The Using a BoxSizer recipe in Chapter 7, Window Design and Layout, explains how the BoxSizer class can be used to perform the layout of controls in a window.