|
From: Rowland S. <ro...@we...> - 2002-01-11 20:07:13
|
We need some form of component object model for PythonCard that will
allow us to build sophisticated compound components, and will support
the development of a visual application building tool.
I hope this will kick start another round of discussion on what
type of component model we want.
I've been working on some ideas on and off over the past couple of
months with input from Kevin and Jeff Turner. I just got back into it
after a few weeks hiatus, and I think I might be getting overly
complicated, as well as over my head ;)
I've been trying to think of what features would be necessary to support
a visual app tool without really defining the requirements for a visual
tool.
I'm going to step back and write down some requirements here, then
take another look at the ideas presented below.
Visual App Builder Requirements
Basic Features
1. Display a palette of valid component classes.
2. Allow the creation of one or more forms for a particular
application.
3. Allow the creation of simple or compound components that may be
added to the palette of available components.
4. Display a particular component's interface. This includes the
attributes, events, and methods that the component
has published as it's public interface.
5. Allow creation of new instances of a component as elements
of a Form.
6. Allow a user to edit the values of a component
instance's attributes.
7. Persist component instances and their attribute values to
a resource file.
Advanced Features
8. Allow a user to somehow connect the events generated by
one component to the methods published by another component.
Some number of logical 'connector' classes will be required
to allow the wiring together of events and methods
to build complex behaviors.
Component Model Overview
Components publish Interfaces. Each Interface describes the events,
attributes and methods that a Component supports.
class Interface :
def getAttributes( self ) :
def getEvents( self ) :
def getMethods( self ) :
Typing
Currently Python is not a strictly typed language, but in order to
build an intelligent visual app builder, we need to know the types
of component attributes and method arguments in order to build
visual editors that are type aware.
Events
Components generate Events. Components publish the names of the
Events that they support.
Components register Listeners. Components notify registered Listeners
when Events occur. Each Event contains a reference to the Component
that generated the Event.
Attributes
Components have Attributes. Attributes are typed. An Attribute has
and 'access' property. Attribute access can be 'read-only', or
'read-write'. An attribute's access property determines which
access methods will be automatically provided for the Attribute by the
Component model:
'read-only' -get<AttributeName>()
'read-write' -get<AttributeName>() / set<AttributeName>( self, value )
Methods
Methods describe the public methods that a Component supports.
Methods contain Arguments. Each Argument is typed.
Introspection
An Inspector allows the discovery of a Component implementation's
interface, i.e. it's events, attributes and methods.
Each Component instance provides a reference to an Inspector for
that Component's interface via the Component.getInspector() method.
The following is a meta format for PythonCard component specifications,
which are themselves meta data about PythonCard components.
type = [ 'boolean', 'dictionary', 'integer', 'list', 'string', ... ]
{
'events' = [ event-name, ... ],
'attributes' = {
name : {
'type' : type,
'presence' : 'mandatory' | 'optional',
'access' : 'read-only' | 'read-write'
}
},
'methods' = {
name : {
'arguments' : [
{
'name' : string,
'type' : type
}
],
'returns' : type | None
}
},
'interfaces' : [ interface-name ],
'icon' : icon-file-path,
'editor' : editor-class-name
}
- type
A list of the built-in data types supported by the component model,
which should include all of the valid Python types ( but doesn't yet ;)
- events
A list of the names of the events that the component generates.
- attributes
A list of the attributes that make up the component. Each element in the
list is a dictionary with the following keys:
name - The name of the attribute.
type - The data type of the attribute.
presence - A string indicating whether the attribute is
required in a resource description or not.
The valid values are 'mandatory' and 'optional'.
access - A string that specifies whether an attribute can be
read-only or read-write. This property is used to
determine which access methods get generated for the
component ( get | get/set )
- methods
A list of the methods that make up a component. Each element in the
list is a dictionary with the following keys:
name - The name of the method.
arguments - A list of the arguments that the method supports.
Each entry in the list is a dictionary with the
following keys:
name - The name of the argument.
type - The data type of the argument
returns - The data type of the method's return value, or None.
- icon
The path to the file that contains the icon for the component that will
be used to represent the component in visual tools.
- editor-class-name
The name of the class that is used to edit instances of the component.
This will be used by custom components that cannot be edited by some
combination of the default type editors ( string, boolean, integer,
list, etc. ).
A ComponentInspector can pull a spec from the ComponentRegistry,
or from an instance of a component.
The definition of an Interface can be specified outside of a Component's
class definition. This allows for the creation of standard interfaces
that have many implementations.
Example 1 - Timer
class TimerActivated( Event ) :
...
TimerInterface = Interface(
{
'events' = [ 'TimerActivated' ]
'attributes' = {
'`seconds' : {
'type' : 'integer',
'presence' : 'mandatory',
'access' : 'read-write'
}
},
'methods' = {}
'icon' = 'timer.gif'
'editor' = None
}
)
class Timer( Component ) :
interfaces = [ TimerInterface ]
def __init__( self, seconds ) :
self.seconds = seconds
...
class TimerClient ( Listener ) :
def __init__( self, triggerTime ) :
t = Timer( triggerTime )
t.addEventListener( 'TimerActivated', self.timeToGo )
def timeToGo( self, event ) :
print 'it's time to go!'
c = TimerClient( 5 )
# The following methods are implied by the 'seconds' attribute and
# are created by PythonCard when the component is loaded.
print c.getSeconds()
c.setSeconds( 10 )
Talk to ya,
Rowland
|