Introduction

wxWidgets supports doing a 2 stage creation of window objects, which is sometimes necessary for setting extra flags or for class factories like XRC. Since C++ allows overloading of methods (including constructors) it is fairly easy to precreate an instance of a window class and then call its Create method later. But in Python the constructor that is wrapped by __init__ is always the one that does the creation too.

It is also possible to do 2 stage creation of window objects in wxPython, it's just a tiny bit tricky. All of the classes have a special factory function that precreates the object, and then you can later call the object's Create method to do the 2nd stage, (or let XRC or whatever do it for you.) The factory is always the same name as the class, with "Pre" inserted in it, like wx.PreWindow, wx.PreFrame, etc.

Code Sample

Here is an example of deriving a class from wx.Dialog that uses 2-stage creation in order to set extra flags. Notice that wx.Dialog.__init__ is not called, the Create method is called instead.

   1 class MyDialog(wx.Dialog):
   2     def __init__(self, parent, ID, title):
   3         pre = wx.PreDialog()
   4         pre.SetExtraStyle(wx.FRAME_EX_CONTEXTHELP)
   5         pre.Create(parent, ID, title)
   6         self.PostCreate(pre)

As mentioned above, two stage initialization can be used to allow XRC to populate the contents of your wx.Dialog or wx.Frame for you. Here is an example of how you might do that:

   1 class MyFrame(wx.Frame):
   2     def __init__(self, parent):
   3         pre = wx.PreFrame()
   4         wx.xrc.XmlResource.Get().LoadOnFrame(pre, parent, "TheFrameXRCName")
   5         self.PostCreate(pre)

The PostCreate method implements a little BlackMagic that transfers the frame (or dialog) object refered to by pre into self. In other words, afer PostCreate returns then self will look and act just as if you had called the parent class __init__ just like normal, and pre will be disposed of properly. In other words it makes self be a Python proxy for the same C++ object that pre is a proxy for, and then unhooks pre. This was originally announced in the wxPython 2.5 migration guide.

XRC Subclass

Another feature that XRC provides is to be able to use standard class names in the xml, and also specify a custom subclass that should be instantiated instead of the default wx class. In other words, suppose you have a special class that derives from wx.TextCtrl that you want to use in a particular layout, but you also want to use a design tool such as XRCed that knows nothing about your derived class. The way to handle this is to go ahead and use a wxTextCtrl in XRCed but also specify the name of your subclass. Then when XRC loads the xml at runtime it will hunt for that subclass when creating the instance, but it will still be able to treat it as a regular wx.TextCtrl otherwise.

To be able to use subclasses from XRC, the class needs to be able to be constructed via the 2-phase method. Everything works similarly to how it is described above except for one key point, the Create doesn't happen during the life of the __init__ call, so you are not able to call any methods that need a fully created widget until later. One way to work around that is to bind a handler for the wx.EVT_WINDOW_CREATE event, and do the rest of your initialization of the object in that handler. Here is a basic recipie that shows how to do this:

   1 class MySpecialTextCtrl(wx.TextCtrl):
   2     def __init__(self):
   3         pre = wx.PreTextCtrl()
   4         # the Create step is done later by XRC.
   5         self.PostCreate(pre)
   6         self.Bind(wx.EVT_WINDOW_CREATE, self.OnCreate)
   7 
   8     def OnCreate(self, event):
   9         self.Unbind(wx.EVT_WINDOW_CREATE)
  10         # Do all extra initialization here

More Details

The following, taken from a message by Robin on the mailing list, goes into more detail, including "under the covers" material:

[Two-stage creation] is typically something that is used more often from C++. Since C++ supports function overloading most of the UI classes have at least two constructors, one that takes no parameters, and one that takes all the parameters that we are used to using from wxPython. This is useful from C++ because the no-parameter version is what C++ calls the default constructor and will be used automatically in several language constructs unless explicit parameters are specified, for example when a derived class is instantiated and it needs to initialize its base classes. So it basically allows the all the C++ parts of a class (including the base classes) to get initialized, but delay the creation of the actual UI object until later. This also means that the UI creation code only runs once, not for each base class.

The Create function of the class is normally executed from within the non-default constructor (the one that takes parameters) but because of the 2-phase create there is no special dance needed to only do the UI object creation one time, and C++ itself does most of the work. For example the order of function calls when a wxBitmapButton is created goes something like this:

call wxBitmapButton(parent, id, bmp)

The creator of XRC found it convenient to build upon this model and allow the instance that is created be separated from the call to create the UI object, especially when XRC is used to create instances of classes that XRC doesn't already know about, but where it does know about the base class. It uses it essentially as a way to be dynamic from a very non-dynamic language.

[The "pre = wx.PreFrame()" statement] creates a C++ instance of the wxFrame class without calling the code that creates the actual UI object, and pre is a proxy for that C++ object.

[In LoadOnFrame]XRC calls the frame's Create method, and then the contents defined in the xrc file are recursivly created with the frame as the parent, following a similar pattern, (create the instance, and then call Create.) When it is done, the pre frame is now a fully constructed wx.Frame object, which takes us to the next step... turning the frame into 'self' for the class ... which is actually done in the PostCreate method call. Since pre is its own instance of a wx.Frame, but you are working with separate instance of the Main_Frame class, it would be nice to be able to combine them.

Originally I recommended using delegation and forwarding everything to the instance created by XRC, but that became real awkward real fast. So since this is Python and if you can think it you can probably do it, I realized that we could easily transfer everything that makes pre be a wx.Frame and put it in self so self will be that frame, and also be an instance of the Main_Frame class. That magic is done on PostCreate, (and is surprisingly simple.)

See Also

There is a very informative thread from the wxPython-users mailing list about two-stage creation consisting of a question from Will Sadkin and a response from Robin Dunn. Unfortunately the thread seems to die there.

There is also an example of using an XRC subclass in the wxPython demo under Window Layout / XmlResourceSubclass.

Comments

TwoStageCreation (last edited 2008-03-11 10:50:17 by localhost)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.