Download Using the Maya Python API - Programming Concepts for Visual Effects | VSFX 705 and more Study notes Typography in PDF only on Docsity!
API Guide
Using the Maya Python API
It is possible to write basic scripts that use the wrapper, iterator and function set classes of
the Maya API. These scripts can query and manipulate the Maya model but are not fully
integrated into Maya. A scripted plug-in provides a more complex solution that is tightly
integrated into Maya. In this section, we discuss how to write both basic and scripted plug-in
scripts along with standalone scripts.
As this is a Python based API, knowledge of Python is required.
Importing modules
The Maya Python API is contained in a number of Python modules. You must import the
functionality that you wish to use in your script. Additionally, the Maya Python API lives in the
Maya namespace; therefore, an extra prefix is required. To import the OpenMaya module, run
the following:
import maya.OpenMaya
Help on a module or class
Information can be displayed about any of the modules or classes using the help command.
For example, if you wish to display the class information for MVector, use:
help(maya.OpenMaya.MVector)
It is also possible to display the information of an entire module:
help(maya.OpenMaya)
This operation will take a while to return since the OpenMaya module is very large.
Writing scripts
The Maya Python API modules contain the classes that are available for Python programming.
These classes are separated into different categories and have appropriate naming conventions
to signify their association. Classes include:
MFn
Any class with this prefix is a function set used to operate on MObjects of a particular type.
MIt
These classes are iterators and work on MObjects similar to the way a function set does. For
example, MItCurveCV is used to operate on an individual NURBS curve CV (there is no
MFnNurbsCurveCV), or, iteratively, on all the CVs of a curve.
MPx
Classes with this prefix are all “Proxies”, that is, API classes designed for you to derive from and
create your own object types.
M classes
Most, although not all, of these classes are “Wrappers”. Examples of this class are: MVector,
MIntArray, and so forth.
We can use wrapper and function set classes to write scripts such as the following:
import maya.OpenMaya vector1 = maya.OpenMaya.MVector(0,1,0) vector2 = maya.OpenMaya.MVector(1,0,0) vector3 = maya.OpenMaya.MVector(0,0,2) newVector = vector1 + vector2 + vector print "newVector %f, %f, %f " % (newVector.x, newVector.y, newVector.z)
It is possible to shorten the symbol names used by modifying the import command:
import maya.OpenMaya as OpenMaya vector1 = OpenMaya.MVector(0,1,0)
Scripts can access dependency graph information using the Maya Python API classes. The
following is a script that finds the persp node and prints out its translateX attribute value:
import the OpenMaya module
import maya.OpenMaya as OpenMaya
function that returns a node object given a name
def nameToNode( name ): selectionList = OpenMaya.MSelectionList() selectionList.add( name ) node = OpenMaya.MObject() selectionList.getDependNode( 0, node ) return node
function that finds a plug given a node object and plug name
def nameToNodePlug( attrName, nodeObject ): depNodeFn = OpenMaya.MFnDependencyNode( nodeObject ) attrObject = depNodeFn.attribute( attrName ) plug = OpenMaya.MPlug( nodeObject, attrObject ) return plug
Find the persp camera node
print "Find the persp camera"; perspNode = nameToNode( "persp" ) print "APItype %d" % perspNode.apiType() print "APItype string %s" % perspNode.apiTypeStr()
Print the translateX value
translatePlug = nameToNodePlug( "translateX", perspNode ) print "Plug name: %s" % translatePlug.name() print "Plug value %g" % translatePlug.asDouble()
The example above demonstrates the following:
To instantiate a class, use the fn = OpenMaya.MFnFunctionSet() notation.
MObjects can be created using node = OpenMaya.MObject().
Although Python is a typeless language, you must instantiate the correct type in order
to pass it as a parameter of the class.
Python strings are passed and returned in place of the MString wrapper class.
Note
For the sake of clarity, the example above has omitted error checking.
Scripted plug-ins
Scripted plug-ins allow a developer to create a solution that is tightly coupled with Maya.
Scripted plug-ins allow a developer to support functionality such as the undoing of commands
and the building of appropriate requires lines into the Maya scene file. Another advantage of
using a scripted plug-in is that its functionality is available in both MEL and Python.
loadPlugin helixCmd.py; spHelix();
Writing a scripted plug-in
Writing a scripted plug-in requires the definition of some specialized functions within the
plug-in. The scripted plug-in must:
Define initializePlugin and uninitializePlugin entry points.
Register and unregister the proxy class within these entry points.
Implement creator and initialize methods (as required) which Maya calls to build the
proxy class.
Implement the required functionality of the proxy class. This requires importing the
necessary modules.
The following sections describe these pieces in more detail with examples.
Importing
Python uses the import keyword to include functionality from a module into a script. For
example:
import maya.OpenMaya as OpenMaya import maya.OpenMayaMPx as OpenMayaMPx import sys
It is possible for a scripted plug-in to be split among several files. The import command is used
to load the functionality of the secondary file into the scripted plug-in.
import polyModifier
Any secondary scripts must be located in the same directory as the scripted plug-in.
Scripted plug-in initialization
When a scripted plug-in is loaded, Maya searches for an initializePlugin() function in its
definition. Within this function, all proxy nodes are registered:
Initialize the script plug-in
def initializePlugin(mobject): mplugin = OpenMayaMPx.MFnPlugin(mobject) try: mplugin.registerCommand( kPluginCmdName, cmdCreator ) except: sys.stderr.write( "Failed to register command: %s\n" % kPluginCmdName ) raise
If the initializePlugin() function is not found, the scripted plug-in fails to load. In addition,
during the load, Maya searches for an uninitializePlugin() function. If this is not found, then
the scripted plug-in fails to load.
Scripted plug-in uninitialization
When Maya is attempting to unload the plug-in, the previously found uninitializePlugin()
function is called to unload the resources of the plug-in.
def uninitializePlugin(mobject): mplugin = OpenMayaMPx.MFnPlugin(mobject) try: mplugin.deregisterCommand( kPluginCmdName ) except: sys.stderr.write( "Failed to unregister command: %s\n" % kPluginCmdName ) raise
Creator functions
Creator functions are used to return a derived version of a proxy class to Maya. Virtual methods
are implemented on the derived class which are called from Maya. An example of a class
definition and a creator function is:
class scriptedCommand(OpenMayaMPx.MPxCommand):
...
def cmdCreator(): return OpenMayaMPx.asMPxPtr( scriptedCommand() )
It is very important to call the OpenMayaMPx.asMPxPtr() on the newly created proxy object. This
call transfers ownership of the object from Python to Maya. Program errors will occur if you do
not make this call since Python can unreference this object and destroy it. This will leave a
dangling pointer in Maya.
Class implementation
Implementing a proxy class requires deriving from the Maya Python API object.
class scriptedCommand(OpenMayaMPx.MPxCommand): def init(self): OpenMayaMPx.MPxCommand.init(self) def doIt(self,argList): print "Hello World!"
The scriptedCommand class is derived from OpenMayaMPx.MPxCommand. The constructor or
init method must call the parent class init method. All class methods require self
as the first parameter, followed by the normal argument list. This command’s doIt() method
simply prints out “Hello World!”.
Initialization Functions
Initialization functions are used within scripted plug-ins that define new proxy nodes using the
MPxNode class. The following is an example that demonstrates how to create a simple scripted
plug-in node, the output of which is the sine function.
import math, sys import maya.OpenMaya as OpenMaya import maya.OpenMayaMPx as OpenMayaMPx kPluginNodeTypeName = "spSineNode" sineNodeId = OpenMaya.MTypeId(0x8700)
Node definition
class sineNode(OpenMayaMPx.MPxNode):
class variables
input = OpenMaya.MObject() output = OpenMaya.MObject() def init(self): OpenMayaMPx.MPxNode.init(self) def compute(self,plug,dataBlock): if ( plug == sineNode.output ): dataHandle = dataBlock.inputValue( sineNode.input )
inputFloat = dataHandle.asFloat() result = math.sin( inputFloat ) * 10. outputHandle = dataBlock.outputValue( sineNode.output ) outputHandle.setFloat( result ) dataBlock.setClean( plug )
creator
def nodeCreator(): return OpenMayaMPx.asMPxPtr( sineNode() )
initializer
def nodeInitializer():
input
nAttr = OpenMaya.MFnNumericAttribute(); sineNode.input = nAttr.create( "input", "in", OpenMaya.MFnNumericData.kFloat, 0.0 ) nAttr.setStorable(1)
output
nAttr = OpenMaya.MFnNumericAttribute(); sineNode.output = nAttr.create( "output", "out", OpenMaya.MFnNumericData.kFloat, 0.0 )
method is MPxNode::compute(). In this situation, the Python code would return
OpenMaya.kUnknownParameter.
Classes support slicing
All of the number arrays (MIntArray, MUintArray, MUint64Array, MFloatArray, and
MDoubleArray) support Python-style slicing. For example:
import maya.OpenMaya as OpenMaya array = OpenMaya.MUintArray() for i in range(0,9): array.append( i ) array[2:8:2]
# Result:[2, 4, 6]
Accessing static MObjects of an MPx class
The proxy classes provide some standard information to a developer about the node that is
being used. This includes attribute objects that are used to define the node. To access a static
class MObject in the Maya Python API, similar code can be used:
envelope = OpenMayaMPx.cvar.MPxDeformerNode_envelope
After making this call, the envelope will be an MObject for MPxDeformerNode::envelope.
Messages
Message classes are supported in the Maya Python API. A Python function is passed for the
callback. This function must have the exact number of parameters required by the callback
message. If it does not, an exception will occur when the message is invoked and information
will be written to the console. Client data in the form of a python object can also be passed with
most messages. The following is an example of a message:
Message callback
def dagParentAddedCallback( child, parent, clientData ): print "dagParentAddedCallback..." print "\tchild %s" % child.fullPathName() print "\tparent %s" % parent.fullPathName() print "\tclient data %s" % clientData
Create the mesage
def createParentAddedCallback(stringData): try: id = OpenMaya.MDagMessage.addParentAddedCallback( dagParentAddedCallback, stringData ) except: sys.stderr.write( "Failed to install dag parent added callback\n" ) messageIdSet = False else: messageIdSet = True return id
Call the message creator
messageId = createParentAddedCallback( "noData" )
Modify Parameter Values Instead of Using an Assignment
In Python, it is best to modify a parameter rather than using an assignment. The code below contains an assignment and demonstrates how an error can occur: import maya.OpenMaya as OpenMaya def vectorTest(v): lv = OpenMaya.MVector(1,5,9) v = lv print "%g %g %g" % (v.x,v.y,v.z) v = OpenMaya.MVector() vectorTest(v) print “%g %g %g” % (v.x,v.y,v.z)
The second print command will emit all zeroes. In Python, either modify the parameter value or
write the code so that a new value is returned. Rewrite the vectorTest() function as follows:
def vectorTest(v): lv = OpenMaya.MVector(1,5,9) v.x = lv.x v.y = lv.y v.z = lv.z print "%g %g %g" % (v.x,v.y,v.z)
References to Basic Types
The Maya Python API contains many calls in which return values or parameters are references
to basic types such as: int&, char&, float& etc. In the Maya Python API, all references are
treated as pointers. As a result, special calls are required to create, set and access the values of
these items.
A utility class called MScriptUtil that exists in the OpenMaya.py module is used to create, get
and set values of these types.
Commands with Arguments
Commands with arguments must use the MSyntax and MArgParser classes within a scripted
MPxCommand. See the following code for an example:
import maya.OpenMaya as OpenMaya import maya.OpenMayaMPx as OpenMayaMPx import sys, math kPluginCmdName="spHelix" kPitchFlag = "-p" kPitchLongFlag = "-pitch" kRadiusFlag = "-r" kRadiusLongFlag = "-radius"
command
class scriptedCommand(OpenMayaMPx.MPxCommand): def init(self): OpenMayaMPx.MPxCommand.init(self)
def doIt(self, args): deg = 3 ncvs = 20 spans = ncvs - deg nknots = spans+2*deg- radius = 4. pitch = 0.
Parse the arguments.
argData = OpenMaya.MArgDatabase(self.syntax(), args) if argData.isFlagSet(kPitchFlag): pitch = argData.flagArgumentDouble(kPitchFlag, 0) if argData.isFlagSet(kRadiusFlag): radius = argData.flagArgumentDouble(kRadiusFlag, 0) controlVertices = OpenMaya.MPointArray() knotSequences = OpenMaya.MDoubleArray()
Set up cvs and knots for the helix
for i in range(0, ncvs): controlVertices.append( OpenMaya.MPoint( radius * math.cos(i), pitch * i, radius * math.sin(i) ) ) for i in range(0, nknots): knotSequences.append( i )
Now create the curve
curveFn = OpenMaya.MFnNurbsCurve()
nullObj = OpenMaya.MObject() try:
This plugin normally creates the curve by passing in the
cv's. A function to create curves by passing in the ep's
has been added. Set this to False to get that behaviour.
if True: curveFn.create( controlVertices, knotSequences, deg,
OpenMaya.MFnNurbsCurve.kOpen, 0, 0, nullObj )
def init(self): OpenMayaMPx.MPxToolCommand.init(self) self.setCommandString(kPluginCmdName) self.__delta = OpenMaya.MVector() kTrackingDictionary[OpenMayaMPx.asHashable(self)] = self
3. Retrieve self from the dictionary using the pointer.
Code is in a different class
Pointer is returned
newCmd = self._newToolCommand()
Use pointer to get to self of the command
self.__cmd = kTrackingDictionary.get(OpenMayaMPx.asHashable(newCmd), None)
Set the class variable
self.__cmd.setVector(0.0, 0.0, 0.0)
4. Clean up the tracking dictionary.
def del(self): del kTrackingDictionary[OpenMayaMPx.asHashable(self)]
For examples that demonstrate these principles, see the Development Kit.
Operating System Types
There are some methods in the Maya Python API that require operating system
types. As these are not included in Python, a MStreamUtils class is available for creating and
using these type of objects. Please check the Development kit for examples on how to use this
class.
Calling into the Parent class
Often when writing an MPx proxy class, the scripts will require calling into the parent class. This
is done using notation such as the following:
matrix = OpenMayaMPx.MPxTransformationMatrix.asMatrix(self)
Enum values
Enum values are accessed using a moduleName.className.value notation such as:
OpenMaya.MSyntax.kDouble
Using OpenGL
We have provided a wrapper class MGLFunctionTable for using OpenGL functionality in script
on all of our support platforms. To acquire a reference to this class use the following code:
glRenderer = OpenMayaRender.MHardwareRenderer.theRenderer() glFT = glRenderer.glFunctionTable()
Standalone Scripts
It is possible to write standalone scripts that make use of the wrapper classes and function
sets to modify the Maya model. These scripts are run from the command line. A simple “hello
world” standalone script follows:
import maya.standalone import maya.OpenMaya as OpenMaya import sys def main( argv = None ): try: maya.standalone.initialize( name='python' ) except: sys.stderr.write( "Failed in initialize standalone application" )
raise sys.stderr.write( "Hello world! (script output)\n" ) OpenMaya.MGlobal().executeCommand( "print "Hello world! (command script output)\n"" )
if name == "main": main()
After the standalone is initialized, function sets and wrapper classes can be used to create and
modify a Maya model. This script must be run using the Python executable that is supplied
with Maya. For example:
$MAYA_LOCATION/bin/mayapy helloWorld.py