pyInterface
Code
Introduction
The class pyInterface defines the basic interface between Ygdrasil and
Python, and is used by pyNode,
pyTransform, and
pySelector. Each of these Ygdrasil node classes
uses a pyInterface object to handle common messages and the app() function.
pyInterface defines a Python module "ygdrasil", which contains several
functions corresponding to basic Ygdrasil functions.
It also defines a Python class "ygNode", which provides several member
functions corresponding to the C++ ygNode's member functions.
All Ygdrasil/Python scripts are expected to define a class which is derived from
ygNode or one of the other Ygdrasil/Python classes (currently ygTransform &
ygSelector). An object of this class will be created when the script is loaded,
and its app() member function will be called by ygInterface on each frame.
Example
Here is a very simple example scene and Python script. The script keeps a counter
that increments each frame; when it reaches 1000, the counter resets and the
event "overflow" is generated. The script also includes a function
"ping()" that can is called by a WandTrigger.
Scene file:
pyNode nodeA (script(example.py), when(overflow, print(overflow occurred)))
wandTrigger (when(button1, nodeA.ping))
Script file example.py:
class example(ygNode):
def __init__(self):
self.val = 1
def app(self):
self.val += 1
if (self.val > 1000):
self.eventOccurred('overflow')
self.val = 1
def ping(self):
print name(), 'pinged; val =', self.val
Messages
These messages are available in all Ygdrasil/Python node classes:
- script(filename [, classname])
- Executes the Python script filename, and creates a Python object of
class classname. If no classname is given, the file name, minus its
extension, is used (e.g. for "foo.py", and object of class "foo"
is created). The Python script is searched for in the standard Ygdrasil search
path.
The Python variable for the created object has the same name as the Ygdrasil
node. The variable ygNodeName within the object is also assigned
the node's name as its value.
e.g., if a scene contains "pyNode nodeA (script(foo.py))", the following actions are performed:
[C++:] PyRun_SimpleFile("foo.py")
[Python:] nodeA = foo()
[Python:] nodeA.ygNodeName = 'nodeA'
And then on each frame, the Python command "nodeA.app()" is run.
- reload
- Causes the script previously loaded by script() to be re-executed,
and the node's Python object to be re-created.
- autoReset([bool])
- If bool is true (or omitted), tells pyInterface to automatically reset the
Ygdrasil node any time that the Python script is changed.
On each frame, pyInterface will check the last-modified time on the script file; if
this value changes, the message "reset" is sent to the Ygdrasil node.
This will cause the script to be reloaded, assuming it was included in the node's
initialization messages in the scene file (i.e. not passed to the node by a later
message).
Note: I chose to use the "reset" message, rather than just calling the
reload() function, for the case where call() messages are also used
in the scene file (such as to set values of variables in the script). If reload()
were used instead, these call()s would not be re-run, and the new
instance of the Python node would not be completely initialized. This is probably
an area for further improvement (perhaps using a "scriptChanged" event instead).
This feature is off by default.
- call(function)
- Executes the Python function function on the node's Python object.
e.g., the message "call(xyzzy(1,2))" sent to pyNode "nodeA"
translates into the Python command "nodeA.xyzzy(1,2)".
Note that because of the way this is implemented, it can also be used to assign
values to object member variables. e.g. the message "call(plugh=3)"
sent to "nodeA" translates into the Python command "nodeA.plugh=3".
ygNode Python class
The Python class ygNode has the following member functions:
- name ( )
- Returns the name of the node, which is the same as the name of the Ygdrasil
node that owns this Python object.
- eventOccurred (eventName [,args])
- Equivalent to the C++ function ygNode::eventOccurred().
eventName is the name of the event, and the optional argument
args is a space-separated string of event arguments, of the form
"name=value".
- numChildren ( )
- Returns the number of child nodes of the corresponding Ygdrasil node.
- childName (i)
- Returns the name of child node #i of the Ygdrasil node.
- parentName ( )
- Returns the name of the parent of the Ygdrasil node.
- origin ([otherNode])
- Returns the origin of the node. If otherNode is omitted, the position
is in world coordinates; otherwise, it is relative to the node named otherNode.
Equivalent to the C++ function ygNode::origin(); the C++ pfVec3 is translated into
a Python 3-tuple.
- app ( )
- Does nothing. This is just a placeholder function for any Python node classes
that don't need to define their own app() functions.
Note that some of the functions (childName, parentName, origin)
deal with the names of nodes, where the corresponding
C++ ygNode functions use pointers to actual node objects.
This is done because there is no guarantee that the other Ygdrasil node
being referred to is of a Python-based node class; hence, only
the name of the node is used, rather than a Python ygNode object.
ygdrasil Python module
pyInterface also defines the module ygdrasil, which consists of a set
of functions that provide the actual connection between Python and Ygdrasil.
(This is separate from the ygNode class because of some of the ugly details
of Python's embedding/extending system.)
Some of these functions correspond to ygWorld functions, and so were not
included as Python ygNode functions.
Other functions are primarily used behind the scenes in the Python ygNode class;
however, these can also be called directly if desired, such as to allow a Python
node to get data from some other non-Python Ygdrasil node.
The functions defined in the ygdrasil module are:
- sendMessage (message)
- Sends an Ygdrasil message. message is a string containing the message
to be sent, such as "nodeA.position(1 2 3)".
- frameTime ( )
- Returns the current frame time, in seconds, from ygWorld::FrameTime.
- frameDeltaTime ( )
- Returns the current frame delta time (i.e. the length of the previous frame),
in seconds, from ygWorld::FrameDeltaTime.
- eventOccurred (nodename, event [,args])
- Signals that an Ygdrasil event has occurred for node nodename.
- numChildren (nodename)
- Returns the number of child nodes of Ygdrasil node nodename.
- childName (nodename, i)
- Returns the name of child node #i of the Ygdrasil node nodename.
- parentName (nodename)
- Returns the name of the parent of the Ygdrasil node nodename.
- origin (nodename [,otherNode])
- Returns the origin of the Ygdrasil node nodename, relative to
otherNode. If otherNode is omitted, the position
is in world coordinates.
The position is returned as a 3-tuple.
Bugs
Illegal node names are possible:
Note: this bug has been corrected in Ygdrasil 0.1.7 (except for cases
where a scene file explicitly assigns a node an illegal name).
Because pyInterface uses an ygNode's name for the name of the corresponding Python
variable that it creates, the node name needs to be something that will be a
legal variable name. Normally this is not a problem, but in the case where the
node has not been given a name in the scene, it is assigned one by Ygdrasil.
By default, this name is built from the host name (plus process ID); some systems
are configured to return their fully qualified internet name, rather than just
the basic machine name - e.g. hostname returns "mediastudy1.fal.buffalo.edu"
instead of just "mediastudy1". This causes pyInterface to try to create
a variable with dots in its name, which will fail.
Use the environment variable YG_DUMMYNAME_BASE, or explicity name all Python nodes,
to avoid this for the time being.
Note that really this is a flaw in Ygdrasil, not pyInterface - node names with dots
in them could cause problems elsewhere.
Last updated 26 October 2002.
home page