Keywords: Maya, Scripting, Python, MEL, PyMel

Common Cases

How to enable command line running

If want to run python script in background process without GUI, you should initialize maya env at first.
On task beginning:

import maya.standalone
maya.standalone.initialize()

On task end:

maya.standalone.uninitialize() 

origin: Initializing and uninitializing in Python

If want to import fbx file in CLI, must load fbx plugin at first, otherwise you would get error RuntimeError: Unrecognized file or RuntimeError: Invalid file type specified: FBX.

Load fbx plugin:

import maya.cmds as cmds

cmds.loadPlugin("fbxmaya")
How to import fbx file
import maya.cmds as cmds
from os.path import join

my_filename = "test.fbx"
my_folder = "C:/workspace/maya"
file_path = join(my_folder, my_filename).replace('\\', '/')

# ReturnNewNodes(rnn) flag will tell us if it added anything
new_nodes = cmds.file(file_path, i=True, rnn=True)
print("Nodes added: {}".format(new_nodes))

# If nothing was added to the scene, import as reference
if not new_nodes:
    cmds.file(file_path, mnc=True, reference=True)
    # Make reference part of the scene
    cmds.file(file_path, importReference=True)

Origin:
https://stackoverflow.com/a/68227985/1645289

How to export fbx file
import maya.mel as mel
import maya.cmds as cmds
# select object wanted to export
cmds.select( 'pCylinder1', visible=True )
#-s means export selection only
mel.eval("FBXExport -f \"{0}\" -s".format("D:/pCylinder1.fbx"))

Don’t export fbx file to system drive in Windows, e.g. C:/test.fbx, and drive character must be upper case. Otherwise script would run failed with error.

Ohter options:

mel.eval('FBXResetExport')
mel.eval('FBXExportBakeComplexAnimation -v 1')
mel.eval('FBXExportInAscii -v 1')
mel.eval('FBXExportFileVersion -v FBX20200')
mel.eval('FBXExportSmoothMesh -v true')
mel.eval('FBXExportUseSceneName -v true')
mel.eval('FBXExportUpAxis y')
mel.eval('FBXExportCameras -v false')
mel.eval('FBXExportLights -v false')
How to get object list

Get all selected objects:

from maya import cmds
selList = cmds.ls(selection= True, long=True) or []
for obj in selList:
    print(obj)

Get all geometry objects:

from maya import cmds
geometryList = cmds.ls(geometry = True, long=True)
for geo in geometryList:
    print(geo)

Get all objects and check object type:

from maya import cmds

## dag=True : We will only get objects which are listed in the outliner, and none of the hidden objects inside of Maya.
objList = cmds.ls(dag=True, long=True)
for geo in objList:
    if cmds.objectType(geo) == "mesh":
        print(geo)
How to rename all mesh objects listed in Outliner

Example:

from maya import cmds

objList = cmds.ls(dag=True, long=True)
lst = []
for obj in objList:
    isMeshTransform = False
    
    if cmds.objectType(obj) == "transform":
        meshList = cmds.listRelatives(obj, children=True, fullPath=True)
        if meshList is not None:
            for mesh in meshList:
                if cmds.objectType(mesh) == "mesh":
                    isMeshTransform = True
                    break
    
    if isMeshTransform:
        lst.append(obj)

for obj in lst:
    newName = "www_#"
    cmds.rename(obj, newName)
How to rename duplicate Objects
import re
from maya import cmds
def renameDuplicates():
    #Find all objects that have the same shortname as another
    #We can indentify them because they have | in the name
    duplicates = [f for f in cmds.ls() if '|' in f]
    #Sort them by hierarchy so that we don't rename a parent before a child.
    duplicates.sort(key=lambda obj: obj.count('|'), reverse=True)
     
    #if we have duplicates, rename them
    if duplicates:
        for name in duplicates:
            # extract the base name
            m = re.compile("[^|]*$").search(name) 
            shortname = m.group(0)
 
            # extract the numeric suffix
            m2 = re.compile(".*[^0-9]").match(shortname) 
            if m2:
                stripSuffix = m2.group(0)
            else:
                stripSuffix = shortname
             
            #rename, adding '#' as the suffix, which tells maya to find the next available number
            newname = cmds.rename(name, (stripSuffix + "#")) 
            print("renamed %s to %s" % (name, newname))
             
        return "Renamed %s objects with duplicated name." % len(duplicates)
    else:
        return "No Duplicates"
         
renameDuplicates()

Origin:
https://erwanleroy.com/maya-python-renaming-duplicate-objects/

How to drop object to floor
import pymel.core as pm

