Cookbook

Note

The main purpose of the cookbook is to convey the idea of how to use omx, you may need to change some node names or import relevant modules to make it work in your environment.

The import statements are omitted from all the code snippets below. These are two typical module import statements you need:

from AL import omx
from maya.api import OpenMaya as om2

In this cookbook, we always refer to maya.api.OpenMaya as om2, and AL.omx as omx.

See also

XModifier Immediate Mode

The execute mode of omx dictates whether you need to call AL.omx.doIt() manually.

Create DagNode

Use AL.omx.createDagNode() and expect an XNode to be returned.

from AL import omx
# create transform node and its shape:
locatorTransform1 = omx.createDagNode("transform", nodeName="myLoc1")
locatorShape1 = omx.createDagNode("locator", parent=locatorTransform1, nodeName="myLoc1Shape")

# create shape and its parent transform in one go, by default parent transform will have a default name,
# and here the function returns the shape only:
locatorShape2 = omx.createDagNode("locator", nodeName="myLoc2Shape")

# create shape and its parent transform in on go, but return [transform, shape]:
locatorTransform3, locatorShape3 = omx.createDagNode("locator", nodeName="myLoc3Shape", returnAllCreated=True)

omx.doIt() # optional in script editor

Create DGNode

Use AL.omx.createDGNode() and expect an XNode to be returned.

from AL import omx
timeNode = omx.createDGNode("time", nodeName="myTime")
omx.doIt() # optional in script editor

XNode from an existing node

You can use; node name/dag path, om2.MObject, om2.MFnBase or another XNode to construct an XNode. Refer to AL.omx.XNode for more details.

from AL import omx
import maya.api.OpenMaya as om2

# from string:
persp = omx.XNode("persp")
mobj = persp.object()  # get MObject from XNode

# construct XNode from MObject:
persp = omx.XNode(mobj)

# from MFn:
fn = om2.MFnDependencyNode(mobj)
xnode = omx.XNode(fn)

# from another XNode:
xnode = omx.XNode(persp)

Query XNode States

An XNode is not an om2.MObject, instead it is a thin wrapper around it. However, all the methods available in om2.MObject are also available in XNode, plus more. Refer to AL.omx.XNode for more details.

from AL import omx
import maya.api.OpenMaya as om2

# from string:
persp = omx.XNode("perspShape")
print("XNode is an om2.MObject:", isinstance(persp, om2.MObject))
print("Camera api type: ", persp.apiType())
print("Has camera functor: ", persp.hasFn(om2.MFn.kCamera))
print("Camera is null: ", persp.isNull())
print("Camera is valid: ", persp.isValid())
print("Camera MObject: ", persp.object())

Access to Plug

One way is to get an XNode, then you get access to the XPlug from it. Starting from 1.0.10, you can also construct an XPlug directly from the “node.attribute” string.

from AL import omx
persp = omx.XNode("persp")
visXPlug = persp.visibility                                     # normal plug
wmXPlug = persp.wm[0]                                           # array element by logical index
bgColorRXPlug = persp.backgroundColorR                     # compound child
bgColorGXPlug = persp.backgroundColor.child(1)             # compound child
bgColorGXPlug = persp.backgroundColor['backgroundColorB']  # compound child

focalLengthXPlug = omx.XPlug("perspShape.focalLength")          # directly from string

Get & Set Plug Value

An XPlug is actually an instance of om2.MPlug, this means you have access to all of the om2.MPlug methods, and you can use XPlug whenever an om2.MPlug is needed. Refer to AL.omx.XPlug for more details.

from AL import omx

persp = omx.XNode("persp")
print("World matrix:", persp.wm[0].get())
persp.visibility.set(not persp.visibility.get())

Connection

The connection methods on XPlug will unlock the destination plug if it is locked, and disconnect it if it’s already connected when the argument force=True.

from AL import omx
persp = omx.XNode("persp")
side = omx.XNode("side")

# connection
persp.t.connectTo(side.t, force=True)
side.r.connectFrom(persp.r)

# disconnection
side.r.disconnectFromSource()

