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 |