Gmane
From: Robin Dunn <robin <at> alldunn.com>
Subject: Re: Using maskededit controls with xrc and wxpyt hon
Newsgroups: gmane.comp.python.wxpython
Date: 2004-06-26 21:16:53 GMT (5 years, 1 week, 1 day, 18 hours and 13 minutes ago)
Will Sadkin wrote:
> Robin Dunn wrote:
> 

>>
>>Here is a basic recipie just off the top of my head.  
> 
> 
> Ok, I'm a little confused by what follows, because I don't have the 
> context for XRC and how it works. Is there a good reference that 
> discusses how this framework works, how to build controls that are 
> compatible with it, and how to test said controls? (I've looked on
> the Wiki, but can't find anything.)

There is some info in this tech-note, but it is mainly about the 
structure of the XML:

http://cvs.wxwidgets.org/viewcvs.cgi/wxWidgets/docs/tech/tn0014.txt?rev=1.9&content-type=text/vnd.viewcvs-markup

There's probably other relevant stuff in the main docs but I'll try to 
summarize as much as possible below.

> 
> Assuming not, I've interspersed questions below, so I can get some
> orientation...Forgive me if I'm being dense, but this is my first
> exposure to creating GUI elements in this way...

First a little architecture discussion...

1) All wxWidgets classes derived from wxWindow have at least two 
constructors.  The one you are familiar with from Python takes all the 
args and fully creates the platform widget as well as the wx class 
instance.  There is also a default constructor that takes no args and 
only creates initializes the wx instance object.  These objects do not 
yet have a platform UI widget created for them yet, and can not be used 
for much until their Create() method is called.  Using the default 
constructor followed by the Create() method is called 2-phase create.

2) wxWidgets also employs a run-time type identification system (wxRTTI) 
for all classes derived from wxObject.  It allows you to, among other 
things, use a class name to find at runtime a factory function for 
creating an instance of that class.  The factory will use the default 
constructor for that class.

3) XRC uses these features to do its magic.  When you use "<object 
class='wxTextCtrl'..." in the XML then it calls an xrc handler for 
wxTextCtrl that creates the instance and then calls Create with the 
values from the other nodes in the XML.  XRC also is able to go one step 
further with the "subclass='classname'" attribute of the <object> tag. 
This allows the C++ programmer to derive a class from wxTextCtrl and 
then tell XRC to create an instance of it instead of wxTextCtrl.  All 
they have to do is use some wxRTTI macros in their class definition. 
XRC's wxTextCtrl handler  will look up class using wxRTTI and then 
creates a first phase instance of JoesTextCtrl (or whatever) and then 
later calls the wxTextCtrl::Create() method to fully create the widget. 
  This is all done with the default SubclassFactory that XRC implements.

For Python classes we have the XmlSubclassFactory_Python defined at the 
end of xrc.py.  It will try to import the module that a subclass is 
defined in and then create an instance of it using no args.  Then later 
XRC will call the base class' Create method to do the 2nd phase of the 
create.

> 
> 
>>Assuming that the class you are working on is called D and it 
>>is derived from some built-in wx class called wx.B:
>>
>>1. Move everything currently in in D.__init__ except the call to
>>wx.B.__init__ into some other method, say _PostInit or something like
>>that.  If you need to pass extra args to _PostInit then be sure that
>>they can be given suitable default values because when you use it in
>>the XRC version you won't be able to pass any args.
>>
>>2. Add a call to the _PostInit method from D.__init__.
> 
> 
> Does the _PostInit() take all the "constructor" arguments that used
> to be handled by the D.__init__?  

Yes.  This way users of D will not see any difference.  The desire here 
is to separate out the guts of the initialization so it can be called 
separatly (as part of the second phase) in the XRC case.

> All of the examples show __init__
> not taking any arguments, (clearly NOT the typical case for the masked 
> edit control set.)

True.

> 
>>3. Make a new class in the same module called PreD, or maybe XrcD or
>>something that derives from D.
>>
>>4. PreD.__init__ will need to take no extra args, and instead of
>>calling D.__init__ you will need to call the "Pre" version of the wx
>>class, like this:
>>
>>	class PreD(D):
>>		def __init__(self):
>>			pre = wx.PreB()
>>			self.PostCreate(pre)
>>
>>Then when an instance of this class is created, the C++ instance is
>>also created, but the UI object will not be created until the
>>Create() method is called, either explicitly from the programmer's
>>code, or implicitly via XRC or something similar.
> 
> 
> Here's where I get confused with the XmlResourceHandler demo you mentioned.
>>From the demo:
> 
> class PreMyCustomPanel(wx.Panel):
>     def __init__(self):
>         p = wx.PrePanel()
>         self.PostCreate(p)
> 
> I can't find wx.PrePanel() discussed anywhere.  Is this a standard
> naming convention on all the base controls?  (This doesn't appear in
> the docs, AFAICT.)