Undo & Redo

Read the Undoability document to know how undo & redo actually works.

from AL import omx
transform = omx.createDagNode("transform", nodeName="myLoc1")
shape = omx.createDagNode("locator", parent=transform, nodeName="myLoc1Shape")
omx.doIt() # optional in script editor

# In immediate mode, undo and redo are automatically handled by Maya. But when you manage the
# undo and redo yourself by using omx.currentModifier(), you need to call these:
omx.currentModifier().undoIt()
omx.currentModifier().doIt()  # here calling omx.doIt() is the same.

Getting om2.MFn Functors

XNode comes with convenience methods to get the basic or best om2.MFn functors. But since 1.1, using XFn is recommended.

from AL import omx
# retrieve basic functor, om2.MFnDependencyNode for DG node and om2.MFnDagNode for DAG node:
print("basic functor for dag:", omx.XNode("persp").basicFn())
print("basic functor for dg:", omx.XNode("time1").basicFn())

# retrieve the most type-specific functor:
print("basic functor for transform:", omx.XNode("persp").bestFn())
print("basic functor for camera:", omx.XNode("perspShape").bestFn())

# retrieve the universal XFn, which gives you more convenience methods along with all the functionality of basic and best functors above.
print("XFn for transform:", omx.XNode("persp").xFn())
print("XFn for camera:", omx.XFn("perspShape"))

Stringification

AL.omx.XNode and AL.omx.XPlug both support stringification, when used in print() or logger, it will be converted to a nice-formed string.

from AL import omx
node = omx.XNode("persp")
visPlug = node.visibility
print("node:", node)        # the minimum dag path or dg name will be used.
print("plug", visPlug)      # minimumDagPath.plugLongName will be used.

Iteration In Dag Hierarchy

With AL.omx.XFn, you can iterate all the parents, children, ancestors, descendants and shapes of the node in the DAG hierarchy.

from AL import omx
cameraFn = omx.XFn("perspShape")
# Get parent at index 0 by default.
parent = cameraFn.getParent()
print("Parent for perspShape:", parent)

# Get child by index:
child = parent.xFn().getChild(0)
print(f"Child of {parent}: {child}")

# Iterate all children:
for child in parent.xFn().iterChildren():
    print(f"Child of {parent}: {child}")

# Iterate all ancestors:
for ancestor in cameraFn.iterAncestors():
    print(f"Ancestor of {cameraFn.xnode()}: {ancestor}")

# Iterate all descendants:
for descendant in parent.xFn().iterDescendants():
    print(f"Descendant of {parent}: {descendant}")

# Get shape:
shape = parent.xFn().getShape()
print(f"Shape of {parent}: {shape}")

Iteration Plugs on Node

Both AL.omx.XNode and AL.omx.XFn supports iteration over plugs that match certain criteria.

from AL import omx
camera = omx.XNode("perspShape")

# Iterate all plugs:
for i, plug in enumerate(camera.iterXPlugs()):
    print(f"Plugs[{i}]: {plug}")

# Iter all visible angle xplugs
for p in camera.iterXPlugs(attrType=omx.XAttrType.ANGLE, states=omx.XPlugState.VISIBLE):
    print("Visible angle plug:", p)


# Iter all angle xplugs that are either connected as destination or locked
for p in camera.iterXPlugs(attrType=omx.XAttrType.ANGLE|omx.XAttrType.DOUBLE, states=omx.XPlugState.DESTINATION|omx.XPlugState.LOCKED):
    print("Connected or locked angle or double plug:", p)


# Iter all dynamic or connected destination xplugs but they need to be visible:
for p in camera.iterXPlugs(states=(omx.XPlugState.DYNAMIC|omx.XPlugState.DESTINATION, omx.XPlugState.VISIBLE)):
    print("Dynamic visible plug:", p)


# Further predicate:
predicate = lambda xplug : str(xplug).endswith("Aperture")
for p in camera.iterXPlugs(states=omx.XPlugState.VISIBLE, predicate=predicate):
    print("Visible plug that related to aperture:", p)