Maya Python Scripts
Here are a few Python scripts I’ve written for Maya:
Adds velocity graphs to Maya’s Graph Editor. Useful for analyzing your motion.
It makes velocity graphs for all keyframes and expressions in the Scene. Make a shelf button for this and use it to toggle velocity graphs on or off in the Graph Editor.
The velocity is in units per frame. Use the Velocity Display Scale attribute to change the scale of the velocity to be more readable relative to the other date in the graph. (Defaults to 10x.)
The velocity channels are expression-driven so you have to select the velocity channel in the graph editor to see it and you sometimes have to zoom or pan a little in the graph to get it to update.
This does not make velocity graphs for Alembic… that was too difficult and is not really needed if you’re using it while creating animation.
#Create or remove velocity channels for keyframes and expressions from pymel.core import * import sys if not (objExists('velocityChannelStuff')): selection = ls(sl=1) curvs = ls(typ='animCurve') + ls(typ='expression') vChannels = [] dsChannels = [] vExpressions = [] count = 0 if curvs: objs = [] for node in curvs: for connected in listConnections(node+'.output', p=1): objs.append(connected.split('.')[0]) addAttr(connected.split('.')[0], ln=connected.split('.')[-1]+'_velocity', at='float', k=0, h=1) count +1 vExpression = expression(o=connected.split('.')[0], s=connected.split('.')[0]+'.'+connected.split('.')[-1]+'_velocity'+' = (`getAttr -t (frame) '+connected+'` - `getAttr -t (frame-1) '+connected+'`) * `getAttr '+(connected.split('.')[0])+'.vDispScale`', name='vExpr'+str(count)) vChannels.append(connected.split('.')[0]+'.'+connected.split('.')[-1]+'_velocity') vExpressions.append(str(vExpression)) objs = list(set(objs)) for obj in objs: addAttr(obj, ln='vDispScale', nn='Velocity Display Scale', at='float', dv=10, k=1, h=0) dsChannels.append(obj + '.vDispScale') createNode('script', n='velocityChannelStuff') addAttr('velocityChannelStuff', dt='string', ln='vChannelsList') addAttr('velocityChannelStuff', dt='string', ln='dsChannelsList') addAttr('velocityChannelStuff', dt='string', ln='vExpressionsList') addAttr('velocityChannelStuff', dt='string', ln='showResultsState') setAttr('velocityChannelStuff.vChannelsList', ','.join(vChannels)) setAttr('velocityChannelStuff.dsChannelsList', ','.join(dsChannels)) setAttr('velocityChannelStuff.vExpressionsList', ','.join(vExpressions)) setAttr('velocityChannelStuff.showResultsState', animCurveEditor('graphEditor1GraphEd', q=1, showResults=1)) animCurveEditor('graphEditor1GraphEd', edit=1, showResults=1) select(selection) sys.stdout.write('Created Velocity Channels') else: sys.stdout.write('There\'s no animation to add velocity channels to.') else: vChannels = getAttr('velocityChannelStuff.vChannelsList').split(",") dsChannels = getAttr('velocityChannelStuff.dsChannelsList').split(",") vExpressions = getAttr('velocityChannelStuff.vExpressionsList').split(",") for vChannel in vChannels: deleteAttr(vChannel) for dsChannel in dsChannels: deleteAttr(dsChannel) for vExpression in vExpressions: delete(vExpression) animCurveEditor('graphEditor1GraphEd', edit=1, showResults=(getAttr('velocityChannelStuff.showResultsState'))) delete('velocityChannelStuff') sys.stdout.write('Removed Velocity Channels') |
#Create or remove velocity channels for keyframes and expressions from pymel.core import * import sys if not (objExists('velocityChannelStuff')): selection = ls(sl=1) curvs = ls(typ='animCurve') + ls(typ='expression') vChannels = [] dsChannels = [] vExpressions = [] count = 0 if curvs: objs = [] for node in curvs: for connected in listConnections(node+'.output', p=1): objs.append(connected.split('.')[0]) addAttr(connected.split('.')[0], ln=connected.split('.')[-1]+'_velocity', at='float', k=0, h=1) count +1 vExpression = expression(o=connected.split('.')[0], s=connected.split('.')[0]+'.'+connected.split('.')[-1]+'_velocity'+' = (`getAttr -t (frame) '+connected+'` - `getAttr -t (frame-1) '+connected+'`) * `getAttr '+(connected.split('.')[0])+'.vDispScale`', name='vExpr'+str(count)) vChannels.append(connected.split('.')[0]+'.'+connected.split('.')[-1]+'_velocity') vExpressions.append(str(vExpression)) objs = list(set(objs)) for obj in objs: addAttr(obj, ln='vDispScale', nn='Velocity Display Scale', at='float', dv=10, k=1, h=0) dsChannels.append(obj + '.vDispScale') createNode('script', n='velocityChannelStuff') addAttr('velocityChannelStuff', dt='string', ln='vChannelsList') addAttr('velocityChannelStuff', dt='string', ln='dsChannelsList') addAttr('velocityChannelStuff', dt='string', ln='vExpressionsList') addAttr('velocityChannelStuff', dt='string', ln='showResultsState') setAttr('velocityChannelStuff.vChannelsList', ','.join(vChannels)) setAttr('velocityChannelStuff.dsChannelsList', ','.join(dsChannels)) setAttr('velocityChannelStuff.vExpressionsList', ','.join(vExpressions)) setAttr('velocityChannelStuff.showResultsState', animCurveEditor('graphEditor1GraphEd', q=1, showResults=1)) animCurveEditor('graphEditor1GraphEd', edit=1, showResults=1) select(selection) sys.stdout.write('Created Velocity Channels') else: sys.stdout.write('There\'s no animation to add velocity channels to.') else: vChannels = getAttr('velocityChannelStuff.vChannelsList').split(",") dsChannels = getAttr('velocityChannelStuff.dsChannelsList').split(",") vExpressions = getAttr('velocityChannelStuff.vExpressionsList').split(",") for vChannel in vChannels: deleteAttr(vChannel) for dsChannel in dsChannels: deleteAttr(dsChannel) for vExpression in vExpressions: delete(vExpression) animCurveEditor('graphEditor1GraphEd', edit=1, showResults=(getAttr('velocityChannelStuff.showResultsState'))) delete('velocityChannelStuff') sys.stdout.write('Removed Velocity Channels')
Converts any Unix path to Windows. Converts any Windows path to Unix.
#Convert Windows Path to Unix #Convert Unix Path to Windows from pymel.core import * import sys def closeWindow(*args): deleteUI(sourceRepWindow) def doIt(*args): path = textField(sourceText, q=1, tx=1) if len(path): if '/' in path: if not '\\' in path: path = path.replace('/', '\\') else: path = 'Not a valid source path.' else: if '\\' in path: path = path.replace('\\', '/') else: path = 'Not a valid source path.' else: path = 'Not a valid source path.' textField(destText, edit=1, tx=path) sys.stdout.write(path) winDimensions = (500, 98) sourceRepWindow = window(s=1, wh=winDimensions, title='Path Converter Tool') formParent = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) form = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) mainLabel = text('Swap Windows and Unix Paths') sourceLabel = text('Source Path') global sourceText sourceText = textField(w=400) destLabel = text('Destination Path') global destText destText = textField(w=400, ed=0) doButton = button(label='Do It', c=doIt, w=60) cancelButton = button(label='Close', c=closeWindow, w=60) setFocus(sourceText) formLayout( form, edit=True, attachForm=[(mainLabel, 'top', 7), (mainLabel, 'left', 5), (mainLabel, 'right', 5), (sourceLabel, 'top', 28), (sourceLabel, 'right', 410), (sourceText, 'top', 25), (sourceText, 'left', 90), (sourceText, 'right', 255), (destLabel, 'top', 48), (destLabel, 'right', 410), (destText, 'top', 45), (destText, 'left', 90), (destText, 'right', 255), (doButton, 'top', 70), (doButton, 'right', 5), (cancelButton, 'top', 70), (cancelButton, 'right', 70)]) showWindow(sourceRepWindow) |
#Convert Windows Path to Unix #Convert Unix Path to Windows from pymel.core import * import sys def closeWindow(*args): deleteUI(sourceRepWindow) def doIt(*args): path = textField(sourceText, q=1, tx=1) if len(path): if '/' in path: if not '\\' in path: path = path.replace('/', '\\') else: path = 'Not a valid source path.' else: if '\\' in path: path = path.replace('\\', '/') else: path = 'Not a valid source path.' else: path = 'Not a valid source path.' textField(destText, edit=1, tx=path) sys.stdout.write(path) winDimensions = (500, 98) sourceRepWindow = window(s=1, wh=winDimensions, title='Path Converter Tool') formParent = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) form = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) mainLabel = text('Swap Windows and Unix Paths') sourceLabel = text('Source Path') global sourceText sourceText = textField(w=400) destLabel = text('Destination Path') global destText destText = textField(w=400, ed=0) doButton = button(label='Do It', c=doIt, w=60) cancelButton = button(label='Close', c=closeWindow, w=60) setFocus(sourceText) formLayout( form, edit=True, attachForm=[(mainLabel, 'top', 7), (mainLabel, 'left', 5), (mainLabel, 'right', 5), (sourceLabel, 'top', 28), (sourceLabel, 'right', 410), (sourceText, 'top', 25), (sourceText, 'left', 90), (sourceText, 'right', 255), (destLabel, 'top', 48), (destLabel, 'right', 410), (destText, 'top', 45), (destText, 'left', 90), (destText, 'right', 255), (doButton, 'top', 70), (doButton, 'right', 5), (cancelButton, 'top', 70), (cancelButton, 'right', 70)]) showWindow(sourceRepWindow)
This tool automatically renames nodes by finding and replacing text within the names of the nodes. Select some nodes (or objects) and then run this script. A dialog box will appear, containing text fields for what to “Find” and what to “Replace” it with.
# Find and Replace Node Renamer Tool from pymel.core import * import sys def closeWindow(*args): deleteUI(findRepWindow) def doIt(*args): objList = selected() if objList: objCount = 0 for obj in objList: objname = obj.name() if textField(findText, q=1, tx=1) in objname: objCount += 1 objend = obj.split('|')[-1] rename(obj, objend.replace(textField(findText, q=1, tx=1), textField(replaceText, q=1, tx=1))) sys.stdout.write(str(objCount) + ' nodes were renamed.\n') closeWindow() else: sys.stdout.write('No nodes are selected.\n') winDimensions = (295, 98) findRepWindow = window(s=1, wh=winDimensions, title='Find and Replace Node Renamer') formParent = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) form = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) mainLabel = text('Rename currently selected nodes using find and replace') findLabel = text('Find') global findText findText = textField(w=200) replaceLabel = text('Replace with') global replaceText replaceText = textField(w=200) doButton = button(label='Rename', c=doIt, w=60) cancelButton = button(label='Cancel', c=closeWindow, w=60) setFocus(findText) formLayout( form, edit=True, attachForm=[(mainLabel, 'top', 7), (mainLabel, 'left', 5), (mainLabel, 'right', 5), (findLabel, 'top', 28), (findLabel, 'right', 210), (findText, 'top', 25), (findText, 'left', 90), (findText, 'right', 255), (replaceLabel, 'top', 48), (replaceLabel, 'right', 210), (replaceText, 'top', 45), (replaceText, 'left', 90), (replaceText, 'right', 255), (doButton, 'top', 70), (doButton, 'right', 5), (cancelButton, 'top', 70), (cancelButton, 'right', 70)]) showWindow(findRepWindow) |
# Find and Replace Node Renamer Tool from pymel.core import * import sys def closeWindow(*args): deleteUI(findRepWindow) def doIt(*args): objList = selected() if objList: objCount = 0 for obj in objList: objname = obj.name() if textField(findText, q=1, tx=1) in objname: objCount += 1 objend = obj.split('|')[-1] rename(obj, objend.replace(textField(findText, q=1, tx=1), textField(replaceText, q=1, tx=1))) sys.stdout.write(str(objCount) + ' nodes were renamed.\n') closeWindow() else: sys.stdout.write('No nodes are selected.\n') winDimensions = (295, 98) findRepWindow = window(s=1, wh=winDimensions, title='Find and Replace Node Renamer') formParent = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) form = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) mainLabel = text('Rename currently selected nodes using find and replace') findLabel = text('Find') global findText findText = textField(w=200) replaceLabel = text('Replace with') global replaceText replaceText = textField(w=200) doButton = button(label='Rename', c=doIt, w=60) cancelButton = button(label='Cancel', c=closeWindow, w=60) setFocus(findText) formLayout( form, edit=True, attachForm=[(mainLabel, 'top', 7), (mainLabel, 'left', 5), (mainLabel, 'right', 5), (findLabel, 'top', 28), (findLabel, 'right', 210), (findText, 'top', 25), (findText, 'left', 90), (findText, 'right', 255), (replaceLabel, 'top', 48), (replaceLabel, 'right', 210), (replaceText, 'top', 45), (replaceText, 'left', 90), (replaceText, 'right', 255), (doButton, 'top', 70), (doButton, 'right', 5), (cancelButton, 'top', 70), (cancelButton, 'right', 70)]) showWindow(findRepWindow)
This tool automatically renames and numbers nodes by giving them a base name and a number at the end of the name. Select some nodes (or objects) and then run this script. A dialog box will appear, containing text fields for the “Base Name”, “Starting Number” and “Padding” (number of digits to use).
# Node Rename and Renumber Tool from pymel.core import * import sys def closeWindow(*args): deleteUI(renameWindow) def doIt(*args): objList = ls(sl=1) baseNameLoc = textField(baseName, q=1, tx=1) startNumLoc = textField(startNum, q=1, tx=1) padLoc = textField(pad, q=1, tx=1) if not baseNameLoc: sys.stdout.write('You must enter a basename.\n') return try: val = int(startNumLoc) except: sys.stdout.write('You must enter an integer for the starting number.\n') return try: val = int(padLoc) except: sys.stdout.write('You must enter an integer for the pad.\n') return if objList: objCount = 0 for obj in objList: numStrLen = len(startNumLoc) if numStrLen < padLoc: for i in range(int(padLoc) - numStrLen): startNumLoc = '0' + startNumLoc rename(obj, baseNameLoc + startNumLoc) objCount += 1 startNumLoc = str(int(startNumLoc) + 1) sys.stdout.write(str(objCount) + ' nodes were renamed.\n') closeWindow() else: sys.stdout.write('No nodes are selected.\n') winDimensions = (255, 98) renameWindow = window(s=1, wh=winDimensions, title='Node Rename and Renumber') formParent = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) form = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) mainLabel = text('Rename and renumber currently selected nodes') baseLabel = text('Base Name') global baseName baseName = textField(w=160) startLabel = text('Starting Number') global startNum startNum = textField(w=65) padLabel = text('Pad') global pad pad = textField(w=65) doButton = button(label='Rename', c=doIt, w=60) cancelButton = button(label='Cancel', c=closeWindow, w=60) setFocus(baseName) formLayout( form, edit=True, attachForm=[(mainLabel, 'top', 7), (mainLabel, 'left', 5), (mainLabel, 'right', 5), (baseLabel, 'top', 28), (baseLabel, 'right', 170), (baseName, 'top', 25), (baseName, 'left', 90), (baseName, 'right', 255), (startLabel, 'top', 48), (startLabel, 'right', 170), (startNum, 'top', 45), (startNum, 'left', 90), (padLabel, 'top', 48), (padLabel, 'right', 75), (pad, 'top', 45), (pad, 'left', 185), (doButton, 'top', 70), (doButton, 'right', 5), (cancelButton, 'top', 70), (cancelButton, 'right', 70)]) showWindow(renameWindow) |
# Node Rename and Renumber Tool from pymel.core import * import sys def closeWindow(*args): deleteUI(renameWindow) def doIt(*args): objList = ls(sl=1) baseNameLoc = textField(baseName, q=1, tx=1) startNumLoc = textField(startNum, q=1, tx=1) padLoc = textField(pad, q=1, tx=1) if not baseNameLoc: sys.stdout.write('You must enter a basename.\n') return try: val = int(startNumLoc) except: sys.stdout.write('You must enter an integer for the starting number.\n') return try: val = int(padLoc) except: sys.stdout.write('You must enter an integer for the pad.\n') return if objList: objCount = 0 for obj in objList: numStrLen = len(startNumLoc) if numStrLen < padLoc: for i in range(int(padLoc) - numStrLen): startNumLoc = '0' + startNumLoc rename(obj, baseNameLoc + startNumLoc) objCount += 1 startNumLoc = str(int(startNumLoc) + 1) sys.stdout.write(str(objCount) + ' nodes were renamed.\n') closeWindow() else: sys.stdout.write('No nodes are selected.\n') winDimensions = (255, 98) renameWindow = window(s=1, wh=winDimensions, title='Node Rename and Renumber') formParent = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) form = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) mainLabel = text('Rename and renumber currently selected nodes') baseLabel = text('Base Name') global baseName baseName = textField(w=160) startLabel = text('Starting Number') global startNum startNum = textField(w=65) padLabel = text('Pad') global pad pad = textField(w=65) doButton = button(label='Rename', c=doIt, w=60) cancelButton = button(label='Cancel', c=closeWindow, w=60) setFocus(baseName) formLayout( form, edit=True, attachForm=[(mainLabel, 'top', 7), (mainLabel, 'left', 5), (mainLabel, 'right', 5), (baseLabel, 'top', 28), (baseLabel, 'right', 170), (baseName, 'top', 25), (baseName, 'left', 90), (baseName, 'right', 255), (startLabel, 'top', 48), (startLabel, 'right', 170), (startNum, 'top', 45), (startNum, 'left', 90), (padLabel, 'top', 48), (padLabel, 'right', 75), (pad, 'top', 45), (pad, 'left', 185), (doButton, 'top', 70), (doButton, 'right', 5), (cancelButton, 'top', 70), (cancelButton, 'right', 70)]) showWindow(renameWindow)
List the positions of the selected points
#Get Point Positions from pymel.core import * sel = ls(sl=1, flatten=1) for vert in sel: sys.stdout.write(str(pointPosition(vert))+'\n') |
#Get Point Positions from pymel.core import * sel = ls(sl=1, flatten=1) for vert in sel: sys.stdout.write(str(pointPosition(vert))+'\n')
List the positions of the selected UVs
#Get UV Positions from pymel.core import * sel = ls(sl=1, flatten=1) for uv in sel: sys.stdout.write(str(polyEditUV(uv, q=1))+'\n') |
#Get UV Positions from pymel.core import * sel = ls(sl=1, flatten=1) for uv in sel: sys.stdout.write(str(polyEditUV(uv, q=1))+'\n')
Randomize the positions of all selected objects.
A dialog pops up asking you how much you want to randomize. This is the maximum distance you want any object to be moved. The default is 1.0 if nothing is entered.
# Randomize positions of selected objects from pymel.core import * import sys import random def closeWindow(*args): deleteUI(randomizeWindow) def doIt(*args): objList = selected() amount = textField(distanceText, q=1, tx=1) if not amount: amount = 1.0 else: amount = float(amount) if objList: for obj in objList: numberX = (random.random()-.5)*amount numberY = (random.random()-.5)*amount numberZ = (random.random()-.5)*amount move(obj, numberX, numberY, numberZ, r=1) closeWindow() else: sys.stdout.write('No objects are selected.\n') deleteUI(randomizeWindow) winDimensions = (222, 98) randomizeWindow = window(s=1, wh=winDimensions, title='Object Position Randomizer') formParent = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) form = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) mainLabel = text('Randomize positions of selected objects') distanceLabel = text('Amount To Randomize:') global distanceText distanceText = textField(w=50) doButton = button(label='Randomize', c=doIt, w=65) cancelButton = button(label='Cancel', c=closeWindow, w=60) setFocus(distanceText) formLayout( form, edit=True, attachForm=[(mainLabel, 'top', 7), (mainLabel, 'left', 5), (mainLabel, 'right', 5), (distanceLabel, 'top', 31), (distanceLabel, 'right', 80), (distanceText, 'top', 28), (distanceText, 'left', 150), (distanceText, 'right', 50), (doButton, 'top', 59), (doButton, 'right', 17), (cancelButton, 'top', 59), (cancelButton, 'right', 92)]) showWindow(randomizeWindow) |
# Randomize positions of selected objects from pymel.core import * import sys import random def closeWindow(*args): deleteUI(randomizeWindow) def doIt(*args): objList = selected() amount = textField(distanceText, q=1, tx=1) if not amount: amount = 1.0 else: amount = float(amount) if objList: for obj in objList: numberX = (random.random()-.5)*amount numberY = (random.random()-.5)*amount numberZ = (random.random()-.5)*amount move(obj, numberX, numberY, numberZ, r=1) closeWindow() else: sys.stdout.write('No objects are selected.\n') deleteUI(randomizeWindow) winDimensions = (222, 98) randomizeWindow = window(s=1, wh=winDimensions, title='Object Position Randomizer') formParent = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) form = formLayout(numberOfDivisions=100, w=winDimensions[0], h=winDimensions[1]) mainLabel = text('Randomize positions of selected objects') distanceLabel = text('Amount To Randomize:') global distanceText distanceText = textField(w=50) doButton = button(label='Randomize', c=doIt, w=65) cancelButton = button(label='Cancel', c=closeWindow, w=60) setFocus(distanceText) formLayout( form, edit=True, attachForm=[(mainLabel, 'top', 7), (mainLabel, 'left', 5), (mainLabel, 'right', 5), (distanceLabel, 'top', 31), (distanceLabel, 'right', 80), (distanceText, 'top', 28), (distanceText, 'left', 150), (distanceText, 'right', 50), (doButton, 'top', 59), (doButton, 'right', 17), (cancelButton, 'top', 59), (cancelButton, 'right', 92)]) showWindow(randomizeWindow)
Sometimes when writing a Python script you may need to find the “root” or “highest level” parent object(s) for a given set of objects. Here’s a way to do that.
# Get highest parent(s) of current selection from pymel.core import * import sys parents = [] for n in selected(): objname = n.root().name() if objname not in parents: parents.append(objname) sys.stdout.write(parents) select(parents) |
# Get highest parent(s) of current selection from pymel.core import * import sys parents = [] for n in selected(): objname = n.root().name() if objname not in parents: parents.append(objname) sys.stdout.write(parents) select(parents)
Sometimes when writing a Python script you may need to find the immediate or “next level up” parent object(s) for a given set of objects. Here’s a way to do that.
# Get immediate parent(s) of current selection from pymel.core import * import sys parents = [] for n in selected(): parname = listRelatives(n, p=1)[0].name() if parname not in parents: parents.append(parname) sys.stdout.write(parents) select(parents) |
# Get immediate parent(s) of current selection from pymel.core import * import sys parents = [] for n in selected(): parname = listRelatives(n, p=1)[0].name() if parname not in parents: parents.append(parname) sys.stdout.write(parents) select(parents)
This will randomly re-order the current selection. Useful when you need to apply a Maya command using the selected objects in a random order.
# Randomize the order of the current selection from pymel.core import * import random selection = selected() random.shuffle(selection) select(selection, r=1) |
# Randomize the order of the current selection from pymel.core import * import random selection = selected() random.shuffle(selection) select(selection, r=1)
This removes any unknown nodes that might be left-over from plugins that aren’t installed anymore. It deletes all the unknown nodes from your scene file, even if they’re locked. After deleting, it confirms that all unknown nodes have indeed been deleted. Or, if there aren’t any unknown nodes in the first place, it lets you know.
# Unlock unknown types and delete them from pymel.core import * import sys unknowns = ls(type='unknown') if unknowns: lockNode(unknowns, lock=0) delete(unknowns) unknowns = ls(type='unknown') if not unknowns: sys.stdout.write('All unknown nodes have been deleted.') else: sys.stdout.write('Somehow, not all unknown nodes were deleted.') else: sys.stdout.write('There are no unknown nodes in the scene.') |
# Unlock unknown types and delete them from pymel.core import * import sys unknowns = ls(type='unknown') if unknowns: lockNode(unknowns, lock=0) delete(unknowns) unknowns = ls(type='unknown') if not unknowns: sys.stdout.write('All unknown nodes have been deleted.') else: sys.stdout.write('Somehow, not all unknown nodes were deleted.') else: sys.stdout.write('There are no unknown nodes in the scene.')
This script dissables thumnails in the Hypershade view. Handy if you have a lot of shaders who’s thumbnails are making Hypershade take a long time to draw. I keep this as a shelf button along with one for “Enable Hypershade Thumbnails” (see below).
# dissable hypershade thumbnails from pymel.core import * import sys renderThumbnailUpdate(0) sys.stdout.write('Hypershade Thumbnails Dissabled\n') |
# dissable hypershade thumbnails from pymel.core import * import sys renderThumbnailUpdate(0) sys.stdout.write('Hypershade Thumbnails Dissabled\n')
This script enables thumnails in the Hypershade view. I keep this as a shelf button along with one for “Dissable Thumbnails” (above).
# enable hypershade thumbnails from pymel.core import * import sys renderThumbnailUpdate(1) sys.stdout.write('Hypershade Thumbnails Enabled\n') |
# enable hypershade thumbnails from pymel.core import * import sys renderThumbnailUpdate(1) sys.stdout.write('Hypershade Thumbnails Enabled\n')
This script sets the filtertype for all file nodes in the scene to “Mipmap”, which is a higher quality filtering method than the default “Quadratic” method that filenodes initially come with.
# Set filtertype of all file nodes to Mipmap from pymel.core import * for selectedList in ls(typ='file'): setAttr((selectedList + '.filterType'), 1.0) |
# Set filtertype of all file nodes to Mipmap from pymel.core import * for selectedList in ls(typ='file'): setAttr((selectedList + '.filterType'), 1.0)
This script un-sets the “opposite” attribute for all selected objects. The “opposite” attribute gets set when you Freeze Transformations on an object that has negative scale. Rather than leaving the “opposite” attribute set when this happens (which can cause complications later), I prefer to un-set the “opposite” attribute and reverse the object’s normals.
# Un-set Opposite Attribute for Selected from pymel.core import * import sys for obj in selected(): if objExists(obj.name() + '.opposite'): setAttr(obj.name() + '.opposite', 0) sys.stdout.write('Opposite attibute un-set for selected.') |
# Un-set Opposite Attribute for Selected from pymel.core import * import sys for obj in selected(): if objExists(obj.name() + '.opposite'): setAttr(obj.name() + '.opposite', 0) sys.stdout.write('Opposite attibute un-set for selected.')
Run this script to replace the path of every File node in the scene with a relative path, retaining the part of the original path that comes after “sourceimages”.
For example, the path:
“C:\someOtherMayaProject\sourceimages\test_images\UV_test_image.jpg”
becomes:
“sourceimages\test_images\UV_test_image.jpg”
This is handy when moving a lot of textures from one Maya project folder to another.
(Note: Copy the files into the new location first, before running this script. If the files are not at the new location, Maya may keep the old paths even though the script has run correctly!)
# Replace all File node paths in the scene with a relative path # retaining the original path after "sourceimages" from pymel.core import * fileNodeList = ls(type="file") for obj in fileNodeList: pathEnd = getAttr(obj + '.fileTextureName').rsplit('sourceimages',1)[-1] setAttr(obj + '.fileTextureName', 'sourceimages' + pathEnd, type='string') |
# Replace all File node paths in the scene with a relative path # retaining the original path after "sourceimages" from pymel.core import * fileNodeList = ls(type="file") for obj in fileNodeList: pathEnd = getAttr(obj + '.fileTextureName').rsplit('sourceimages',1)[-1] setAttr(obj + '.fileTextureName', 'sourceimages' + pathEnd, type='string')
Run this script to replace the entire path of each selected File node with a new relative path. Adding an optional sub-folder.
For example, the path:
“C:\someOtherMayaProject\some_other_texture_folder\UV_test_image.jpg”
becomes:
“sourceimages\UV_test_image.jpg”
or (optionally):
“sourceimages\optional_new_subfolder\UV_test_image.jpg”
Unkike the “Make File Node Paths Relative” script above, this script defaults to only working on selected File nodes, so you must select the File nodes who’s paths you want to change first, before running this script (unless you enable the option to work on all File nodes in the scene, see below).
To add an optional sub-folder, un-comment (remove the # at the beginning of) the line of code that adds the optional sub-folder, and replace “optional_new_subfolder” with the name of the sub-folder you want to use. Be sure to keep the single quotes and the forward slash.
To have this script work on all file nodes in the scene, un-comment (remove the # at the beginning of) the line of code that adds the option to replace all File nodes in the scene.
This is handy when moving textures from a project folder that doesn’t have the textures organized the way you want them, into a new project folder that does.
(Note: Copy the files into the new location first, before running this script. If the files are not at the new location, Maya may keep the old paths even though the script has run correctly!)
# Replace each of the selected File nodes's paths with a relative path # replacing the entire path and adding optional sub-folder from pymel.core import * extraFolder = '' #extraFolder = 'optional_new_subfolder/' #optional sub-folder fileNodeList = ls(sl=1, type="file") #fileNodeList = mc.ls(type="file") #option to replace ALL File nodes for obj in fileNodeList: prePath = getAttr(obj + '.fileTextureName') pathEnd = prePath.rsplit('/',1)[-1] newPath = 'sourceimages/' + extraFolder + pathEnd setAttr(obj + '.fileTextureName', newPath, type='string') |
# Replace each of the selected File nodes's paths with a relative path # replacing the entire path and adding optional sub-folder from pymel.core import * extraFolder = '' #extraFolder = 'optional_new_subfolder/' #optional sub-folder fileNodeList = ls(sl=1, type="file") #fileNodeList = mc.ls(type="file") #option to replace ALL File nodes for obj in fileNodeList: prePath = getAttr(obj + '.fileTextureName') pathEnd = prePath.rsplit('/',1)[-1] newPath = 'sourceimages/' + extraFolder + pathEnd setAttr(obj + '.fileTextureName', newPath, type='string')
For use with Mental Ray: This script adds a new attribute called “miFinalGatherHide” to all selected geometry objects, and enables it. This causes the objects to be “hidden” from Final Gather as sources, so so the objects neither block Final Gather rays nor cast them. However, the objects will still recieve final gather rays. This is very useful when you need to have one or more objects be visible in the scene but not block Final Gather. For example, a wall could have Final Gather light sources behind it, casting light through it onto other objects, or even onto parts of the same wall (i.e. if the wall is curved).
# Add and Enable miFinalGatherHide Attribute for Selected Objects from pymel.core import * for curObject in selected(): if nodeType(curObject) == 'transform': shapeList = listRelatives(curObject, fullPath=1, shapes=1) for curShape in shapeList: if not attributeQuery('miFinalGatherHide', node=curShape, exists=1): addAttr(curShape, ln='miFinalGatherHide', at='bool', dv=1) elif nodeType(curObject) == 'mesh': if not attributeQuery('miFinalGatherHide', node=curObject, exists=1): addAttr(curObject, ln='miFinalGatherHide', at='bool', dv=1) |
# Add and Enable miFinalGatherHide Attribute for Selected Objects from pymel.core import * for curObject in selected(): if nodeType(curObject) == 'transform': shapeList = listRelatives(curObject, fullPath=1, shapes=1) for curShape in shapeList: if not attributeQuery('miFinalGatherHide', node=curShape, exists=1): addAttr(curShape, ln='miFinalGatherHide', at='bool', dv=1) elif nodeType(curObject) == 'mesh': if not attributeQuery('miFinalGatherHide', node=curObject, exists=1): addAttr(curObject, ln='miFinalGatherHide', at='bool', dv=1)
Note: I often use sys.stdout.write instead of Python’s native print command because the print command doesn’t show in the prompt box in Maya.
Leave a Reply
You must be logged in to post a comment.