It is the python name for the default C++ constructor.  Each of the C++ 
window classes that have a default constructor (a couple of them don't) 
have a Python wrapper with the "Pre" name.  I chose Pre because it is a 
way to precreating the C++ instance.

>  Do all classes derived from wx base classes have 
> a .PostCreate() method?  

It is defined in the wx.Window class as the following.  It is merely a 
way to do all the internal plumbing necessary to transfer the C++ 
instance from the return value of wx.PreWhatever() into self.

     def PostCreate(self, pre):
         """
         Phase 3 of the 2-phase create <wink!>
         Call this method after precreating the window with the 2-phase 
create method.
         """
         self.this = pre.this
         self.thisown = pre.thisown
         pre.thisown = 0
         if hasattr(self, '_setOORInfo'):
             self._setOORInfo(self)
         if hasattr(self, '_setCallbackInfo'):
             self._setCallbackInfo(self, self.__class__)

> Where do all the construction arguments go?
> How do size, positioning, validators, etc etc. all get handled?

They are passed to Create, which either happens internal to XRC or 
explitily be the programmer.  For example, you could do this in your own 
code if there was ever a need to do so:

	panel = wx.PrePanel()
	panel.Create(parent, ID, size=(100,100))

> 
> 
> 
>>5. Add a handler for the EVT_WINDOW_CREATE event that calls the
>>_PostInit method from step 1:
>>
>>			# this goes in the __init__ above
>>			self.Bind(wx.EVT_WINDOW_CREATE, self.OnCreate)
>>
>>		def OnCreate(self, evt):
>>			self._PostInit()
>>
>>Now when Create() is called the event will be sent and the rest of
>>what used to be in __init__ will be executed and the control should
>>be ready to use.
> 
> 
> This goes in the PreD class, right?

Yes.

> 
> Again, where do the construction arguments go?  Eg: the masked edit
> controls typically have __init__ functions of the form:
>  def __init__( self, parent, id=-1, value = '',
>                   pos = wx.DefaultPosition,
>                   size = wx.DefaultSize,
>                   style = wx.TE_PROCESS_TAB,
>                   validator=wx.DefaultValidator,     ## placeholder provided
> for data-transfer logic
>                   name = 'maskedTextCtrl',

These will all have been already set in the Create(...) call that XRC does.

>                   setupEventHandling = True,        ## setup event handling
> by default
>                   **kwargs):
> 
> where the kwargs configure the semantics of the control.  How
> would these be extracted from the "Create" event?

They won't.  That is why I said that _PostInit (or whatever you want to 
call it) will need to be able to use suitable default values for the 
args because you can't set everything there that you might normally do 
when creating the control yourself from Python code.

> Or would you
> require this to be set up somehow through further XML processing,
> using the controls other methods?  

The extra stuff not handled by Create and defaulted in _PostInit would 
have to be set from the programmer's code.  They can get a reference to 
the control using XRCCTRL(name) and then call a SetThis and SetThat or 
perhaps just use the all-purpose SetCtrlParameters method.

> I just don't understand
> how the XML framework works, I guess...  I'd love a primer!

Hopefully this helps.

> 
> AFAICT, Mignon Laurent's posted changes don't exactly fit this 
> model either; he's changed BaseMaskedTextCtrl() to take all the same
> arguments in __init__, *instantiate* the underlying text control,
> and then call _postInit() above with the rest of the contruction
> arguments.  Viz:
> 

[...]

> 
> In this example, he's "preconstucting"( is that what 'pre' means 
> here?) a raw text ctrl, then calling postInit with default arguments 
> on a "size" event, (rather than a create?) and then hard-coding the
> semantic layout parameters after construction.
> 
> Wouldn't it be better to somehow specify the control params as
> part of the XML?  How would this work?  

Currently it can't as XRC has no facility to handle extra XML nodes that 
it doesn't currently know what to do with.  There is some work going on 
with an XRC2 (or "XTTI") that will let us define the metadata about 
custom classes like this and then XRC will automatically be able to use 
that to know what to expect in the XML and how to use it.

> 
> Also, [how] would derivations off lib.masked.textctrl, eg: IpAddrCtrl
> work, given the above?  What would have to go into each derivation,
> since the hierarchy would then be
> 
> class IpAddrCtrl(lib.masked.textctrl(wx.textctrl)))

I think that the same recipe will work for them too.  The Pre version 
will still need to create the PreTextCtrl and then when the create event 
  happens you just need to do what ever is neccessary to complete the 
initialization of the class.

> 
> if you know what i mean.
> 
> Should this discussion move to wxPython-dev?

Probably a good idea.

-- 
Robin Dunn
Software Craftsman
http://wxPython.org  Java give you jitters?  Relax with wxPython!