Quickstart¶
This guide gets you working with tik.maya in five minutes.
Installation¶
tik.maya is part of the TikWorks repository. Add the src directory to your Maya Python path:
import sys
sys.path.append("/path/to/tikworks/src")
import tik.maya as tm
Dynamic cmds Wrapping¶
tik.maya dynamically wraps the entire maya.cmds module, allowing you to use it as a
drop-in replacement for maya.cmds with minimal changes to existing scripts:
# Traditional approach
import maya.cmds as cmds
# Simply change the import!
import tik.maya as tm
# Now all your cmds calls work through tik.maya
tm.polyCube(name="myCube")
tm.xform("myCube", translation=(1, 2, 3))
tm.setAttr("myCube.translateX", 5.0)
Under the hood, tik.maya uses PEP 562 (module-level
__getattr__) to intercept attribute access. When you call tm.polyCube(), it
dynamically proxies to maya.cmds.polyCube() while intelligently wrapping inputs and outputs.
Key benefits:
Seamless migration: Existing scripts can switch namespaces with minimal changes
Automatic type resolution: Commands that return nodes automatically return typed tik.maya wrappers
Selective overrides: Critical functions like
createNodeuse optimized OpenMaya API implementations for better performanceInput cleaning: tik.maya objects are automatically converted to strings for Maya commands
Output wrapping: Maya node names are automatically wrapped as tik.maya objects when appropriate
# Example: This returns a tik.maya.Transform object, not a string
cube = tm.polyCube(name="myCube")[0]
print(type(cube)) # <class 'tik.maya.types.transform.Transform'>
# You can immediately use object-oriented methods
cube.translate = (1, 2, 3)
cube["visibility"].value = False
Tip
Want to see more? Check out the snippets/comparisons/ folder in the repository
for many side-by-side examples comparing maya.cmds and tik.maya approaches.
Example 08_cylinder_rig demonstrates how an entire rigging script can work by
simply changing import maya.cmds as cmds to import tik.maya as tm!
Wrapping Existing Nodes¶
Use tik.maya.resolve() to wrap any existing Maya node:
import tik.maya as tm
# Wrap a node by name
cube = tm.resolve("pCube1")
# The returned object is typed — Transform, Mesh, Joint, etc.
print(type(cube)) # <class 'tik.maya.types.transform.Transform'>
tik.maya automatically returns the correct wrapper class based on the Maya node type.
Working with Attributes¶
Access attributes using bracket notation:
# Get a Plug object for the attribute
plug = cube["translateX"]
# Read and write values
plug.value = 5.0
print(plug.value) # 5.0
# Or use the property shortcut on transforms
cube.translate_x = 10.0
Attribute plugs have useful properties:
# Lock/unlock
cube["translateX"].locked = True
cube["translateX"].lock() # equivalent
# Visibility in channel box
cube["translateX"].visible = False
# Keyable state
cube["translateX"].keyable = False
Plugs also support mathematical operators to create dependency graph nodes:
# Arithmetic operations create Maya nodes automatically
driver = tm.Transform.create(name="driver")
follower = tm.Transform.create(name="follower")
driver["tx"].value = 10.0
# Create addDL and multDL nodes, then connect to follower
(driver["tx"] * 2.0 + 5) >> follower["ty"]
print(follower["ty"].value) # 25.0
Tip
For comprehensive coverage of plug operations including all mathematical operators, connections, and advanced patterns, see Working with Plugs and Attributes.
Connecting Attributes¶
Use the >> operator for connections:
locator = tm.resolve("locator1")
cube = tm.resolve("pCube1")
# Connect translate
locator["translate"] >> cube["translate"]
# Chain connections
a["output"] >> b["input"] >> c["input"]
Or use the explicit method:
locator["translateX"].connect(cube["translateX"])
Creating Nodes¶
Use the create() class method on type classes:
# Create a transform
grp = tm.Transform.create(name="myGroup")
# Create a joint
jnt = tm.Joint.create(name="arm_jnt")
# Create a locator (returns the shape node)
loc = tm.Locator.create(name="myLocator")
loc.transform.translate = (1, 2, 3) # Access parent transform
# Create geometry (pass the Maya command as first argument)
sphere = tm.Mesh.create("polySphere", name="mySphere")
plane = tm.Nurbs.create("nurbsPlane", name="myPlane")
Note
Shape types like Locator, Mesh, and Curve return shape node wrappers.
Access the parent transform via the .transform property.
DAG Hierarchy¶
Navigate the scene hierarchy:
transform = tm.resolve("pCube1")
# Get parent
parent = transform.parent
# Set parent
transform.parent = tm.resolve("group1")
# Get children
for child in transform.children:
print(child.name)
# Get shapes
for shape in transform.shapes:
print(shape.name, type(shape))
Transform Operations¶
Common transform operations are built in:
# Read transforms
print(cube.translate) # MVector
print(cube.rotate) # MVector
print(cube.scale) # MVector
# Write transforms
cube.translate = (1, 2, 3)
cube.rotate = (0, 45, 0)
cube.scale = (2, 2, 2)
# Snap to another transform
cube.snap_to(target, position=True, rotation=True)
# Freeze transformations
cube.freeze(translate=True, rotate=True, scale=True)
# Get matrices
local_matrix = cube.matrix
world_matrix = cube.world_matrix
Node Lifecycle¶
# Check existence
if cube.exists():
print("Node exists")
# Rename (reference stays valid!)
cube.rename("newCubeName")
print(cube.name) # "newCubeName"
# Delete
cube.delete()
Adding Custom Attributes¶
# Add an attribute
cube.add_attr("customFloat", attributeType="float", defaultValue=0.0)
# Access it
cube["customFloat"].value = 1.5
# Delete it
cube.delete_attr("customFloat")
Working with Shapes¶
tik.maya provides shape-specific functionality:
# Mesh operations
mesh = tm.resolve("pCubeShape1")
vertices = mesh.vertices(space="world")
nearby = mesh.vertices_in_radius((0, 0, 0), radius=1.0)
mesh.unlock_normals(soften=True)
# Curve operations
curve = tm.resolve("curveShape1")
cvs = curve.cvs(space="world")
Advanced Example: Controllers and Panels¶
This example combines a controller role, the control shape library, hierarchy
utilities, and a panel construct to build a small rig preview setup. The shape
library returns a list of available shape names that you can pass into
Controller.create. The panel accepts a camera name, camera shape, or wrapper
("persp" refers to Maya’s default perspective camera) along with a window
resolution in pixels.
import tik.maya as tm
from tik.maya.roles.controller import Controller
from tik.maya.utils.control_shapes import ControlShapeLibrary
from tik.maya.constructs import Panel
# Build a simple joint chain
root_joint = tm.Joint.create(name="spine_root")
mid_joint = tm.Joint.create(name="spine_mid", parent=root_joint)
end_joint = tm.Joint.create(name="spine_end", parent=mid_joint)
# Browse available shapes in the library
shape_library = ControlShapeLibrary.get_instance()
available_shapes = shape_library.list_shapes()
print(available_shapes) # ["Circle", "Square", "CubePin", ...]
# Create a controller with a library shape
if not available_shapes:
raise RuntimeError("No control shapes found in the library.")
shape_name = available_shapes[0]
ctrl = Controller.create(name="spine_ctrl", shape=shape_name, size=2.0)
ctrl.color = (0.9, 0.2, 0.2)
# Align and connect to the joint chain
ctrl.transform.snap_to(root_joint, position=True, rotation=True)
ctrl.transform["translate"] >> root_joint["translate"]
ctrl.transform["rotate"] >> root_joint["rotate"]
# Lock scale attributes to prevent unintended joint scaling
# collect_hierarchy returns wrapped DAG nodes under the root.
for joint in root_joint.collect_hierarchy(node_types=["joint"], include_self=True):
for axis in ["scaleX", "scaleY", "scaleZ"]:
joint[axis].locked = True
joint[axis].visible = False
# Spawn a dedicated preview panel
panel = Panel(camera="persp", resolution=(1280, 720), title="Rig Preview")
panel.display_textures = True
panel.grid = False
Next Steps¶
Read Why tik.maya? to understand the design philosophy
Explore TikWorks Package Structure and Responsibilities for the architecture
Browse the API Reference for complete API details