[Edu-sig] Big thanks to Ian Bicking for metaclass ideas for PataPata

Paul D. Fernhout pdfernhout at kurtz-fernhout.com
Mon Jul 3 02:58:20 CEST 2006


Ian-

I just wanted to publicly thank you for your suggestions, help, and 
patience here on this Edusig list getting me (and PataPata) started with 
using metaclasses to define prototypes (using the "class ProtoFoo:" 
syntax), for example your note here:
   http://mail.python.org/pipermail/edu-sig/2006-May/006383.html

I just started using a metaclass approach inspired by your comment there 
for the main WorldCommonTK.py library, viewable here:
http://svn.sourceforge.net/viewcvs.cgi/patapata/trunk/PataPata/WorldCommonTK.py?view=log
After a lot of work :-) I could just open the old code written by hand in 
a more procedural way in PataPata and write it out that live world of 
prototypes in the new metaclass way. (I did lose some comments I may want 
to put back in eventually though, probably in some sort of overall 
"comment" property or in the documentation for specific properties.)

For reference, you can see the old approach here:
http://svn.sourceforge.net/viewcvs.cgi/patapata/trunk/PataPata/WorldCommonTK.py?view=markup&rev=237

So, here is an example from that WorldCommonTK.py file (again, wrapping 
Gregor Lingl's xturtle library) of how you can now define a prototype in 
PataPata using the "class" syntax (and stored as a global to use 
elsewhere) somewhat along the lines your outlined:

     class XTurtleCanvas("Morph"):
         __metaclass__ = world.buildPrototype
         pen = None
         size = (200, 200)
         def _destroyEmbeddedWidget(self):
             if self.widget:
                 if xturtle._canvas == self.widget:
                     xturtle._canvas = None
                 if xturtle.pen == self.pen:
                     xturtle._pen = None
                 self.widget.destroy()
                 self.widget = None
                 self.setAttributeWithoutSideEffects("pen", None)

         def _newWidget(self):
             xturtle._root = self.world.widget
             newCanvas = xturtle.ScrolledCanvas(self.container.widget)
             xturtle._canvas = newCanvas
             self.setAttributeWithoutSideEffects("pen", xturtle.Pen())
             xturtle._pen = self.pen
             return newCanvas

         def userInterrupt(self):
             xturtle.TurtleScreen._RUNNING = 0

         def windowFocusIn(self):
             if self.widget:
                 xturtle._canvas = self.widget
                 xturtle._pen = self.pen

         _property_writeFunction_pen = '_store_None'
         _property_copyFunction_pen = '_store_None'
     world.worldGlobals['XTurtleCanvas'] = XTurtleCanvas

[You can also see the older xturtle wrapping approach in my earlier "re: 
News about turtle graphics" post to the Edusig list.]

Notice how I also added inline support for defining properties (anything 
starting with _property_ is processed as a property directive). I also 
used a builder function "__metaclass__ = world.buildPrototype" instead of 
a metaclass, and that builder function is actually a method of an existing 
world; all prototypes need a link back to the world they are in, in the 
current approach, so it was either that or some other initialization code 
with a module or class global. Notice also how what would normally be 
shared class variables (e.g. "pen" or "size") are instead now more like 
local prototype "instance" variables.

And here is actually using the global prototype in another file 
(WorldExampleTK.py):
http://svn.sourceforge.net/viewcvs.cgi/patapata/trunk/PataPata/WorldExampleTK.py?view=markup

     class proto17("XTurtleCanvas"):
         __metaclass__ = world.buildPrototype
         pen = None
         position = (38, 377)
         widget = None

For reference, I made a decision a long while back to explicitly require 
naming prototypes before you could derive from them (for development 
clarity). There is no reason you could not link directly to other 
prototypes, and I used to do that, and it was more work to make the 
indirect lookup through a globals dictionary, but I'm hoping that 
explicitness make "documentation of intent" clearer.  So, what is going on 
here isn't quite copying or cloning, it is setting a "self.traits" array 
to have one or more strings, which are then used to look up properties and 
values if they are not locally defined in the prototype itself, so 
something like multiple inheritance from named base classes. You can then 
override behavior locally or otherwise modify the globally named prototype 
you have as a trait.

Anyway, thanks again for your help and suggestions, Ian.

All the best.

--Paul Fernhout


More information about the Edu-sig mailing list