Working with Nodes¶
In tik.maya, nodes are the fundamental building blocks. This guide explains how to create, access, and work with different node types.
What is a Node in tik.maya?¶
A node in tik.maya is a Python wrapper around a Maya scene node. Every node is tracked by its UUID and wrapped in a class that corresponds to its Maya type.
import tik.maya as tm
# Resolve a node - returns the appropriate wrapper class
cube = tm.resolve("pCube1") # Returns Transform instance
print(type(cube)) # <class 'tik.maya.types.transform.Transform'>
Understanding Node Types¶
tik.maya automatically wraps Maya nodes in type-specific classes. The type of a node in tik.maya context refers to the Python wrapper class, which corresponds to the Maya node type.
The registry system resolves the correct wrapper class based on Maya’s node type:
import tik.maya as tm
# Different Maya node types get different wrapper classes
transform = tm.resolve("pCube1") # Transform wrapper
mesh = tm.resolve("pCubeShape1") # Mesh wrapper
joint = tm.resolve("joint1") # Joint wrapper
locator = tm.resolve("locatorShape1") # Locator wrapper
# Each type has specialized methods
print(transform.translate) # Transform-specific property
print(mesh.vertices()) # Mesh-specific method
print(joint.radius) # Joint-specific property
Node Type Hierarchy¶
tik.maya’s type system mirrors Maya’s node hierarchy:
Node (base wrapper)
├── DagNode (DAG hierarchy nodes)
│ ├── Transform
│ │ └── Joint
│ └── ShapeNode
│ ├── Mesh
│ ├── Curve
│ ├── Nurbs
│ ├── Locator
│ ├── Camera
│ └── Light
└── DependencyNode (DG-only nodes)
├── AnimCurve
├── BlendShape
└── (other dependency nodes)
Node is the base class for all wrappers. It provides core functionality like UUID tracking, attribute access, and existence checking.
DagNode extends Node for nodes in the DAG (Directed Acyclic Graph) hierarchy. It adds parent/child relationships and world/local transformations.
Transform and ShapeNode are specialized DAG nodes. Transforms can have children and shapes, while ShapeNodes are the geometry/rendering components.
Creating Nodes¶
Creating New Nodes¶
tik.maya offers three ways to create nodes, each with different use cases:
Method 1: Type-Specific create() (Most Explicit)
Use the create() class method on any type class for the most explicit approach:
import tik.maya as tm
# Create a transform
grp = tm.Transform.create(name="myGroup")
# Create a joint
jnt = tm.Joint.create(name="spine_01")
# Create shape nodes (returns the shape wrapper)
loc = tm.Locator.create(name="myLocator")
print(type(loc)) # <class 'tik.maya.types.locator.Locator'>
# Access the parent transform via .transform property
loc.transform.translate = (5, 10, 0)
For geometry shapes, pass the Maya creation command as the first argument:
# Create a polygon sphere
sphere = tm.Mesh.create("polySphere", name="mySphere", radius=2)
sphere.transform.translate = (0, 5, 0)
# Create a NURBS plane
plane = tm.Nurbs.create("nurbsPlane", name="myPlane")
# Create a curve
curve = tm.Curve.create("circle", name="myCircle")
Method 2: createNode() or create_node() (cmds-like)
Use tm.createNode() or tm.create_node() for a familiar cmds-style interface:
import tik.maya as tm
# Create using createNode (camelCase alias)
transform = tm.createNode("transform", name="myTransform")
# Or using create_node (snake_case)
camera = tm.create_node("camera", name="myCamera")
# Works with any Maya node type
mult_node = tm.createNode("multiplyDivide", name="myMultiply")
This method automatically resolves the created node to the correct wrapper type.
Method 3: Node.create() (Generic)
Use tm.Node.create() for a generic creation that still resolves to the correct type:
import tik.maya as tm
# Create a camera using Node.create()
camera = tm.Node.create("camera", name="myCamera")
print(type(camera)) # <class 'tik.maya.types.camera.Camera'>
# Create a transform
transform = tm.Node.create("transform", name="myTransform")
print(type(transform)) # <class 'tik.maya.types.transform.Transform'>
Even though you’re calling Node.create(), tik.maya resolves the node type
and returns the appropriate wrapper class (Camera, Transform, etc.).
Wrapping Existing Nodes¶
Use tm.resolve() to wrap nodes that already exist in the scene:
import tik.maya as tm
# By name
cube = tm.resolve("pCube1")
# From a list of names
nodes = tm.resolve(["pCube1", "pSphere1", "joint1"])
# Works with any valid Maya node name
persp_cam = tm.resolve("persp") # Camera transform
persp_shape = tm.resolve("perspShape") # Camera shape
Node Type Resolution Order¶
When resolving nodes (either with tm.resolve() or creating with tm.createNode()/
tm.Node.create()), tik.maya determines the wrapper class based on Maya’s node
inheritance hierarchy.
Maya nodes inherit from multiple base types. For example, a camera node actually inherits these Maya classes in order:
['containerBase', 'entity', 'dagNode', 'shape', 'camera']
tik.maya resolves to the most specific (closest) available wrapper type in the inheritance chain:
If a
Camerawrapper exists, the node is wrapped asCameraIf not, but a
ShapeNodewrapper exists, it’s wrapped asShapeNodeIf not, but a
DagNodewrapper exists, it’s wrapped asDagNodeIf none match, it falls back to the base
Nodewrapper
import tik.maya as tm
# Camera resolves to most specific type available
camera = tm.resolve("perspShape")
print(type(camera)) # <class 'tik.maya.types.camera.Camera'>
# If Camera type didn't exist, it would resolve to ShapeNode
# If ShapeNode didn't exist, it would resolve to DagNode
# As a last resort, it would resolve to Node
This ensures you always get the most specific wrapper available for any node type.
Node Lifecycle¶
Existence Checking¶
Nodes can be deleted, and references can become invalid. Always check existence when needed:
cube = tm.Transform.create(name="tempCube")
# Check if node still exists in Maya
if cube.exists():
print(f"{cube.name} exists")
# Delete the node
cube.delete()
# Now it doesn't exist
print(cube.exists()) # False
Tip
tik.maya tracks nodes primarily using maya.api.OpenMaya.MObject handles
for performance, with UUID as a backup for restoration when the MObject link breaks
(such as after undo/redo operations). This means your Python reference stays valid
even if the node is recreated.
Renaming Nodes¶
Node wrappers survive renames because they track by MObject (with UUID backup), not name:
cube = tm.Transform.create(name="originalName")
print(cube.name) # "originalName"
# Rename the node
cube.rename("newName")
print(cube.name) # "newName"
# The wrapper still works - UUID hasn't changed
cube.translate_x = 10.0 # Works perfectly
Duplicating Nodes¶
Duplicate nodes and get new wrappers:
original = tm.Transform.create(name="original")
original.translate = (1, 2, 3)
# Duplicate returns a new wrapper
duplicate = original.duplicate()
print(duplicate.name) # "original1"
print(duplicate.translate) # (1.0, 2.0, 3.0)
Working with Different Node Types¶
Transform Nodes¶
Transforms are the most common node type. They support hierarchies and transformations:
import tik.maya as tm
# Create transforms
parent = tm.Transform.create(name="parent")
child = tm.Transform.create(name="child")
# Set hierarchy
child.parent = parent
# Transform operations
parent.translate = (0, 5, 0)
parent.rotate = (0, 45, 0)
parent.scale = (2, 2, 2)
# Matrices
local_matrix = parent.matrix
world_matrix = parent.world_matrix
# Freeze transformations
parent.freeze(translate=True, rotate=True, scale=True)
Joint Nodes¶
Warning
Work in Progress: Joint-specific API is still being developed.
Joints extend Transform with rigging-specific features:
# Create a joint chain
root = tm.Joint.create(name="spine_root")
mid = tm.Joint.create(name="spine_mid", parent=root)
tip = tm.Joint.create(name="spine_tip", parent=mid)
# Joint-specific properties
root.radius = 2.0
root.draw_style = 2 # Bone drawing style
# Position joints
mid.translate = (0, 5, 0)
tip.translate = (0, 5, 0)
Shape Nodes¶
Shape nodes contain geometry or visual data. They always have a parent transform:
# Create shapes
mesh = tm.Mesh.create("polyCube", name="myCube")
curve = tm.Curve.create("circle", name="myCircle")
locator = tm.Locator.create(name="myLocator")
# Access parent transforms
mesh.transform.translate = (0, 0, 0)
curve.transform.rotate = (0, 90, 0)
# Shape-specific operations
vertices = mesh.vertices(space="world")
cvs = curve.cvs(space="object")
Mesh-Specific Operations¶
mesh = tm.resolve("pCubeShape1")
# Get vertex positions
verts = mesh.vertices(space="world")
print(f"Vertex count: {len(verts)}")
# Find vertices in radius
nearby = mesh.vertices_in_radius(center=(0, 0, 0), radius=5.0)
# Unlock normals
mesh.unlock_normals(soften=True)
Curve-Specific Operations¶
curve = tm.resolve("curveShape1")
# Get CV positions
cvs = curve.cvs(space="world")
# Query curve properties
degree = curve.degree
form = curve.form
Camera Nodes¶
# Create a camera (returns the shape)
cam = tm.Camera.create(name="myCamera")
# Camera-specific properties
cam.focal_length = 50.0
cam.horizontal_film_aperture = 1.417
# Position the camera via its transform
cam.transform.translate = (10, 10, 10)
cam.transform.rotate = (0, 45, 0)
Dependency Graph Nodes¶
Some nodes don’t participate in the DAG hierarchy. They’re pure dependency graph nodes:
import maya.cmds as cmds
# Create a dependency node
node_name = cmds.createNode("multiplyDivide")
mult_node = tm.resolve(node_name)
# Access as a generic Node wrapper
mult_node["input1X"].value = 5.0
mult_node["input2X"].value = 2.0
print(mult_node["outputX"].value) # 10.0
Node Selection and Queries¶
Listing Scene Nodes¶
tik.maya provides tm.ls() (or tm.list_scene_nodes()), a wrapper for cmds.ls()
that returns resolved tik.maya objects:
import tik.maya as tm
# List all transforms in the scene (returns tik.maya objects)
transforms = tm.ls(type="transform")
for transform in transforms:
print(f"{transform.name} - {type(transform)}")
# List all joints
joints = tm.ls(type="joint")
# You can use all cmds.ls arguments
selected = tm.ls(selection=True)
visible = tm.ls(visible=True)
# Using the snake_case alias
all_meshes = tm.list_scene_nodes(type="mesh")
Selecting Nodes¶
Use tm.select() (or tm.select_nodes()) to select nodes:
import tik.maya as tm
# Create some nodes
cube = tm.Transform.create(name="myCube")
sphere = tm.Transform.create(name="mySphere")
# Select nodes
tm.select([cube, sphere])
# Using the snake_case alias
tm.select_nodes([cube])
# Supports cmds.select kwargs
tm.select([cube], add=True) # Add to selection
Node Comparison and Identity¶
cube1 = tm.resolve("pCube1")
cube2 = tm.resolve("pCube1")
# Same node, different wrapper instances
print(cube1 == cube2) # True (compares UUIDs)
# Direct UUID comparison
print(cube1.uuid == cube2.uuid) # True
# Check if it's the same type
print(isinstance(cube1, tm.Transform)) # True
Common Patterns¶
Batch Operations on Nodes¶
# Get all selected transforms
nodes = tm.ls(selection=True)
# Lock all scale attributes
for node in nodes:
if isinstance(node, tm.Transform):
for axis in ["scaleX", "scaleY", "scaleZ"]:
node[axis].locked = True
node[axis].visible = False
Summary¶
Nodes are Python wrappers around Maya scene nodes, tracked by MObject (with UUID backup)
Types in tik.maya correspond to Maya node types (Transform, Joint, Mesh, etc.)
The registry automatically returns the correct wrapper class based on inheritance order
Three creation methods:
Type.create(),tm.createNode(), andtm.Node.create()Use
tm.resolve()to wrap existing nodesNode wrappers survive renames and namespace changes
Different node types have specialized properties and methods
Navigate hierarchies with
parent,children, andcollect_hierarchy()Use
tm.ls()andtm.select()for scene queries and selection
For detailed information on working with node attributes, see Working with Plugs and Attributes.