def componentSelectionInOrder(): ''' Returns a list of the selected components in the order they were selected. ''' # Get selection selection = [] selectionAll = mc.ls(sl=1) lastCommand = mc.undoInfo(q=True,un=True) counter = 0 # Traverse undo list while lastCommand.count('select'): lastCommand = mc.undoInfo(q=True,un=True) if lastCommand.count('select'): selectElem = lastCommand.split(' ') selection.append(selectElem[2]) mc.undo() # Sort selection selection.reverse() realSelection = [] [realSelection.append(i) for i in selection if not realSelection.count(i)] # Return result return realSelection
def __exit__(self, exc_type, exc_value, traceback): '''Close the opened chunk and undo everything that had been captured.''' cmds.undoInfo(closeChunk=True) try: cmds.undo() except RuntimeError: pass
def jobUndo(*args): # need to reset globals if an undo is detected que = cmds.undoInfo(q=1, rn=1) if 'import curveSoftSelect as css' in que or 'jobValue' in que: cmds.undo() killValueJob() activateValueJob()
def rsReAtt(): l_oSels = rsObjList() l_AttributeList = (cmds.textScrollList("rsAttributeScroll", query=True, allItems=True)) for s_Att in l_AttributeList: s_orig = l_oSels[0] + "." + s_Att i_LockState = cmds.getAttr(s_orig, lock=True) if i_LockState: cmds.setAttr(s_orig, lock=False) l_paramDest = cmds.listConnections(s_orig, plugs=True, destination=True, source=True) l_paramDestLock = [] if l_paramDest: for z in range(len(l_paramDest)): l_paramDestLock.append(cmds.getAttr(l_paramDest[z], lock=True)) if l_paramDestLock[z]: cmds.setAttr(l_paramDest[z], lock=False) cmds.deleteAttr(l_oSels[0], at=s_Att) cmds.undo() if l_paramDest: for z in range(len(l_paramDest)): l_paramDestLock.append(cmds.getAttr(l_paramDest[z], lock=True)) if l_paramDestLock[z]: cmds.setAttr(l_paramDest[z], lock=True) if i_LockState: cmds.setAttr(s_orig, lock=True) cmds.select(cl=True) cmds.select(l_oSels[0], r=True) return True
def testUndoRedo(self): """Tests that adaptors work with undo/redo.""" cmds.file(new=True, force=True) cmds.group(name="group1", empty=True) adaptor = UsdMaya.Adaptor("group1") self.assertEqual(adaptor.GetAppliedSchemas(), []) # Do a single operation, then undo, then redo. adaptor.ApplySchema(UsdGeom.ModelAPI) self.assertEqual(adaptor.GetAppliedSchemas(), ["GeomModelAPI"]) cmds.undo() self.assertEqual(adaptor.GetAppliedSchemas(), []) cmds.redo() self.assertEqual(adaptor.GetAppliedSchemas(), ["GeomModelAPI"]) # Do a compound operation, then undo, then redo. cmds.undoInfo(openChunk=True) adaptor.ApplySchema(UsdGeom.MotionAPI).CreateAttribute( UsdGeom.Tokens.motionVelocityScale).Set(0.42) self.assertEqual(adaptor.GetAppliedSchemas(), ["GeomModelAPI", "MotionAPI"]) self.assertAlmostEqual(adaptor.GetSchema(UsdGeom.MotionAPI).GetAttribute( UsdGeom.Tokens.motionVelocityScale).Get(), 0.42) cmds.undoInfo(closeChunk=True) cmds.undo() self.assertEqual(adaptor.GetAppliedSchemas(), ["GeomModelAPI"]) self.assertFalse(adaptor.GetSchema(UsdGeom.MotionAPI).GetAttribute( UsdGeom.Tokens.motionVelocityScale)) self.assertIsNone(adaptor.GetSchema(UsdGeom.MotionAPI).GetAttribute( UsdGeom.Tokens.motionVelocityScale).Get()) cmds.redo() self.assertEqual(adaptor.GetAppliedSchemas(), ["GeomModelAPI", "MotionAPI"]) self.assertAlmostEqual(adaptor.GetSchema(UsdGeom.MotionAPI).GetAttribute( UsdGeom.Tokens.motionVelocityScale).Get(), 0.42)
def orientJoints(s): """ Face joints in the correct direction. """ sel = cmds.ls(sl=True) err = cmds.undoInfo(openChunk=True) try: markers = s.markers joints = markers.keys() with ReSeat(joints): for j in joints: m = markers[j] if cmds.objExists(m.marker) and cmds.objExists(j): with Isolate(j): m.setJoint() try: cmds.makeIdentity( j, apply=True, r=True) # Freeze Rotations except RuntimeError: pass else: # User deleted marker / joint. Stop tracking. m.removeMarker() del markers[j] cmds.select(sel, r=True) except Exception as err: raise finally: cmds.undoInfo(closeChunk=True) if err: cmds.undo()
def test_undoPerformance(self): import time iterations = 35 maxdepth = 3 totalops = 0 all_elapsed = [list(), list()] for undoEnabled in range(2): undo = "" if not undoEnabled: undo = "Undo disabled" cmds.undoInfo(st=undoEnabled) # decorated ! starttime = time.time() numops = TestUndoPerformance._recurseUndoDeco( iterations, 0, maxdepth) totalops += numops elapsed = time.time() - starttime all_elapsed[undoEnabled].append(elapsed) print >> sys.stderr, "UNDO: DECORATED %s: %i ops in %f s ( %f / s )" % ( undo, numops, elapsed, numops / elapsed) starttime = time.time() numops = TestUndoPerformance._recurseUndo(iterations, 0, maxdepth) totalops += numops elapsed_deco = elapsed elapsed = time.time() - starttime all_elapsed[undoEnabled].append(elapsed) print >> sys.stderr, "UNDO: MANUAL %s: %i ops in %f s ( %f / s )" % ( undo, numops, elapsed, numops / elapsed) starttime = time.time() print >> sys.stderr, "UNDO: DECORATED is %f %% faster than manually implemented functions !" % ( 100 - (elapsed_deco / elapsed) * 100) if undoEnabled: cmds.undo() cmds.undo() cmds.redo() cmds.redo() elapsed = time.time() - starttime print >> sys.stderr, "UNDO: CALL TIME: %i operations in %f s ( %f / s )" % ( totalops, elapsed, totalops / elapsed) #END if undo enabled # END for each undo queue state ratio = 100.0 - ((all_elapsed[0][0] / all_elapsed[1][0]) * 100) difference = all_elapsed[1][1] - all_elapsed[0][1] # RATIOS between enabled undo system and without print >> sys.stderr, "UNDO: RATIO UNDO QUEUE ON/OFF: %f s (on) vs %f s (off) = %f %% speedup on disabled queue ( difference [s] = %f )" % ( all_elapsed[1][0], all_elapsed[0][0], ratio, difference)
def testAlreadyChild(self): '''Parenting an object to its current parent is a no-op.''' with OpenFileCtx("simpleHierarchy.ma"): shapeSegment = mayaUtils.createUfePathSegment( "|world|mayaUsdProxy1|mayaUsdProxyShape1") spherePath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCylinder1/pCube1/pSphere1")]) sphereItem = ufe.Hierarchy.createItem(spherePath) cylinderShapePath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCylinder1/pCylinderShape1")]) cylinderShapeItem = ufe.Hierarchy.createItem(cylinderShapePath) parentPath = ufe.Path( [shapeSegment, usdUtils.createUfePathSegment("/pCylinder1")]) parentItem = ufe.Hierarchy.createItem(parentPath) parent = ufe.Hierarchy.hierarchy(parentItem) childrenPre = parent.children() # get the USD stage stage = mayaUsd.ufe.getStage(str(shapeSegment)) # check GetLayerStack behavior self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer()) # The sphere is not a child of the cylinder self.assertNotIn("pSphere1", childrenNames(childrenPre)) # The cylinder shape is a child of the cylinder self.assertIn("pCylinderShape1", childrenNames(childrenPre)) # Parent the sphere and the cylinder shape to the cylinder. This # is a no-op for the cylinder shape, as it's already a child of the # cylinder, and the sphere is parented to the cylinder. cmds.parent(ufe.PathString.string(spherePath), ufe.PathString.string(cylinderShapePath), ufe.PathString.string(parentPath)) children = parent.children() self.assertEqual(len(childrenPre)+1, len(children)) self.assertIn("pSphere1", childrenNames(children)) self.assertIn("pCylinderShape1", childrenNames(children)) # Undo / redo cmds.undo() children = parent.children() self.assertEqual(len(childrenPre), len(children)) self.assertNotIn("pSphere1", childrenNames(children)) self.assertIn("pCylinderShape1", childrenNames(children)) cmds.redo() children = parent.children() self.assertEqual(len(childrenPre)+1, len(children)) self.assertIn("pSphere1", childrenNames(children)) self.assertIn("pCylinderShape1", childrenNames(children))
def wrapper(*args, **kw): with self: try: return f(*args, **kw) except: if rollback: mc.undo() raise
def test_UndoChunk(self): cmds.file(f=True, new=True) cmds.createNode('transform') with undoChunk: cmds.createNode('transform') cmds.createNode('transform') cmds.undo() self.assertEqual(cmds.ls('transform*'), ['transform1'])
def fix_shaders(): """ Fixing a bug in maya where a referenced maya file loses it's shaders. Mesh needs to be selected """ get_selected(scriptEditorWarning=True) mel.eval("sets -e -forceElement initialShadingGroup;") cmds.undo() pm.select(cl=1)
def undoCall(self): for _ in range(1, self.undoDepth + 1): #log.depth('undoDepth : %s' % i) if [ func for func in self.undoFuncCache if func in cmds.undoInfo(q=True, undoName=True) ]: cmds.undo()
def bakeShader(self, shader, cmd): self.createShaders() cmds.undoInfo(openChunk=True) self.assignShader(shader) self.setAttributes(shader) mel.eval(cmd) cmds.undoInfo(closeChunk=True) cmds.undo()
def _close_and_undo(defer=False): do_undo = not cmds.undoInfo(undoQueueEmpty=True, query=True) cmds.undoInfo(closeChunk=True) if do_undo: if defer and not cmds.about(batch=True): cmds.evalDeferred("cmds.undo()") else: cmds.undo()
def testDuplicateAsUsdUndoRedo(self): '''Duplicate a Maya transform hierarchy to USD and then undo and redo the command.''' # Create a hierarchy. Because group1 is selected upon creation, group2 # will be its parent. group1 = cmds.createNode('transform') group2 = cmds.group() self.assertEqual(cmds.listRelatives(group1, parent=True)[0], group2) cmds.setAttr(group1 + '.translate', 1, 2, 3) cmds.setAttr(group2 + '.translate', -4, -5, -6) # Create a stage to receive the USD duplicate. psPathStr = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() # Duplicate Maya data as USD data. As of 17-Nov-2021 no single-segment # path handler registered to UFE for Maya path strings, so use absolute # path. cmds.mayaUsdDuplicate(cmds.ls(group2, long=True)[0], psPathStr) def verifyDuplicate(): # Maya hierarchy should be duplicated in USD. usdGroup2PathStr = psPathStr + ',/' + group2 usdGroup1PathStr = usdGroup2PathStr + '/' + group1 usdGroup2Path = ufe.PathString.path(usdGroup2PathStr) usdGroup1Path = ufe.PathString.path(usdGroup1PathStr) # group1 is the child of group2 usdGroup1 = ufe.Hierarchy.createItem(usdGroup1Path) usdGroup2 = ufe.Hierarchy.createItem(usdGroup2Path) usdGroup1Hier = ufe.Hierarchy.hierarchy(usdGroup1) usdGroup2Hier = ufe.Hierarchy.hierarchy(usdGroup2) self.assertEqual(usdGroup2, usdGroup1Hier.parent()) self.assertEqual(len(usdGroup2Hier.children()), 1) self.assertEqual(usdGroup1, usdGroup2Hier.children()[0]) # Translations have been preserved. usdGroup1T3d = ufe.Transform3d.transform3d(usdGroup1) usdGroup2T3d = ufe.Transform3d.transform3d(usdGroup2) self.assertEqual([1, 2, 3], usdGroup1T3d.translation().vector) self.assertEqual([-4, -5, -6], usdGroup2T3d.translation().vector) verifyDuplicate() cmds.undo() def verifyDuplicateIsGone(): # Maya hierarchy should no longer be duplicated in USD. usdGroup2PathStr = psPathStr + ',/' + group2 usdGroup2Path = ufe.PathString.path(usdGroup2PathStr) usdGroup2 = ufe.Hierarchy.createItem(usdGroup2Path) self.assertIsNone(usdGroup2) verifyDuplicateIsGone() cmds.redo() verifyDuplicate()
def testRename(self): '''Rename USD node.''' # Select a USD object. ball35Path = ufe.Path([ mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), usdUtils.createUfePathSegment("/Room_set/Props/Ball_35") ]) ball35Item = ufe.Hierarchy.createItem(ball35Path) ball35Hierarchy = ufe.Hierarchy.hierarchy(ball35Item) propsItem = ball35Hierarchy.parent() propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildrenPre = propsHierarchy.children() ufe.GlobalSelection.get().append(ball35Item) newName = 'Ball_35_Renamed' cmds.rename(newName) # The renamed item is in the selection. snIter = iter(ufe.GlobalSelection.get()) ball35RenItem = next(snIter) ball35RenName = str(ball35RenItem.path().back()) self.assertEqual(ball35RenName, newName) # MAYA-92350: should not need to re-bind hierarchy interface objects # with their item. propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildren = propsHierarchy.children() self.assertEqual(len(propsChildren), len(propsChildrenPre)) self.assertIn(ball35RenItem, propsChildren) cmds.undo() def childrenNames(children): return [str(child.path().back()) for child in children] propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildren = propsHierarchy.children() propsChildrenNames = childrenNames(propsChildren) self.assertNotIn(ball35RenName, propsChildrenNames) self.assertIn('Ball_35', propsChildrenNames) self.assertEqual(len(propsChildren), len(propsChildrenPre)) cmds.redo() propsHierarchy = ufe.Hierarchy.hierarchy(propsItem) propsChildren = propsHierarchy.children() propsChildrenNames = childrenNames(propsChildren) self.assertIn(ball35RenName, propsChildrenNames) self.assertNotIn('Ball_35', propsChildrenNames) self.assertEqual(len(propsChildren), len(propsChildrenPre))
def rewindMemento(self): ''' Undo through all items in memento. Make sure that the current selection match their respective registered selection in memento. ''' for m in reversed(self.memento[:-1]): cmds.undo() self.assertEqual(m[0], mayaUtils.getMayaSelectionList()) self.assertEqual(m[1], ufeUtils.getUfeGlobalSelectionList())
def testCenterPivotUndo(self): cmds.file(new=True, force=True) import mayaUsd_createStageWithNewLayer mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path('|stage1|stageShape1') proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Capsule']) capsulePath = ufe.PathString.path('|stage1|stageShape1,/Capsule1') capsuleItem = ufe.Hierarchy.createItem(capsulePath) usdT3d = ufe.Transform3d.transform3d(capsuleItem) sn = ufe.GlobalSelection.get() sn.clear() sn.append(capsuleItem) # center point is expected to be at [0.0, 0.0, 0.0] assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [0.0, 0.0, 0.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [0.0, 0.0, 0.0]) # move the pivot location cmds.move(7, 2, 1, r=True, urp=True, usp=True) assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [7.0, 2.0, 1.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [7.0, 2.0, 1.0]) # call center pivot command cmds.xform(cp=True) # center point is expected to be at [0.0, 0.0, 0.0] assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [0.0, 0.0, 0.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [0.0, 0.0, 0.0]) # undo cmds.undo() assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [7.0, 2.0, 1.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [7.0, 2.0, 1.0]) # redo cmds.redo() assertVectorAlmostEqual(self, usdT3d.rotatePivot().vector, [0.0, 0.0, 0.0]) assertVectorAlmostEqual(self, usdT3d.scalePivot().vector, [0.0, 0.0, 0.0])
def undoSel(): sel = cmds.ls(sl=True) info = cmds.undoInfo(q=True, un=True) if 'selectKey' in info: return None elif 'select' in info: cmds.undo() cmds.select(clear=True) cmds.select(sel) return True
def testUnparentMultiStage(self): '''Unparent USD nodes in more than one stage.''' with OpenFileCtx("simpleHierarchy.ma"): # An early version of this test imported the same file into the # opened file. Layers are then shared between the stages, because # they come from the same USD file, causing changes done below one # proxy shape to be seen in the other. Import from another file. filePath = mayaUtils.getTestScene("parentCmd", "simpleSceneUSD_TRS.ma") cmds.file(filePath, i=True) # Unparent a USD node in each stage. Unparenting Lambert node is # nonsensical, but demonstrates the functionality. cubePathStr1 = '|mayaUsdProxy1|mayaUsdProxyShape1,/pCylinder1/pCube1' lambertPathStr2 = '|simpleSceneUSD_TRS_mayaUsdProxy1|simpleSceneUSD_TRS_mayaUsdProxyShape1,/initialShadingGroup/initialShadingGroup_lambert' cylinderItem1 = ufe.Hierarchy.createItem(ufe.PathString.path( '|mayaUsdProxy1|mayaUsdProxyShape1,/pCylinder1')) shadingGroupItem2 = ufe.Hierarchy.createItem( ufe.PathString.path('|simpleSceneUSD_TRS_mayaUsdProxy1|simpleSceneUSD_TRS_mayaUsdProxyShape1,/initialShadingGroup')) proxyShapeItem1 = ufe.Hierarchy.createItem(ufe.PathString.path( '|mayaUsdProxy1|mayaUsdProxyShape1')) proxyShapeItem2 = ufe.Hierarchy.createItem(ufe.PathString.path( '|simpleSceneUSD_TRS_mayaUsdProxy1|simpleSceneUSD_TRS_mayaUsdProxyShape1')) cylinder1 = ufe.Hierarchy.hierarchy(cylinderItem1) shadingGroup2 = ufe.Hierarchy.hierarchy(shadingGroupItem2) proxyShape1 = ufe.Hierarchy.hierarchy(proxyShapeItem1) proxyShape2 = ufe.Hierarchy.hierarchy(proxyShapeItem2) def checkUnparent(done): proxyShape1Children = proxyShape1.children() proxyShape2Children = proxyShape2.children() cylinder1Children = cylinder1.children() shadingGroup2Children = shadingGroup2.children() self.assertEqual( 'pCube1' in childrenNames(proxyShape1Children), done) self.assertEqual( 'pCube1' in childrenNames(cylinder1Children), not done) self.assertEqual( 'initialShadingGroup_lambert' in childrenNames(proxyShape2Children), done) self.assertEqual( 'initialShadingGroup_lambert' in childrenNames(shadingGroup2Children), not done) checkUnparent(done=False) # Use relative parenting, else trying to keep absolute world # position of Lambert node fails (of course). cmds.parent(cubePathStr1, lambertPathStr2, w=True, r=True) checkUnparent(done=True) cmds.undo() checkUnparent(done=False) cmds.redo() checkUnparent(done=True)
def testUndoLast(self): self.assertEquals(len(self.executor.undoStack), 0) result = self.executor.execute("test.mayaTestCreateNodeCommand") self.assertIsInstance(result, om2.MObject) self.assertEquals(len(self.executor.undoStack), 1) cmds.undo() self.assertEquals(len(self.executor.undoStack), 0) self.assertEquals(len(self.executor.redoStack), 1) cmds.redo() self.assertEquals(len(self.executor.redoStack), 0) self.assertEquals(len(self.executor.undoStack), 1)
def reorderAttributes(obj, attrs, direction=0): """ >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Acknowledgement: Thank you to - http://www.the-area.com/forum/autodesk-maya/mel/how-can-we-reorder-an-attribute-in-the-channel-box/ DESCRIPTION: Reorders attributes on an object ARGUMENTS: obj(string) - obj with message attrs attrs(list) must be attributes on the object direction(int) - 0 is is negative (up on the channelbox), 1 is positive (up on the channelbox) RETURNS: messageList - nested list in terms of [[attrName, target],[etc][etc]] >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> """ assert direction in [ 0, 1 ], "Direction must be 0 for negative, or 1 for positive movement" for attr in attrs: assert cmds.objExists( obj + '.' + attr) is True, "'%s.%s' doesn't exist. Swing and a miss..." % (obj, atr) userAttrs = cmds.listAttr(obj, userDefined=True) attrsToMove = [] for attr in userAttrs: if not cmds.attributeQuery(attr, node=obj, listParent=True): attrsToMove.append(attr) lists.reorderListInPlace(attrsToMove, attrs, direction) #To reorder, we need delete and undo in the order we want for attr in attrsToMove: try: attrBuffer = '%s.%s' % (obj, attr) lockState = False if cmds.getAttr(attrBuffer, lock=True) == True: lockState = True cmds.setAttr(attrBuffer, lock=False) cmds.deleteAttr('%s.%s' % (obj, attr)) cmds.undo() if lockState: cmds.setAttr(attrBuffer, lock=True) except: guiFactory.warning("'%s' Failed to reorder" % attr)
def __exit__(self, exec_type, exec_value, traceback): """Undo any commands that were done while this context was left open.""" needs_undo = not cmds.undoInfo(undoQueueEmpty=True, q=True) cmds.undoInfo(closeChunk=True) if needs_undo: # Only undo if there are any commands to undo. If you try # to run undo when there's nothing that can be undone, Maya # will raise a RuntimeError. # cmds.undo()
def test_NonUndoable(self): num = len(cmds.ls(type='transform')) with undoChunk: cmds.createNode('transform') with nonUndoable: cmds.createNode('transform') cmds.createNode('transform') cmds.createNode('transform') self.assertEqual(num + 4, len(cmds.ls(type='transform'))) cmds.undo() self.assertEqual(num + 2, len(cmds.ls(type='transform')))
def test_undoPerformance( self ): import time iterations = 35 maxdepth = 3 totalops = 0 all_elapsed = [list(),list()] for undoEnabled in range( 2 ): undo = "" if not undoEnabled: undo = "Undo disabled" cmds.undoInfo( st=undoEnabled ) # decorated ! starttime = time.time() numops = TestUndoPerformance._recurseUndoDeco( iterations, 0, maxdepth ) totalops += numops elapsed = time.time() - starttime all_elapsed[undoEnabled].append( elapsed ) print >> sys.stderr, "UNDO: DECORATED %s: %i ops in %f s ( %f / s )" % ( undo, numops, elapsed, numops / elapsed ) starttime = time.time() numops = TestUndoPerformance._recurseUndo( iterations, 0, maxdepth ) totalops += numops elapsed_deco = elapsed elapsed = time.time() - starttime all_elapsed[undoEnabled].append( elapsed ) print >> sys.stderr, "UNDO: MANUAL %s: %i ops in %f s ( %f / s )" % ( undo, numops, elapsed, numops / elapsed ) starttime = time.time() print >> sys.stderr, "UNDO: DECORATED is %f %% faster than manually implemented functions !" % ( 100 - ( elapsed_deco / elapsed ) * 100 ) if undoEnabled: cmds.undo() cmds.undo() cmds.redo() cmds.redo() elapsed = time.time() - starttime print >> sys.stderr, "UNDO: CALL TIME: %i operations in %f s ( %f / s )" % ( totalops, elapsed, totalops / elapsed ) #END if undo enabled # END for each undo queue state ratio = 100.0 - ( ( all_elapsed[0][0] / all_elapsed[1][0] ) * 100 ) difference = all_elapsed[1][1] - all_elapsed[0][1] # RATIOS between enabled undo system and without print >> sys.stderr, "UNDO: RATIO UNDO QUEUE ON/OFF: %f s (on) vs %f s (off) = %f %% speedup on disabled queue ( difference [s] = %f )" % (all_elapsed[1][0], all_elapsed[0][0], ratio, difference )
def reorder_attributes(node, attributes): """ :param str node: The node containing the attributes to re-order :param attributes: The attributes names, sorted in the desired order """ cmds.undoInfo(openChunk=True) for attribute in reversed(attributes): attr_path = "%s.%s" % (node, attribute) cmds.deleteAttr(attr_path) cmds.undoInfo(closeChunk=True) cmds.undo()
def main(): err = cmds.undoInfo(openChunk=True) try: # Create our objects. Parent them together. root = cmds.group(name="roller_root", empty=True) ctrl = cmds.polySphere(name="%s_ctrl" % root)[0] # TODO: change this to something nicer cmds.parent(ctrl, root) # Lock our rotation axis. cmds.setAttr("%s.ry" % ctrl, lock=True) # Create our nodes. mult_radians = cmds.shadingNode("multiplyDivide", asUtility=True) mult_scale = cmds.shadingNode("multiplyDivide", asUtility=True) mult_invert = cmds.shadingNode("multiplyDivide", asUtility=True) # Set values radians = math.radians(1) for xyz in XYZ: cmds.setAttr("%s.input1%s" % (mult_radians, xyz), radians) cmds.setAttr("%s.input1%s" % (mult_invert, xyz), -1) # Plug em together! for xyz in XYZ: cmds.connectAttr("%s.rotate%s" % (ctrl, xyz), "%s.input2%s" % (mult_radians, xyz), force=True) cmds.connectAttr("%s.scale%s" % (ctrl, xyz), "%s.input1%s" % (mult_scale, xyz), force=True) cmds.connectAttr("%s.output%s" % (mult_radians, xyz), "%s.input2%s" % (mult_scale, xyz), force=True) cmds.connectAttr("%s.output%s" % (mult_scale, xyz), "%s.input2%s" % (mult_invert, xyz), force=True) cmds.connectAttr("%s.outputX" % mult_scale, "%s.translateZ" % ctrl, force=True) cmds.connectAttr("%s.outputZ" % mult_invert, "%s.translateX" % ctrl, force=True) cmds.connectAttr("%s.scaleY" % ctrl, "%s.translateY" % ctrl, force=True) except err: pass finally: cmds.select(clear=True) cmds.undoInfo(closeChunk=True) if err: cmds.undo()
def test_strNodeDeleted(self): for attr_instance in get_scene_attribute_instances( maya_attributes=False): mc.delete(attr_instance.nodeName) self.assertRaises(InvalidMayaObjectError, attr_instance.__str__) mc.undo() str(attr_instance) mc.redo() self.assertRaises(InvalidMayaObjectError, attr_instance.__str__) mc.undo() str(attr_instance)
def assign_shader(node_list, shadingEngine): """ assign model to shading group """ cmds.undoInfo(ock=True) try: cmds.sets(node_list, edit=True, forceElement=shadingEngine) yield finally: cmds.undoInfo(cck=True) cmds.undo()
def mel_handler(self, input_str): prev_chunk = cmds.undoInfo(q=True, chunkName=True) cmds.undoInfo(openChunk=True) try: execInMain(partial(mel.eval, input_str)) cmds.repeatLast(addCommand=input_str) cmds.undoInfo(closeChunk=True) except: cmds.undoInfo(closeChunk=True) if not cmds.undoInfo(q=True, chunkName=True) == prev_chunk: cmds.undo() raise
def multiSelectRewindMemento(self, items): '''Undo through all items in memento. Starting at the top of the undo stack, perform undo to bring us to the bottom of the undo stack. During iteration, we ensure that the current state matches the state stored in the memento. ''' # Ignore the top of the memento stack, as it corresponds to the current # state. for m in reversed(self.memento[:-1]): cmds.undo() self.assertEqual(m, self.multiSelectSnapshotRunTimeUFE(items))
def undoIt(): """ Maya expects this method to exist and to be able to call it if the command is flagged as undoable. Since all we use is a bunch of commands undoing is relatively trivial, and we can just call Maya's own undo and let it go over the chunk of commands that was ran during doIt() Normally this wouldn't be static as it might need to access something from inside the instance of the command class that was run, but when all these methods do is call a one-liner Maya command there really is no reason for them to be more than a static wrapper of the factory undo call. :return: `None` """ m_cmds.undo()
def keyhammer_button_clicked(): cmds.undoInfo(openChunk=True, chunkName="keyHammer") try: result = cmds.keyHammer() finally: cmds.undoInfo(closeChunk=True) # API 2.0 seems to always return MPxCommand results as a list if isinstance(result, list) and len(result) >= 1: result = result[0] if not result: cmds.undo()
def test_removeChild(self): base = nt.createNode("base" , "transform") trans = nt.createNode("base|trans", "transform") mesh = nt.createNode("base|mesh", "mesh") for item in [trans, mesh]: removeditem = base.removeChild(item, allowZeroParents=True) # PATHS ARE INVALID NOW - object is nowhere to be found assert not removeditem.isValid() and removeditem.isAlive() cmds.undo() assert removeditem.isValid() and removeditem.isAlive()
def py_handler(self, input_str): prev_chunk = cmds.undoInfo(q=True, chunkName=True) cmds.undoInfo(openChunk=True) try: execInMain(input_str) setattr(__main__, "last_py_cmd", input_str) cmds.repeatLast(addCommand='python("execInMain(last_py_cmd)")') cmds.undoInfo(closeChunk=True) except: cmds.undoInfo(closeChunk=True) if not cmds.undoInfo(q=True, chunkName=True) == prev_chunk: cmds.undo() raise
def test_removeChild( self ): base = nt.createNode( "base" , "transform" ) trans = nt.createNode( "base|trans", "transform" ) mesh = nt.createNode( "base|mesh", "mesh" ) for item in [ trans, mesh ]: removeditem = base.removeChild( item, allowZeroParents=True ) # PATHS ARE INVALID NOW - object is nowhere to be found assert not removeditem.isValid() and removeditem.isAlive() cmds.undo() assert removeditem.isValid() and removeditem.isAlive()
def test_MPlugAccess_nodeDeleted(self): attr = Attribute(MayaTest.SCENE['transform_1_attr_1']) attr.MPlug mc.delete(MayaTest.SCENE['transform_1']) with self.assertRaises(InvalidMayaObjectError): attr.MPlug mc.undo() attr.MPlug mc.redo() with self.assertRaises(InvalidMayaObjectError): attr.MPlug mc.undo() attr.MPlug
def moveToBottom(attribute): ''' Move specified attribute to the bottom of the channel box ''' # Determine object and attribute names from input argument obj = attribute.split('.')[0] attr = attribute.split('.')[-1] # Delete attribute temporarily mc.deleteAttr(obj, attribute=attr) # Undo deletion mc.undo()
def reorder(self): """ Reorder all of the attributes based on the new attribute list. All of the attributes in the list are deleted, they are deleted in such an order that when all of the deletion are undone. The attribute order is changed to what the input of the user. """ with utils.UndoStateContext(): for attr in self.widget.attributes: attr.delete() for _ in range(self.widget.count()): cmds.undo()
def moveToBottom(attribute): """ Move specified attribute to the bottom of the channel box """ # Determine object and attribute names from input argument obj = attribute.split(".")[0] attr = attribute.split(".")[-1] # Delete attribute temporarily mc.deleteAttr(obj, attribute=attr) # Undo deletion mc.undo()
def create_fk_control(fk_jnt): """ creates and fk control on the given jnt with the parent add shape command :param fk_jnt: name of the joint to become fk :type: str """ fk_cc_base = fk_jnt.rsplit('_', 1)[0] fk_cc = fk_cc_base + '_FK' + NamingConventionEnums.CONTROL_CURVE_SUFFIX # unlocks all the channels and then freezes them on the fk joint so that it # preserves the transforms when parented unlock_all_channels(fk_cc) cmds.parent(fk_cc, fk_jnt) cmds.makeIdentity(fk_cc, apply=True, translate=True, rotate=True, scale=True, normal=False, preserveNormals=1) cmds.parent(fk_cc, world=True) # get the shape node of the control curve cmds.select(fk_cc) cmds.pickWalk(direction='down') shape = cmds.ls(selection=True)[0] # parent the shape node to the fk jnt cmds.parent(shape, fk_jnt, add=True, shape=True) # delete the old transform node cmds.delete(fk_cc) # lock the fk joints channels # lock the extra channels on the knees and elbows for cc_type in NamingConventionEnums.LOCK_CHANNLES: if fk_jnt.find(cc_type) != -1: for channel in NamingConventionEnums.LOCK_CHANNLES[cc_type]: lock_channels(fk_jnt, channel) # lock the standard fk channels for channel in MayaCommandEnums.SCALE: lock_channels(fk_jnt, channel) for channel in MayaCommandEnums.TRANSLATION: lock_channels(fk_jnt, channel) lock_channels(fk_jnt, 'visibility', False, False) lock_channels(fk_jnt, 'radi', False, False) cmds.parent(fk_jnt, world=True) cmds.undo()
def test_undo1(): _new() cube, _ = cmds.polyCube() cmds.select(cube) assert_true(interactive.create_active_rigid()) assert_equals(len(cmds.ls(type="rdRigid")), 1) assert_equals(len(cmds.ls(type="rdScene")), 1) cmds.undo() assert_equals(len(cmds.ls(type="rdRigid")), 0) assert_equals(len(cmds.ls(type="rdScene")), 0)
def testComplexSetAssemblyChangeReps(self): """ This tests that changing representations of a USD reference assembly node in Maya that references a complex hierarchy of different types of prims works as expected, including undo'ing and redo'ing representation changes. """ assemblyNode = self._SetupScene('ComplexSet.usda', '/ComplexSet') # No representation has been activated yet, so ensure the assembly node # has no children. self._ValidateUnloaded(assemblyNode) # Change representations to 'Collapsed' and validate. cmds.assembly(assemblyNode, edit=True, active='Collapsed') self._ValidateCollapsed(assemblyNode) # Change representations to 'Cards' and validate. cmds.assembly(assemblyNode, edit=True, active='Cards') self._ValidateCards(assemblyNode) # Change representations to 'Expanded' and validate. cmds.assembly(assemblyNode, edit=True, active='Expanded') self._ValidateComplexSetExpandedTopLevel(assemblyNode) # Change representations to 'Full' and validate. cmds.assembly(assemblyNode, edit=True, active='Full') self._ValidateComplexSetFullTopLevel(assemblyNode) # Undo and the node should be back to Expanded. cmds.undo() self._ValidateComplexSetExpandedTopLevel(assemblyNode) # Undo and the node should be back to Cards. cmds.undo() self._ValidateCards(assemblyNode) # Undo and the node should be back to Collapsed. cmds.undo() self._ValidateCollapsed(assemblyNode) # Undo once more and no representation should be active. cmds.undo() self._ValidateUnloaded(assemblyNode) # Redo and it's back to Collapsed. cmds.redo() self._ValidateCollapsed(assemblyNode) # Redo and it's back to Cards. cmds.redo() self._ValidateCards(assemblyNode) # Redo again and it's back to Expanded. cmds.redo() self._ValidateComplexSetExpandedTopLevel(assemblyNode) # Redo once more and it's back to Full. cmds.redo() self._ValidateComplexSetFullTopLevel(assemblyNode)
def assert_values(fgetname, fsetname, loose): getter = getattr(p, fgetname) v = getter() assert isinstance(v, api.MVector) nv = api.MVector(i+v.x+1.0, i+v.y+2.0, i+v.z+3.0) getattr(p, fsetname)(nv) cmp_val(nv, getter(), loose) cmds.undo() cmp_val(v, getter(), loose) cmds.redo() cmp_val(nv, getter(), loose)
def test_displaySettings(self): mrvmaya.Scene.new(force = 1) mesh = nt.createNode("a1|b1|c1|d1|mesh", "mesh") mesh.tmp.msetInt(1) # TEMPLATE ########## assert mesh.isTemplate() cmds.undo() assert not mesh.isTemplate() a1 = mesh.root() a1.v.msetInt(0) # VISIBLE ######### assert not mesh.isVisible() cmds.undo() assert mesh.isVisible() # DRAWING OVERRIDES ################### a1.do.mchildByName('ove').msetInt(1) a1.do.mchildByName('ovdt').msetInt(2) assert mesh.displayOverrideValue('ovdt') == 2 cmds.undo() cmds.undo() assert mesh.displayOverrideValue('ovdt') == None
def bake(self): for shader, attr in self.shaders.items(): t = initstats.emit('bake', True) assignation = attr[0] path = attr[1] texture = attr[2] Umin, Vmin = tdLib.getUDIM(assignation) with tdLib.UndoContext(): assignation = self.extract_combine(assignation) dummyFile = cmds.convertSolidTx(texture + '.outColor', assignation, fileImageName=path, antiAlias=1, backgroundMode=2, resolutionX=512, resolutionY=512, fileFormat='tga', uvRange=[Umin, Umin+1, Vmin, Vmin+1]) cmds.undo() self.reinitialise_texture() logger.info(self.create_high(path)) logger.info(path) t.stop()
def _ValidateAllModelRepresentations(self, nodeName): """ Tests all representations of an assembly node that references a model (i.e. has no sub-assemblies). This should apply for both a standalone model reference node as well as a nested assembly reference node that references a model. """ # No representation has been activated yet, so ensure the assembly node # has no children. self._ValidateUnloaded(nodeName) # Change representations to 'Collapsed' and validate. cmds.assembly(nodeName, edit=True, active='Collapsed') self._ValidateCollapsed(nodeName) # Change representations to 'Cards' and validate. cmds.assembly(nodeName, edit=True, active='Cards') self._ValidateCards(nodeName) # Change representations to 'Expanded' and validate. cmds.assembly(nodeName, edit=True, active='Expanded') self._ValidateModelExpanded(nodeName) # Change representations to 'Full' and validate. cmds.assembly(nodeName, edit=True, active='Full') self._ValidateModelFull(nodeName) # Undo and the node should be back to Expanded. cmds.undo() self._ValidateModelExpanded(nodeName) # Undo and the node should be back to Cards. cmds.undo() self._ValidateCards(nodeName) # Undo and the node should be back to Collapsed. cmds.undo() self._ValidateCollapsed(nodeName) # Undo once more and no representation should be active. cmds.undo() self._ValidateUnloaded(nodeName) # Redo and it's back to Collapsed. cmds.redo() self._ValidateCollapsed(nodeName) # Redo and it's back to Cards. cmds.redo() self._ValidateCards(nodeName) # Redo again and it's back to Expanded. cmds.redo() self._ValidateModelExpanded(nodeName) # Redo once more and it's back to Full. cmds.redo() self._ValidateModelFull(nodeName)
def ren_handler(self, input_str): nodes = [(node, index) for index, node in enumerate(cmds.ls(sl=True, long=True))] if not nodes: raise NameError("Select some nodes first!") prev_chunk = cmds.undoInfo(q=True, chunkName=True) cmds.undoInfo(openChunk=True) try: do_rename(nodes, input_str) cmds.undoInfo(closeChunk=True) except: cmds.undoInfo(closeChunk=True) if not cmds.undoInfo(q=True, chunkName=True) == prev_chunk: cmds.undo() raise
def test_dagmod(self): undo.startUndo() dagmod = undo.DagModifier() obj = dagmod.createNode("transform") dagmod.renameNode(obj, "thisnewnode") dagmod.doIt() handle = api.MObjectHandle(obj) assert handle.isValid() and handle.isAlive() undo.endUndo() cmds.undo() assert not handle.isValid() and handle.isAlive() cmds.redo() assert handle.isValid() and handle.isAlive()
def test_dgmod( self ): persp = Node( "persp" ) front = Node( "front" ) side = Node( "side" ) # SIMPLE CONNECTION ################ # start undo uobj = undo.StartUndo( ) dgmod = undo.DGModifier( ) assert len( sys._maya_stack ) == 1 dgmod.connect( persp.message, front.isHistoricallyInteresting ) dgmod.doIt( ) # create undo step del( uobj ) assert len( sys._maya_stack ) == 0 cmds.undo() # undo connection # check connection - should be undone assert not persp.message.misConnectedTo( front.isHistoricallyInteresting ) cmds.redo() # redo it and check connection assert persp.message.misConnectedTo( front.isHistoricallyInteresting ) # connect and break existing conenction uobj = undo.StartUndo( ) dgmod = undo.DGModifier( ) dgmod.disconnect( persp.message, front.isHistoricallyInteresting ) dgmod.connect( side.message, front.isHistoricallyInteresting ) dgmod.doIt( ) del( uobj ) assert side.message.misConnectedTo( front.isHistoricallyInteresting ) cmds.undo() # old connection should be back assert persp.message.misConnectedTo( front.isHistoricallyInteresting ) # undo first change cmds.undo() # EMPTY DOIT ################ undo.startUndo( ) dgmod = undo.DGModifier( ) dgmod.doIt( ) undo.endUndo( ) cmds.undo()
def test_convenienceFunctions(self): # SELECTION ############ nt.select("persp") persp = nt.selection()[0] assert persp == nt.Node("persp") # clear selection nt.select() assert not nt.selection() # undo/redo cmds.undo() assert len(nt.selection()) == 1 cmds.redo() assert len(nt.selection()) == 0 # select object and selection list nt.select(persp) assert len(nt.selection()) == 1 nt.select(nt.toSelectionList(nt.selection())) assert len(nt.selection()) == 1 # select mixed nt.select(persp, "front") assert len(nt.selection()) == 2 # GET BY NAME ############### persp = nt.findByName("pers*")[0] assert persp == nt.Node("persp") # filter selection ################## nt.select("persp", "perspShape") assert len(nt.selection(api.MFn.kCamera)) == 1 assert len(list(nt.iterSelection(api.MFn.kCamera))) == 1 sl = nt.activeSelectionList() assert len(sl) and isinstance(sl, api.MSelectionList)
def node_handler(self, input_str): input_buffer = input_str.split() if len(input_buffer) > 1: node_type, node_name = input_buffer if "#" in node_name: raise NameError( "# symbol found in node name. " "This will completely f**k your maya scene. \n" "Try again without the #.") else: node_type = input_buffer[0] node_name = None #Wrap node creation and naming in a single chunk prev_chunk = cmds.undoInfo(q=True, chunkName=True) cmds.undoInfo(openChunk=True) try: if cmds.getClassification(node_type, satisfies="utility"): node = cmds.shadingNode(node_type, asUtility=True) elif cmds.getClassification(node_type, satisfies="shader"): node = cmds.shadingNode(node_type, asShader=True) elif cmds.getClassification(node_type, satisfies="texture"): node = cmds.shadingNode(node_type, asTexture=True) elif cmds.getClassification(node_type, satisfies="rendering"): node = cmds.shadingNode(node_type, asRendering=True) elif cmds.getClassification(node_type, satisfies="postprocess"): node = cmds.shadingNode(node_type, asPostProcess=True) elif cmds.getClassification(node_type, satisfies="light"): node = cmds.shadingNode(node_type, asLight=True) else: node = cmds.createNode(node_type) if node_name: cmds.rename(node, node_name.replace('\"', '')) cmds.undoInfo(closeChunk=True) except: cmds.undoInfo(closeChunk=True) if not cmds.undoInfo(q=True, chunkName=True) == prev_chunk: cmds.undo() raise
def _doTest(self): self.assertFalse(cmds.isTrue('UndoAvailable')) self.assertFalse(cmds.isTrue('RedoAvailable')) self.assertFalse(cmds.isTrue(self.CONDITION)) cmds.setAttr('persp.tx', 10) cmds.setAttr('top.tx', 10) self.assertTrue(cmds.isTrue('UndoAvailable')) self.assertFalse(cmds.isTrue('RedoAvailable')) self.assertTrue(cmds.isTrue(self.CONDITION)) cmds.undo() self.assertTrue(cmds.isTrue('UndoAvailable')) self.assertTrue(cmds.isTrue('RedoAvailable')) self.assertTrue(cmds.isTrue(self.CONDITION)) # after doing a new file, does UndoOrRedoAvailable reset properly? cmds.file(new=1, force=1) self.assertFalse(cmds.isTrue('UndoAvailable')) self.assertFalse(cmds.isTrue('RedoAvailable')) self.assertFalse(cmds.isTrue(self.CONDITION), 'expected failure here')
def attr_reorder( objs, attrs ): for obj in objs: #unlock all user defined attributes locked_attrs = cmds.listAttr(obj, ud=True, l=True) if locked_attrs: for locked_attr in locked_attrs: cmds.setAttr("%s.%s" %(obj, locked_attr), lock=0) #reorder attr by undo delete attribute for attr in attrs: try: cmds.deleteAttr(obj, at=attr) cmds.undo() except: cmds.warning("%s.%s cannont be deleted, skipping...") pass #relock all user defined attributes if locked_attrs: for locked_attr in locked_attrs: cmds.setAttr("%s.%s" %(obj, locked_attr), lock=1)
def test_keepWorldSpace(self): g = nt.createNode("g", "transform") t = nt.createNode("t", "transform") t.setParent(g) mainattrs = ("t","s") subattrs = ("x","y","z") count = 0.0 for ma in mainattrs: for sa in subattrs: getattr(g, ma).mchildByName(ma+sa).msetFloat(count) count += 1.0 # END for each sa # END for each ma # REPARENT TO WORLD ################### t = t.reparent(None, keepWorldSpace = 1) count = 0.0 for ma in mainattrs: for sa in subattrs: value = t.findPlug(ma+sa).asFloat() assert value == count count += 1.0 # END # END # undo - everything should be back to normal cmds.undo() self._checkIdentity(t) cmds.redo() # REPARENT TO PARENT NODE ########################### t = t.setParent(g, keepWorldSpace = 1) self._checkIdentity(t)
def testNestedAssemblyChangeReps(self): """ This tests that changing representations of a USD reference assembly node in Maya that references a hierarchy of assemblies works as expected, including undo'ing and redo'ing representation changes. """ assemblyNode = self._SetupScene('OneCube_set.usda', '/set') # No representation has been activated yet, so ensure the assembly node # has no children. self._ValidateUnloaded(assemblyNode) # Change representations to 'Collapsed' and validate. cmds.assembly(assemblyNode, edit=True, active='Collapsed') self._ValidateCollapsed(assemblyNode) # Change representations to 'Expanded' and validate. cmds.assembly(assemblyNode, edit=True, active='Expanded') self._ValidateNestedExpandedTopLevel(assemblyNode) # Change representations to 'Full' and validate. cmds.assembly(assemblyNode, edit=True, active='Full') self._ValidateNestedFullTopLevel(assemblyNode) # Undo and the node should be back to Expanded. cmds.undo() self._ValidateNestedExpandedTopLevel(assemblyNode) # Undo and the node should be back to Collapsed. cmds.undo() self._ValidateCollapsed(assemblyNode) # Undo once more and no representation should be active. cmds.undo() self._ValidateUnloaded(assemblyNode) # Redo and it's back to Collapsed. cmds.redo() self._ValidateCollapsed(assemblyNode) # Redo again and it's back to Expanded. cmds.redo() self._ValidateNestedExpandedTopLevel(assemblyNode) # Redo once more and it's back to Full. cmds.redo() self._ValidateNestedFullTopLevel(assemblyNode) # Now test changing representations of the sub-assembly. # Start by unloading the sub-assembly. childNodes = self._GetChildren(assemblyNode) nestedAssemblyNode = childNodes[0] cmds.assembly(nestedAssemblyNode, edit=True, active='') # From here, testing the nested assembly node should be the same as # testing a standalone model reference node. self._ValidateAllModelRepresentations(nestedAssemblyNode)
def shiftAttr(mode, *args): """shifts the selected attr up or down""" obj = cmds.channelBox('mainChannelBox',q=True,mol=True) if obj: attr = cmds.channelBox('mainChannelBox',q=True,sma=True) if attr: for eachObj in obj: udAttr = cmds.listAttr(eachObj,ud=True) if not attr[0] in udAttr: sys.exit('selected attribute is static and cannot be shifted') #temp unlock all user defined attributes attrLock = cmds.listAttr(eachObj,ud=True,l=True) if attrLock: for alck in attrLock: cmds.setAttr(eachObj + '.' + alck,lock=0) #shift down if mode == 0: if len(attr) > 1: attr.reverse() sort = attr if len(attr) == 1: sort = attr for i in sort: attrLs = cmds.listAttr(eachObj,ud=True) attrSize = len(attrLs) attrPos = attrLs.index(i) cmds.deleteAttr(eachObj,at=attrLs[attrPos]) cmds.undo() for x in range(attrPos+2,attrSize,1): cmds.deleteAttr(eachObj,at=attrLs[x]) cmds.undo() #shift up if mode == 1: for i in attr: attrLs = cmds.listAttr(eachObj,ud=True) attrSize = len(attrLs) attrPos = attrLs.index(i) if attrLs[attrPos-1]: cmds.deleteAttr(eachObj,at=attrLs[attrPos-1]) cmds.undo() for x in range(attrPos+1,attrSize,1): cmds.deleteAttr(eachObj,at=attrLs[x]) cmds.undo() #relock all user defined attributes if attrLock: for alck in attrLock: cmds.setAttr(eachObj + '.' + alck,lock=1)