[TransWarp] first experiences with Transwarp
Phillip J. Eby
pje at telecommunity.com
Mon Mar 25 23:38:53 EST 2002
At 08:16 PM 3/24/02 +0100, ML wrote:
>Hi Phillip,
>
> > I would advise holding off for a bit on digging too deep into the use of
> > Elements and Features, however, as I'm about to redo their API
> > entirely. After that's done, it should be possible to make some simple
>.. <snip>
>
>i watch your progress with Element/Features ... looks promising :)
It's pretty much done. I need to document the fact that '__proceed__'
isn't of any real use in features' method templates, but it's basically done.
>I have worked on a concept for the GUI with Transwarp .. this is my
>way to evaluate Transwarp for our project ..
>
>precondition is that one uses wxWindows and the new XML-Resource-system.
>
>I have come up with a similar system to RecordManager->RecordType:
>
>all Gui-Objects derive from baseclass GUIObject(SEF.Base) that has a
>class-init
>method that finds it Child-Objects (only classes/instances of
>GUIObject-Interface)
>and stores it in newClass.guiObjects.
May I suggest you try using ActiveDescriptors? I'm trying to get rid of
__class_init__, if I can. I'm finding it better to use active descriptors
which register themselves, in favor of things which are searched for by a
class-init. Partly, this is because having lots of class-init loops slows
things down. Another is that the class-init mechanism is quite
clunky. I'm thinking it's probably better to use __init__ on a metaclass
than class-init. Now that I have all the bugs worked out of metaclass
generation and module inheritance, I want to go back and factor out the few
uses of __class_init__ that I have.
If you take a look at how ActiveDescriptors, ActiveDescriptor, and
MethodExporter work, you'll see that if you make a class that derives from
(ActiveDescriptor, type), you can make a metaclass that will have its
"activate()" class method called when a containing class is defined around
it. At that point, it can modify the containing class, instantiate
something, register itself in an attribute of the containing class, etc.
I would recommend this approach in favor of __class_init__, because it
means all the "active" components of a class can be found and activated
with a single loop in the ActiveDescriptors metaclass.
>a <root-class>.setup() method set's up the whole GUI-Object-Tree recursivly.
>by invoking <child>.setup(). this is actually needed because i make heavy
>use of
>SEF-Bindings that don't exist at __init__ and some wxPython issues.
Hm. Keep in mind that if you use either Service or App as a base class for
an item, it will be instantiated upon reference. That means, for example,
if you need to do something like this:
class aFrameClass(someGUIClassBasedOnSEF):
class myTextField(GUIFieldClassBasedOnSEF_Service):
...
class myButton(GUIButtonClassBasedOnSEF_Service):
...
subwindows = SEF.bindToNames('myTextField','myButton')
def __init__(self):
# this will automatically create the sub-items
for subwindow in self.subwindows:
...
It should work. Now, if the issue here is that at __init__, the component
doesn't know who its 'getSEFparent()' is, then I understand. Still, you
can use SEF.bindToNames() and SEF.Service to do most of the above, you
would just move the loop to the setup method.
I probably should take a look and see if I can just let DynamicBinding
objects take their parent as an __init__ parameter, as I've thought before
this might be an issue. At one time, though, I had Service being used as a
mixin with classes that needed a different init signature... but I think
it might be safe now to require a parent object to be passed in.
>I created BaseClasses for Window,Panel,Control,Event,Menu,... and some
>specializations.
>Most of them are Services.
>
>a Menu-Specification would look like:
>-----------------------------------------------------------------------------
>
>[cool stuff snipped]
Very neat.
>- There is an ordering problem .. which Panel will be first in
>newClass.__dict__.items() ?
> i will probably use the attribute guiObjects =['TreePanel','ListPanel']
> to fix this.
Use SEF.bindToNames('TreePanel','ListPanel'). This is a Once attribute
that maps the names to a tuple of the actual objects. It keeps you from
needing an extra level of lookups at runtime.
>a MainWindow example:
>-----------------------------------------------------------------------------
>from TW.API import *
>from EMF.GUI.Basic import *
>from EMF.GUI.Window import MainWindow,loadResource
>
>class AppWindow(MainWindow):
>
> Resources = loadResource('emfgui_wdr.xrc')
> from MyMenu import MainMenu
> from MyPanels import MainPanel
> from EMF.GUI.Event import EventDispatcher
>
>
>setupModule()
>-----------------------------------------------------------------------------
>Notes:
>- loadResource is again a specialization of Once that loads the XML-Resource
Just a suggestion, I'd call it something like "ResourceFile()" or
"ResourceLoader()" if it doesn't load the file right away, just to avoid
confusion for somebody looking at the code later. Or maybe even
"bindToResourceFile()" to imply the similarity to the SEF binding operations.
>.. that's it .. ;-)
Very nice.
>after coming that far .. I'ld like to hear your comment.
I like what I've seen so far. I'd be *very* interested in looking at your
code as a possible starting point for TW's "official" GUI framework at some
point in the future, although I don't have a lot of time for GUI stuff
right now.
>i build the framework without use of the (Data)Elements and Features ..
>because as
>far as i understand they are provided by the specialists of my Application.
>right now there is no connection between the controls and the features of
>an Element and i have not yet found a satisfying solution for this problem.
>
>Do you have a hint, how I could "link" the setter/getter - methods of my
>control
>with an Elements-Feature somewhere up in the object-tree: make use of an
>"Element" Service that provides the current Element or some better idea??
>then you would be able to map the controls-values to the Elements features
>and most of standard-typing would be gone :)
>
>what do you think ??
Well, I have some ideas for this, but they're not real specific as
yet. Here's what I do know.
First, windows can be Elements. They're views onto Elements, and have the
same dynamic of being created and destroyed by the application, rather than
being part of the "application object" the way the main window and app
components like Specialists are. A view element needs to know its data
element. Field bindings can use standard SEF binding elements and
capabilities to access features of an element, using a trick something like
this:
class aField(whatever):
DataOwner = SEF.bindToParent()
def fieldChanged(self):
element = self.DataOwner.Element
getattr(element.__class__, self.featureName).getMethod(element,
'set')(self.value)
This assumes that 'aField' is placed directly inside an item that has an
'Element' attribute which is the element the form is bound to.
In practice, writing this example shows me that the framework really needs
some kind of "more than once" attribute that is bound each time you use it,
or that can be partially bound and leave some part of it dynamic. This
would provide the necessary flexibility for something like a GUI where
things "move around" more than in other application types.
To provide that level of binding sophistication, I'll probably ultimately
end up with something like the Smalltalk "ValueModel" framework, which
includes notification when a value is changed. There is a Java framework
called "XMLTalk" which does data binding in XML in a way that's heavily
based on Smalltalk ValueModels, and I intend to look at it for ideas when I
get around to true ValueModel-like sophistication in TW.
However, until such time as I add those notions into TW proper, you can
roll your own quite easily.
First, dynamic binding. Just use a Python 2.2 property object, e.g.:
def computedBinding(self):
# compute it and return
computedBinding = property(computedBinding)
And to do a partially dynamic binding, just define an intermediate Once
attribute for the static part, and then use a property to get it the rest
of the way.
So that leaves notification. For that, you have to have an object that can
hold a value, such as a form holding a data Element. You could define it
as a Feature, adding "subscribeTo" and "unsubscribeFrom" verbs to the
Feature class, which would give the form "subscribeToElement" and
"unsubscribeFromElement" methods. You could then create a Once binding
which subscribes to its targeted feature, with a callback that updates or
deletes the binding when its targeted feature changes.
It's a bit more work, but the result is quite nice. Heck, at this point
I'm tempted to add an "SEF.ObservableFeature" class to the framework to
support observable features like this in general... it would really come
in handy for GUI frameworks. You could make your data elements have
observable features themselves, so that your GUI would update whenever the
underlying objects change.
It almost makes me dizzy sometimes, thinking about the power of the
TransWarp meta-framework, now that it's so close to being usable. I knew
in principle that all these things were possible with it, but it seems now
like the reality is going to be so much more powerful than I really
believed was possible.
This evening, Ty and I were talking about implementing a reporting criteria
system based on SEF patterns; it's similarly mind-boggling in its
applications to the GUI stuff you're working with. We have a concept for
expressing OQL-like queries using a filter class hierarchy that mimics the
Element class hierarchy of the application domain, using only two basic
kinds of features: value ranges and relationships. Something based on it
will probably replace the current Queries module. It's way too early to
say much about it, however.
More information about the PEAK
mailing list