def drop_to_floor():
    """
    Iterate over the user's selection and drop every object to the origin plane.
    """
    for transform in pm.ls(selection=True):
        # This is dirty, but its an easy way to make sure the bounding box is in world space.
        # Create a temp duplicate of the object and freeze its transforms
        temp_dupe = pm.duplicate(transform)[0]
        pm.makeIdentity(temp_dupe, apply=True, translate=True, rotate=True, scale=True, normal=True)
    
        # Query the pivot points and bounding box information, then calculate a new height
        current_position = pm.xform(transform, translation=True, worldSpace=True, query=True)
        bounding_box = pm.xform(temp_dupe, boundingBox=True, query=True, objectSpace=True)
        pivots = pm.xform(temp_dupe, pivots=True, query=True, worldSpace=True)
        new_height = pivots[1] - bounding_box[1]
    
        # Delete the temp and set the original object's new height
        pm.delete(temp_dupe)
        pm.xform(transform, translation=[current_position[0], new_height, current_position[2]], worldSpace=True)

if __name__ == '__main__':
    drop_to_floor()

Origin:
https://www.reddit.com/r/Maya/comments/m7tdzx/comment/grglstl/?utm_source=share&utm_medium=web2x&context=3

How to list all materials used in scene

CLI without graphics:

from maya import cmds
# Doing it with purely cmds
def get_materials_in_scene():
    for shading_engine in cmds.ls(type='shadingEngine'):
        if cmds.sets(shading_engine, q=True):
            for material in cmds.ls(cmds.listConnections(shading_engine), materials=True):
                yield material

Pymel version (GUI input bar):

import pymel.core as pm
def get_materials_in_scene():
    # No need to pass in a string to `type`, if you don't want to.
    for shading_engine in pm.ls(type=pm.nt.ShadingEngine):
        # ShadingEngines are collections, so you can check against their length
        if len(shading_engine):
            # You can call listConnections directly on the attribute you're looking for.
            for material in shading_engine.surfaceShader.listConnections():
                yield material

One line code:

# And because it is stupid, here it is as a one liner
sorted(set(cmds.ls([mat for item in cmds.ls(type='shadingEngine') for mat in cmds.listConnections(item) if cmds.sets(item, q=True)], materials=True)))

Origin:
https://discourse.techart.online/t/list-all-materials-used-in-scene/10185/4

Maya Rigging Script

Create Control
import maya.cmds as cmds

# Function to create a control for each item in given list
# at that item's position and orientation in worldspace.
def CreateControl(joint_name_list=[]):
    for joint_name in joint_name_list:
        circle_name = cmds.circle(n="{0}_CTL".format(joint_name), nr=(1,0,0) )[0]
        group_name = cmds.group(circle_name, n="{0}_OFFSET".format(joint_name) )
        cmds.matchTransform(group_name, joint_name)
        cmds.pointConstraint(circle_name, joint_name)
        cmds.orientConstraint(circle_name, joint_name)
        
# Select desired joints, then call your function.
CreateControl(cmds.ls(sl=1))

## OR ##

# Feed a list of joints, then call your function.
joint_name_list = ["thigh_JNT", "knee_JNT", "ankle_JNT"]
CreateControl(joint_name_list)

## OR ##

# Feed it a list of every joint object in the scene, then call your function.
joint_name_list = cmds.ls(type='joint')
CreateControl(joint_name_list)

Origin:Selecting Joints in hierarchy one at a time in Maya - reddit.com

Issues

Cannot find procedure “internalTimeUnitStringToDisplayFPSString”

Error on executing cmds.file, code:

new_nodes = cmds.file('D:/test01.fbx', i=True, rnn=True, type="FBX")

Log:

Warning: Frame rate mismatch:  The imported scene frame rate 'ntsc' differs from the existing frame rate 'film'.
Imported animation keys may not match scene frames and become fractional.
File read in  0.31 seconds.
Error: line 1: Cannot find procedure "internalTimeUnitStringToDisplayFPSString".

Solution:
Use o=True instead of i=True.

new_nodes = cmds.file('D:/test01.fbx', o=True, rnn=True, type="FBX")
RuntimeError: Unsaved changes.

Error on executing cmds.file(), code:

new_nodes = cmds.file('D:/test01.fbx', o=True, rnn=True, type="FBX")

Log:

RuntimeError: Unsaved changes.

Solution:
Run cmds.file(new=True, force=True) before open new file.

cmds.file(new=True, force=True)
new_nodes = cmds.file('D:/test01.fbx', o=True, rnn=True, type="FBX")

Origin:
https://stackoverflow.com/a/43438675/1645289

References

Blogs

Python maya.cmds模塊代碼示例
https://vimsky.com/zh-tw/examples/detail/python-module-maya.cmds.html

Roy Nieterau
https://gist.github.com/BigRoy

BATCHING ASSETS USING HEADLESS MAYA
https://www.simplygon.com/posts/81ddb551-d62e-4634-b02b-de1fb230a585

Roy Nieterau
https://gist.github.com/BigRoy


“Whatever is rejected from the self, appears in the world as an event.” ― Carl Gustav Jung