_selfand common idioms
Properties in Field are very important (as you might be able to see from the developer documentation and the advanced customization documentation). They are the glue that holds Field's plugin architecture together, but they might also be the glue that holds your work in Field together. There are one of the core concepts that Field adds to the text-editor + interpreter paradigm it builds upon.
All 'visual elements' (all boxes that you create in the canvas that contain code that you edit in The text editor) can have properties that are stored locally to that 'box'. Some properties persist when you save the sheet (they are actually stored in the sheet itself), some properties are stored and version tracked so you can roll back to previous versions. Even the code that you type into the box is a property (a version tracked one at that).
As you might expect, their access from Python code has been made very convenient. They essentially introduce another scope into Python accessible through the special variable
someProperty to 5. By default, this is stored inside this
iVisualElement and it's persisted to disk. Each of the following code snippets set a property, and demonstrate some naming conventions concerning them.
_self.someOtherProperty_ = "10"
someProperty_ to "10", and this too is stored inside
iVisualElement but, because of the trailing
_ it isn't saved to disk. This is important because many things you might want to transitorily store in a property can't be persisted to disk.
_self.someVersionedProperty_v = Vector4(1,1,1,2)
someVersionedProperty_v to this
Vector4, it's stored locally, persisted to disk, and version controlled (hence the
_v). (Specifically, it's broken out of the sheet save file and exposed separately to the Mercurial-based version control system). This means that you can enquire and perhaps even visualize its value over long periods of time.
_self.someProperty_i = "inspect me"
someInspectedProperty_i to "inspect me" which, because of the trailing
_i appears prominently in the Inspector window (thanks to the
InspectorPlugin) where it can be edited:
Also menu items:
def printed(): print "menu selected" _self.someMenuItem_m_ = printed
makes a new menu item "someMenuItem" in the right-click menu for this element that, when clicked, runs the function
printed, all thanks to the trailing
_m (and the
Boxes are actually connected in a graph structure. Elements can have parents and children. If a property is missing at one level, the search continues 'upwards' until a property is found. This is how plugins (which are just elements near the top of the pile) extend the functionality of many elements. This structure is revealed when we select a box with non-default connections:
We can edit this structure using a special mouse tool:
We can either select it with the mouse or just press and hold 'D'. Then drag with the mouse a connection between the two boxes:
When asked to get properties (rather than set them)
_self does the full graph-delegation lookup:
Ultimately gets hold of the
PythonPlugin near the top of the graph. This is useful to the plugin writer — it provides a uniform way for elements' code to find your plugin.
In the image above,
_self.something executed 'in' element 'a1' might pick up a 'default' provided by element 'A' if there's no
something stored locally.
Finally, some example property idioms:
_self.subelements["banana1"].python_source_v ='print"hello world!"'
sets the code "contained" in the sub element called "banana1" to be
print "hello world!". There's also
_self.all for a map, by name, of everything in the sheet and
_self.find for a "map", by regular expression over name, of everything in the sheet.
camera = (_self.defaultCamera_ = _self.defaultCamera_ or BaseCamera())
sets a (transitory) property "defaultCamera_" to a new BaseCamera, unless it's already been set (either here, previously, or by an element further up the graph)
_self.root.globalDefaultColor = Vector4(1,1,1,1)
sets a property "globalDefaultColor" to white at the root of the sheet, so that all other elements that write
_self.globalDefaultColor obtain Vector4(1,1,1,1) (unless it has been overwritten locally by them).
These kinds of pseudo-hierarchical delegation allow control over processes (or colors, or code, or ...) represented in the canvas to take place at various granularities — remember, the dispatch "hierarchy" is in actual fact a directed-graph (cycles in the graph are handled as well as you'd expect, which is to say that they are hard to predict, but they don't lead to an infinite loop).
And for the finest control over properties, consider rewriting the rules directly in Python, inside Field itself — ExtendingFieldInField.
Clearly, Properties are the vital "route in" for much of the functionality that the Field environment offers programers to program on and in the Field environment itself. They are also key to Field's vision of a programing environment that mixes code, instantiation and data. A list of the important properties defined and used by the core plugins is available here.
One last note. Sometimes while building something up you fall into the trap of using global variables (because it's quicker to type) and then regret it — you wish that you'd made all of those
_self.foo_ instead. Things that were previously "global" to you need to be encapsulated somehow. Enter
This is a very powerful command. After this code has been executed global access to
foo is just like writing
_self.foo_. Now you can build up delegation heirarchies around boxes &mdash refactoring the semantics of your boxes without actually editing any code at all. All those global variables become "box local".