def testMuteLayerImpl(addLayerFunc): def checkMuted(layer, stage): # Make sure the layer is muted inside the stage. self.assertTrue(stage.IsLayerMuted(layer.identifier)) self.assertTrue(layer.identifier in stage.GetMutedLayers()) # Make sure the stage does not used the muted layer self.assertFalse(layer in stage.GetLayerStack(False)) self.assertFalse(layer in stage.GetUsedLayers(False)) def checkUnMuted(layer, stage): self.assertFalse(stage.IsLayerMuted(layer.identifier)) self.assertFalse(layer.identifier in stage.GetMutedLayers()) self.assertTrue(layer in stage.GetLayerStack(False)) self.assertTrue(layer in stage.GetUsedLayers(False)) shapePath, stage = getCleanMayaStage() rootLayer = stage.GetRootLayer() layer = addLayerFunc(rootLayer) checkUnMuted(layer, stage) # Mute the layer cmds.mayaUsdLayerEditor(layer.identifier, edit=True, muteLayer=(True, shapePath)) checkMuted(layer, stage) # undo mute cmds.undo() checkUnMuted(layer, stage) # redo mute cmds.redo() checkMuted(layer, stage)
def _selectionTest(self, scene, objectA, objectB, proxyDagPath, displayMode): globalSelection = ufe.GlobalSelection.get() globalSelection.clear() self.assertSnapshotClose('%sunselected_%s.png' % (scene, displayMode)) # Select cube cmds.select(objectA) self.assertSnapshotClose('%sobjectA_%s.png' % (scene, displayMode)) # Select cylinder cmds.select(objectB, add=True) self.assertSnapshotClose('%sobjectA_and_objectB_%s.png' % (scene, displayMode)) # Undo, Redo cmds.undo() # Undo remove capsule from isolate select self.assertSnapshotClose('%sundoObjectB_%s.png' % (scene, displayMode)) cmds.redo() # Redo remove capsule from isolate select self.assertSnapshotClose('%sredoObjectB_%s.png' % (scene, displayMode)) # Proxy cmds.select(proxyDagPath) self.assertSnapshotClose('%sgatewayNode_%s.png' % (scene, displayMode)) cmds.select(clear=True) self.assertSnapshotClose('%sclear_%s.png' % (scene, displayMode))
def test_docmd(self): r = [0, False] def do(): r[0] += 1 def undo(): r[0] -= 1 r[1] = False def redo(): r[0] += 1 r[1] = True self.assertSuccessful(docmd, do, undo) self.assertEqual(r[0], 1) cmds.undo() self.assertEqual(r[0], 0) cmds.redo() self.assertEqual(r[0], 1) cmds.undo() self.assertEqual(r[0], 0) self.assertSuccessful(docmd, do, undo, redo) self.assertEqual(r[0], 1) cmds.undo() self.assertEqual(r[0], 0) cmds.redo() self.assertEqual(r[0], 1) self.assertTrue(r[1]) cmds.undo() self.assertEqual(r[0], 0) self.assertFalse(r[1])
def redoLast(self): if not self.redoStack: return result = None command = self.redoStack[-1] if command is None: return result exc_tb = None exc_type = None exc_value = None try: command.stats = base.CommandStats(command) cmds.redo() except errors.UserCancel: command.stats.finish(None) return except Exception: exc_type, exc_value, exc_tb = sys.exc_info() traceback.print_exception(exc_type, exc_value, exc_tb) raise finally: tb = None command = self.redoStack.pop() if exc_type and exc_value and exc_tb: tb = traceback.format_exception(exc_type, exc_value, exc_tb) elif command.isUndoable: self.undoStack.append(command) command.stats.finish(tb) return result
def test_access_objectDeletedUndoRedo(self): node = MayaObject(MayaTest.SCENE['transform_1']) node.MObject node.MObjectHandle attr = MayaObject(MayaTest.SCENE['transform_1_attr_1']) attr.MObject attr.MObjectHandle mc.delete(MayaTest.SCENE['transform_1']) with self.assertRaises(InvalidMayaObjectError): node.MObject with self.assertRaises(InvalidMayaObjectError): node.MObjectHandle with self.assertRaises(InvalidMayaObjectError): attr.MObject with self.assertRaises(InvalidMayaObjectError): attr.MObjectHandle mc.undo() node.MObject node.MObjectHandle attr.MObject attr.MObjectHandle mc.redo() with self.assertRaises(InvalidMayaObjectError): node.MObject with self.assertRaises(InvalidMayaObjectError): node.MObjectHandle with self.assertRaises(InvalidMayaObjectError): attr.MObject with self.assertRaises(InvalidMayaObjectError): attr.MObjectHandle mc.undo()
def measurePlugConnection(msg, func, callNumberList): """Call func of named operation msg number of times as in callNumberList""" for numcons in callNumberList: undoObj = undo.StartUndo() starttime = time.time() for i in xrange(numcons): func(i) elapsed = time.time() - starttime print >> sys.stderr, "%i %s in %f s ( %f / s )" % ( numcons, msg, elapsed, numcons / elapsed) del (undoObj) starttime = time.time() cmds.undo() undoelapsed = time.time() - starttime starttime = time.time() cmds.redo() redoelapsed = time.time() - starttime print >> sys.stderr, "UNDO / REDO Time = %f / %f ( %f * faster than initial creation )" % ( undoelapsed, redoelapsed, elapsed / max(redoelapsed, 0.001))
def redoIt(): """ See docstring for undoIt() above. This is equivalent except for redoing instead of undoing. :return: `None` """ m_cmds.redo()
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 testUnparentUSD(self): '''Unparent USD node.''' with OpenFileCtx("simpleHierarchy.ma"): # Unparent a USD node cubePathStr = '|mayaUsdProxy1|mayaUsdProxyShape1,/pCylinder1/pCube1' cubePath = ufe.PathString.path(cubePathStr) cylinderItem = ufe.Hierarchy.createItem(ufe.PathString.path( '|mayaUsdProxy1|mayaUsdProxyShape1,/pCylinder1')) proxyShapeItem = ufe.Hierarchy.createItem( ufe.PathString.path('|mayaUsdProxy1|mayaUsdProxyShape1')) proxyShape = ufe.Hierarchy.hierarchy(proxyShapeItem) cylinder = ufe.Hierarchy.hierarchy(cylinderItem) def checkUnparent(done): proxyShapeChildren = proxyShape.children() cylinderChildren = cylinder.children() self.assertEqual( 'pCube1' in childrenNames(proxyShapeChildren), done) self.assertEqual( 'pCube1' in childrenNames(cylinderChildren), not done) checkUnparent(done=False) cmds.parent(cubePathStr, world=True) checkUnparent(done=True) cmds.undo() checkUnparent(done=False) cmds.redo() checkUnparent(done=True)
def testAlreadyChild(self): '''Parenting an object to its current parent is a no-op.''' with OpenFileCtx("simpleHierarchy.ma"): shapeSegment = mayaUtils.createUfePathSegment( "|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 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 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 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 testDoOp(self): # Change visibility, undo / redo. cmd = self.contextOps.doOpCmd(['Toggle Visibility']) self.assertIsNotNone(cmd) attrs = ufe.Attributes.attributes(self.ball35Item) self.assertIsNotNone(attrs) visibility = attrs.attribute(UsdGeom.Tokens.visibility) self.assertIsNotNone(visibility) # Initially, Ball_35 has inherited visibility. self.assertEqual(visibility.get(), UsdGeom.Tokens.inherited) ufeCmd.execute(cmd) self.assertEqual(visibility.get(), UsdGeom.Tokens.invisible) cmds.undo() self.assertEqual(visibility.get(), UsdGeom.Tokens.inherited) cmds.redo() self.assertEqual(visibility.get(), UsdGeom.Tokens.invisible) cmds.undo() # Change variant in variant set. def shadingVariant(): contextItems = self.contextOps.getItems( ['Variant Sets', 'shadingVariant']) for c in contextItems: if c.checked: return c.item self.assertEqual(shadingVariant(), 'Ball_8') cmd = self.contextOps.doOpCmd( ['Variant Sets', 'shadingVariant', 'Cue']) self.assertIsNotNone(cmd) ufeCmd.execute(cmd) self.assertEqual(shadingVariant(), 'Cue') cmds.undo() self.assertEqual(shadingVariant(), 'Ball_8') cmds.redo() self.assertEqual(shadingVariant(), 'Cue') cmds.undo()
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 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 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 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 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_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 fforwardMemento(self): '''Redo through all items in memento. Starting at the bottom of the undo stack, perform redo to bring us to the top of the undo stack. During iteration, we ensure that the current state matches the state stored in the memento. ''' # For a memento list of length n, n-1 redo operations sets us current. self.assertEqual(self.memento[0], self.snapshotRunTimeUFE()) # Skip first for m in self.memento[1:]: cmds.redo() self.assertEqual(m, self.snapshotRunTimeUFE())
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 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 fforwardMemento(self): ''' Redo through all items in memento. Make sure that the current selection match their respective registered selection in memento. ''' # Test initial self.assertEqual(self.memento[0][0], mayaUtils.getMayaSelectionList()) self.assertEqual(self.memento[0][1], ufeUtils.getUfeGlobalSelectionList()) # Skip first for m in self.memento[1:]: cmds.redo() self.assertEqual(m[0], mayaUtils.getMayaSelectionList()) self.assertEqual(m[1], ufeUtils.getUfeGlobalSelectionList())
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 testDiscardEditsUndoRedo(self): '''Discard edits on a USD transform then undo and redo.''' # Edit as Maya first. (ps, xlateOp, usdXlation, aUsdUfePathStr, aUsdUfePath, aUsdItem, _, _, _, _, _) = createSimpleXformScene() cmds.mayaUsdEditAsMaya(aUsdUfePathStr) # Modify the scene. aMayaItem = ufe.GlobalSelection.get().front() mayaXlation = om.MVector(4, 5, 6) (aMayaPath, aMayaPathStr, aFn, mayaMatrix) = \ setMayaTranslation(aMayaItem, mayaXlation) # Verify the scene modifications. def verifyScenesModifications(): self.assertEqual(aFn.translation(om.MSpace.kObject), mayaXlation) mayaToUsd = ufe.PathMappingHandler.pathMappingHandler(aMayaItem) self.assertEqual( ufe.PathString.string(mayaToUsd.fromHost(aMayaPath)), ufe.PathString.string(aUsdUfePath)) verifyScenesModifications() # Discard Maya edits. cmds.mayaUsdDiscardEdits(aMayaPathStr) def verifyDiscard(): # Original USD translation values are preserved. usdMatrix = xlateOp.GetOpTransform( mayaUsd.ufe.getTime(aUsdUfePathStr)) self.assertEqual(usdMatrix.ExtractTranslation(), usdXlation) # Maya node is removed. with self.assertRaises(RuntimeError): om.MSelectionList().add(aMayaPathStr) verifyDiscard() # undo cmds.undo() verifyScenesModifications() # redo cmds.redo() verifyDiscard()
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 testGroupUndoRedo(self): '''Verify grouping after multiple undo/redo.''' cmds.file(new=True, force=True) # create a stage (stage, proxyShapePathStr, proxyShapeItem, contextOp) = createStage() # create a sphere generator sphereGen = SphereGenerator(3, contextOp, proxyShapePathStr) sphere1Path = sphereGen.createSphere() sphere1Prim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(sphere1Path)) sphere2Path = sphereGen.createSphere() sphere2Prim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(sphere2Path)) sphere3Path = sphereGen.createSphere() sphere3Prim = mayaUsd.ufe.ufePathToPrim( ufe.PathString.string(sphere3Path)) # group Sphere1, Sphere2, and Sphere3 groupName = cmds.group(ufe.PathString.string(sphere1Path), ufe.PathString.string(sphere2Path), ufe.PathString.string(sphere3Path)) # verify that groupItem has 3 children groupItem = ufe.GlobalSelection.get().front() groupHierarchy = ufe.Hierarchy.hierarchy(groupItem) self.assertEqual(len(groupHierarchy.children()), 3) cmds.undo() self.assertEqual([item for item in stage.Traverse()], [ stage.GetPrimAtPath("/Sphere3"), stage.GetPrimAtPath("/Sphere2"), stage.GetPrimAtPath("/Sphere1") ]) cmds.redo() self.assertEqual([item for item in stage.Traverse()], [ stage.GetPrimAtPath("/group1"), stage.GetPrimAtPath("/group1/Sphere1"), stage.GetPrimAtPath("/group1/Sphere2"), stage.GetPrimAtPath("/group1/Sphere3") ])
def runUndoRedo(self, attr, newVal, decimalPlaces=None): oldVal = attr.get() assert oldVal != newVal, "Undo / redo testing requires setting a value different from the current value" ufeCmd.execute(attr.setCmd(newVal)) if decimalPlaces is not None: self.assertAlmostEqual(attr.get(), newVal, decimalPlaces) newVal = attr.get() else: self.assertEqual(attr.get(), newVal) cmds.undo() self.assertEqual(attr.get(), oldVal) cmds.redo() self.assertEqual(attr.get(), newVal)
def test_node_creation_undo(self): undoWasEnabled = cmds.undoInfo(q=1, state=1) cmds.undoInfo(state=0) try: cmds.file(new=1, f=1) self.setBasicCam() self.setHdStormRenderer() cmds.undoInfo(state=1) cmds.undoInfo(openChunk=1) try: cubeTrans = cmds.polyCube() cubeShape = cmds.listRelatives(cubeTrans)[0] cubeRprim = self.rprimPath(cubeShape) cmds.select(clear=1) cmds.refresh() self.assertEqual([cubeRprim], self.getIndex()) self.assertSnapshotClose("instances_1.png") finally: cmds.undoInfo(closeChunk=1) cmds.undo() # the playblast command is entered into the undo queue, so we # need to disable without flusing the queue, so we can test redo cmds.undoInfo(stateWithoutFlush=0) try: cmds.refresh() self.assertEqual([], self.getIndex()) self.assertSnapshotClose("instances_0.png") finally: cmds.undoInfo(stateWithoutFlush=1) cmds.redo() cmds.undoInfo(stateWithoutFlush=0) try: cmds.refresh() self.assertEqual([cubeRprim], self.getIndex()) self.assertSnapshotClose("instances_1.png") finally: cmds.undoInfo(stateWithoutFlush=1) finally: cmds.undoInfo(state=undoWasEnabled)
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_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 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 test_create_nodes(self): # create all types of nodes, just individual samples, assure createNode # gets it right pe = nt.PointEmitter() assert pe.parent() is None # a shape with just a shortname works as well m = nt.createNode("myMesh", "mesh") mp = m.parent() assert isinstance(mp, nt.Transform) cmds.undo() cmds.undo() assert pe.isAlive() and not pe.isValid() assert mp.isAlive() and not mp.isValid() assert m.isAlive() and not m.isValid() cmds.redo() assert pe.isValid() cmds.redo() assert m.isValid() and mp.isValid()
def measurePlugConnection( msg, func, callNumberList ): """Call func of named operation msg number of times as in callNumberList""" for numcons in callNumberList: undoObj = undo.StartUndo() starttime = time.time() for i in xrange( numcons ): func( i ) elapsed = time.time( ) - starttime print >> sys.stderr, "%i %s in %f s ( %f / s )" % ( numcons, msg, elapsed, numcons / elapsed ) del( undoObj ) starttime = time.time() cmds.undo() undoelapsed = time.time() - starttime starttime = time.time() cmds.redo() redoelapsed = time.time() - starttime print >> sys.stderr, "UNDO / REDO Time = %f / %f ( %f * faster than initial creation )" % ( undoelapsed, redoelapsed, elapsed / max( redoelapsed, 0.001) )
def test_storageSetHandling( self ): mrvmaya.Scene.new( force = True ) snode = nt.createNode( "storage", "storageNode" ) # SIMPLE SET did = "objset" # error checking objset = snode.objectSet( did, 0, autoCreate = True ) assert isinstance( objset, nt.ObjectSet ) assert len(snode.setsByID(did)) == 1 # does not exist anymore cmds.undo() self.failUnlessRaises( AttributeError, snode.objectSet, did, 0, autoCreate = False ) # objset should be valid again cmds.redo() assert objset.isValid() and objset.isAlive() # del set snode.deleteObjectSet( did, 0 ) assert not objset.isValid() and objset.isAlive() self.failUnlessRaises( AttributeError, snode.objectSet, did, 0, False ) cmds.undo() assert objset.isValid() assert snode.objectSet(did, 0, False) == objset cmds.redo() assert not objset.isValid() self.failUnlessRaises( AttributeError, snode.objectSet, did, 0, False ) cmds.undo() # undo deletion after all assert objset.isValid() assert snode.objectSet(did, 0, False) == objset # SIMPLE OBJSET OPERATIONS # MULTIPLE SETS # PARTITION HANDLING ####################### partition = snode.setPartition( did, True ) assert snode.partition( did ) is not None cmds.undo() assert snode.partition( did ) is None cmds.redo() # set is available again # delete the single set we have, partition should be gone as well snode.deleteObjectSet( did, 0 ) assert not partition.isValid() cmds.undo() assert partition.isValid() # disable partition snode.setPartition( did, False ) assert snode.isAlive() and snode.isValid() # recently it would be deleted assert snode.partition( did ) is None snode.setPartition( did, True ) # new set, check partition oset = snode.objectSet( did, 1, autoCreate = 1 ) assert isinstance( oset, nt.ObjectSet ) assert len( oset.partitions() ) == 1 assert oset.partitions()[0] == snode.partition( did ) cmds.undo() assert len( oset.partitions() ) == 0 cmds.redo() # set is in multiple partitions, some from us, some from the user myprt = nt.createNode( "mypartition", "partition" ) myprt.addSets( oset ) assert myprt != snode.partition( did ) snode.setPartition( did, False ) assert myprt.sets( )[0] == oset assert len( oset.partitions() ) == 1 # undo / redo cmds.undo() assert len( oset.partitions() ) == 2 cmds.redo() assert len( oset.partitions() ) == 1
def test_MPlug( self ): persp = nt.Node( "persp" ) front = nt.Node( "front" ) side = nt.Node( "side" ) matworld = persp.worldMatrix assert isinstance(matworld.mfullyQualifiedName(), basestring) str( matworld ) repr( matworld ) # CONNECTIONS ####################### # CHECK COMPOUND ACCESS tx = persp.translate.mchildByName('tx') # can access attributes twice persp.translate # DO CONNECTIONS ( test undo/redo ) persp.translate.mconnectTo(front.translate, force=True) assert persp.translate.misConnectedTo(front.translate) # misConnectedTo assert persp.translate.minput().isNull( ) cmds.undo( ) assert not persp.translate.misConnectedTo( front.translate ) cmds.redo( ) assert front.translate in persp.translate.moutputs() # check moutput assert persp.translate.moutput() == front.translate assert persp.rotate.moutput().isNull() # unconnected return nullPlus as minput does # CHECK CONNECTION FORCING persp.translate.mconnectTo(front.translate, force=False) # already connected self.failUnlessRaises( RuntimeError, persp.s.mconnectTo, front.translate, force=False ) # overwrite connection side.translate.mconnectTo(front.translate) # force default True assert side.translate.misConnectedTo(front.translate) # undo - old connection should be back cmds.undo() assert persp.translate.misConnectedTo(front.translate) # disconnect input front.translate.mdisconnectInput() assert not persp.translate.misConnectedTo(front.translate) cmds.undo() # disconnect output persp.t.mdisconnectOutputs( ) assert len( persp.translate.moutputs() ) == 0 cmds.undo() assert persp.t.misConnectedTo( front.translate ) # disconnect from persp.t.mdisconnectFrom(front.translate) assert not persp.t.misConnectedTo(front.t) cmds.undo() assert persp.t.misConnectedTo(front.t) # COMPARISONS assert persp.t != front.t assert persp.t.mchildByName('tx') != persp.t.mchildByName('ty') # affected plugs affectedPlugs = persp.t.maffects( ) assert len( affectedPlugs ) > 1 affectedPlugs = persp.t.maffected( ) assert len( affectedPlugs ) > 1 # test multi connections sn = nt.createNode("network1", "network") sn2 = nt.createNode("network2", "network") tn = nt.createNode("network3", "network") def pir(array_plug, range_iter): for index in range_iter: yield array_plug.elementByLogicalIndex(index) # END for each item in range # END plugs-in-range # connect 10 to 10 r = range(10) api.MPlug.mconnectMultiToMulti( izip(pir(sn.a, r), pir(tn.affectedBy, r)), force=False) for i in r: assert sn.a.elementByLogicalIndex(i).misConnectedTo(tn.affectedBy.elementByLogicalIndex(i)) # END make connection assertion # connection of overlapping range fails without force r = range(5, 15) self.failUnlessRaises(RuntimeError, api.MPlug.mconnectMultiToMulti, izip(pir(sn2.a, r), pir(tn.affectedBy, r)), force=False) # there no connection should have worked ( its atomic ) # hence slot 10 is free persp.tx > tn.affectedBy.elementByLogicalIndex(10) # force connection works api.MPlug.mconnectMultiToMulti(izip(pir(sn2.a, r), pir(tn.affectedBy, r)), force=True) for i in r: assert sn2.a.elementByLogicalIndex(i).misConnectedTo(tn.affectedBy.elementByLogicalIndex(i)) # END make connection assertion # ATTRIBUTES AND UNDO ####################### funcs = ( ( "isLocked", "msetLocked" ), ( "isKeyable", "msetKeyable" ), ( "isCachingFlagSet", "msetCaching" ), ( "isChannelBoxFlagSet", "msetChannelBox" ) ) plugnames =( "t", "tx", "r","rx", "s", "sy" ) for p in plugnames: plug = persp.findPlug(p) for ( getname, setname ) in funcs: fget = getattr( plug, getname ) fset = getattr( plug, setname ) curval = fget() oval = bool( 1 - curval ) fset( oval ) # SPECIAL HANDLING as things cannot be uncached if setname == "msetCaching": continue assert fget() == oval cmds.undo() assert fget() == curval cmds.redo() assert fget() == oval fset( curval ) # reset # END for each function # END for each plugname # QUERY ############################ # ELEMENT ITERATION matworld.evaluateNumElements( ) for elm in matworld: assert elm.mparent( ) == matworld translate = persp.translate assert len( translate.mchildren() ) == translate.numChildren() # CHILD ITERATION for child in translate.mchildren( ): assert child.mparent( ) == translate assert len( translate.mchildren() ) == 3 # SUB PLUGS GENERAL METHOD assert len( matworld ) == len( matworld.msubPlugs() ) assert translate.numChildren() == len( translate.msubPlugs() ) assert len( translate.msubPlugs() ) == 3 # ARRAY CONNECTIONS ################### objset = nt.createNode( "set1", "objectSet" ) partition = nt.createNode( "partition1", "partition" ) pma = nt.createNode( "plusMinusAverage1", "plusMinusAverage" ) destplug = persp.translate.mconnectToArray( pma.input3D, exclusive_connection = True ) assert persp.translate.misConnectedTo(destplug) # exclusive connection should return exisiting plug assert persp.translate.mconnectToArray( pma.input3D, exclusive_connection = True ) == destplug # but newones can also be created assert persp.translate.mconnectToArray( pma.input3D, exclusive_connection = False ) != destplug #assert objset.partition.mconnectToArray( partition.sets, exclusive_connection = False ) != destplug # assure the standin classes are there - otherwise my list there would # bind to the standins as the classes have not been created yet plugs = [ matworld, translate ] for plug in plugs: plug.mwrappedAttribute() # CHECK ATTRIBUTES and NODES for plug, attrtype in zip( plugs, [ nt.TypedAttribute, nt.CompoundAttribute ] ): attr = plug.mwrappedAttribute( ) assert isinstance( attr, nt.Attribute ) assert isinstance( attr, attrtype ) node = plug.mwrappedNode() assert isinstance( node, nt.Node ) assert node == persp # UNDO / REDO ############## cam = nt.createNode( "myTrans", "transform" ) testdb = [ ( cam.visibility, "Bool", True, False ), ( cam.tx, "Double", 0.0, 2.0 ) ] # TODO: Add all missing types ! for plug, typename, initialval, targetval in testdb: getattrfunc = getattr( plug, "as"+typename ) setattrfunc = getattr( plug, "mset"+typename ) assert getattrfunc() == initialval setattrfunc( targetval ) assert getattrfunc() == targetval cmds.undo() assert getattrfunc() == initialval cmds.redo() assert getattrfunc() == targetval # END for each tuple in testdb # TEST EVERYTHING ################# # This part has been written to be very sure every customized method gets # called at least once. I don't trust my 'old' tests, although they do # something and are valuable to the testing framework. nwnode = nt.Network() persp.msg.mct(nwnode.affectedBy.elementByLogicalIndex(0)) front.msg.mct(nwnode.affectedBy.elementByLogicalIndex(1)) t = persp.translate tx = persp.tx wm = persp.wm a = nwnode.affectedBy a.evaluateNumElements() assert len(wm) == 1 and len(a) == 2 assert isinstance(iter(wm).next(), api.MPlug) assert len(list(a)) == 2 # test str/repr assert str(wm) != str(a) assert repr(wm) != str(wm) assert wm != a # is it really necessary to apply custom handling here ? Check __eq__ method # test comparison assert a[0] == a[0] assert a[0] != a[1] # mparent assert tx.mparent() == t assert a[0].mparent() == a # mchildren assert len(a[0].mchildren()) == 0 assert len(t.mchildren()) == 3 # mchildByName assert t.mchildByName('tx') == tx self.failUnlessRaises(TypeError, tx.mchildByName, 'something') self.failUnlessRaises(AttributeError, t.mchildByName, 'doesntexist') # msubPlugs assert len(t.msubPlugs()) == 3 assert len(a.msubPlugs()) == 2 assert len(tx.msubPlugs()) == 0 # msetLocked tx.msetLocked(1) assert tx.isLocked() tx.msetLocked(0) assert not tx.isLocked() # msetKeyable tx.msetKeyable(0) assert not tx.isKeyable() tx.msetKeyable(1) assert tx.isKeyable() # msetCaching tx.msetCaching(0) #assert not tx.isCachingFlagSet() # for some reason, the caching cannot be changed here tx.msetCaching(1) assert tx.isCachingFlagSet() == 1 # msetChannelBox tx.msetChannelBox(0) assert not tx.isChannelBoxFlagSet() tx.msetChannelBox(1) assert tx.isChannelBoxFlagSet() == 1 # mconnectMultiToMulti # is tested elsewhere # connectTo self.failUnlessRaises(RuntimeError, persp.msg.mconnectTo, a[1], force=False) # already connected front.msg.mconnectTo(a[1], force=False) # already connected front.msg.mconnectTo(a[0], force=True) # force breaks connections persp.msg.mconnectTo(a[0]) # default is force # mconnectToArray # sufficiently tested ( -> st ) # mdisconnect # st # mdisconnectInput # st # mdisconnectOutputs # st # mdisconnectFrom # st # mdisconnectNode # st # mhaveConnection assert api.MPlug.mhaveConnection(front.msg, a[1]) and api.MPlug.mhaveConnection(a[1], front.msg) assert not api.MPlug.mhaveConnection(persp.msg, a[1]) and not api.MPlug.mhaveConnection(a[1], persp.msg) # misConnectedTo # st # moutputs assert len(front.msg.moutputs()) == 1 and front.msg.moutputs()[0] == a[1] assert len(a[0].moutputs()) == 0 # moutput # st # minputs assert len(a.minputs()) == 2 assert len(a[1].minputs()) == 1 # miterGraph # st # miterInputGraph # st # miterOutputGraph # st # minput # st # mconnections assert len(front.msg.mconnections()) == 2 assert len(a[1].mconnections()) == 2 # mdependencyInfo m = nt.Mesh() assert len(m.outMesh.maffected()) assert isinstance(m.outMesh.maffected()[0], api.MPlug) assert m.outMesh.maffected() == m.outMesh.mdependencyInfo(by=True) assert isinstance(m.inMesh.maffects(), list) # no affected items for some reason assert m.inMesh.maffects() == m.inMesh.mdependencyInfo(by=False) # mnextLogicalIndex|plug assert a.mnextLogicalIndex() == 2 assert a.mnextLogicalPlug().logicalIndex() # mwrappedAttribute assert isinstance(a.mwrappedAttribute(), nt.Attribute) # mwrappedNode assert isinstance(a.mwrappedNode(), nt.Node) # masData nt.PolyCube().output.mconnectTo(m.inMesh) # need data here assert isinstance(m.outMesh.masData(), nt.Data) # mfullyQualifiedName assert a.mfullyQualifiedName() != a.partialName()
def test_undoBasics(self): undo.startUndo() # put some undoable operation op = TestUndoQueue.TestOperation() op.doIt() # apply operation assert len(sys._maya_stack) == 1 assert sys._maya_stack_depth == 1 undo.endUndo() # STACK MUST BE EMPTY# # as it has been taken by the command assert len(sys._maya_stack) == 0 # UNDO cmds.undo() assert op.numDoit == op.numUndoIt # REDO cmds.redo() assert op.numDoit - 1 == op.numUndoIt # OP WITHOUT PUSH self.failUnlessRaises(AssertionError, TestUndoQueue.TestOperation) mrvmaya.Mel.flushUndo() # handle undo ourselves persp = Node("persp") curvalue = persp.tx.asFloat() newvalue = curvalue + 1.0 undo.startUndo() persp.tx.msetFloat(newvalue) assert persp.tx.asFloat() == newvalue undo.undoAndClear() # end undo must come afterwards, otherwise the comand takes the queue undo.endUndo() assert persp.tx.asFloat() == curvalue # its back to normal without an official undo # UNDO AND FILE FLUSHES ######################## # Our stack must be flused once maya's undo queue gets flushed # This is critical if an undoable method causes undo flushes, but also # built up our own intermediate stack which might now contain entries from # a non-existing scene # NOTE: Currently this is a known limitation that could be circumvented # with some pre-scene-callbacks trans = nt.createNode("mytrans", "transform") undo.startUndo() trans.tx.msetFloat(10.0) assert len(sys._maya_stack) == 1 mrvmaya.Scene.new(force=1) # DO NOT FAIL - allow releases to be done which would fail otherwise # Print a reminder though # TODO: Try to fix this # assert len( sys._maya_stack ) == 0, "Known Undo-Limitation" # http://github.com/Byron/mrv/issues#issue/2 print "FIX KNOWN UNDO LIMITATION WHICH HAS BEEN SKIPPED TO ALLOW RELEASES" undo.endUndo()
def test_checkNamespaces( self ): rootns = Namespace( Namespace.rootpath ) childns = rootns.children( ) assert rootns.isRoot() assert len( childns ) == 3 for ns in childns: assert ns.parent( ) == rootns assert ns.isAbsolute() assert ns.exists() allChildren = ns.childrenDeep( ) assert len( allChildren ) == 2 for child in allChildren: assert len( child.parentDeep() ) # end for each childns # cannot delete root namespace self.failUnlessRaises( ValueError, rootns.delete ) # default constructor creates root namesapce assert Namespace() == RootNamespace # create a few namespaces for ns in [ "newns", "newns:child", "longer:namespace",":hello:world:here" ]: curns = Namespace.current() newns = Namespace.create( ns ) assert newns.exists() assert Namespace.current() == curns # test undo: creation cmds.undo() assert not newns.exists() cmds.redo() assert newns.exists() # test undo: change current assert newns.setCurrent() == newns assert Namespace.current() == newns cmds.undo() assert Namespace.current() == curns # rename all children for ns in childns: newname = str( ns ) + "_renamed" renamedns = ns.rename( newname ) assert renamedns == renamedns assert renamedns.exists() # delete all child namepaces childns = rootns.children() # check relative namespace for ns in childns: assert not ns.relativeTo( rootns ).isAbsolute() for ns in childns: allchildren = ns.childrenDeep() ns.delete( move_to_namespace = rootns ) assert not ns.exists() for child in allChildren: assert not child.exists() # ITER ROOT NAMESPACE - REAL OBJECTS curns = Namespace.current() numobjs = 0 for obj in RootNamespace.iterNodes( depth = 0 ): numobjs += 1 assert numobjs != 0 assert Namespace.current() == curns # ITER STRINGS newnumobjs = 0 for obj in Namespace( ":" ).iterNodes( depth = 0, asNode = 0 ): newnumobjs += 1 assert newnumobjs == numobjs assert Namespace.current() == curns # empty namespace must come out as root ns = Namespace( "" ) assert ns == Namespace.rootpath # TEST SPLIT ########### t = Namespace.splitNamespace( "hello:world" ) assert t[0] == ":hello" and t[1] == "world" t = Namespace.splitNamespace( ":hello:world:there" ) assert t[0] == ":hello:world" and t[1] == "there" # TEST TORELATIVE ################# assert Namespace( "hello" ).toRelative( ) == "hello" assert Namespace( ":hello" ).toRelative( ) == "hello" # TEST REPLACEMENT ##################### rootrel,rootabs,nsrel,nsabs,multins,multinabs = "rootnsrel", ":rootnsabs", "ns:relns", ":ns:absolutens", "ns1:ns2:multinsrel",":ns1:ns2:ns3:ns2:multinabs" rootns,ns,ns2 = Namespace( "" ),Namespace("ns",absolute=False),Namespace( "ns2",absolute=False ) newnssingle = Namespace( "nns" ) newnsmulti = Namespace( "nns1:nns2" ) assert rootns.substitute( rootrel, newnssingle ) == ":nns:rootnsrel" assert rootns.substitute( rootrel, newnsmulti ) == ":nns1:nns2:rootnsrel" assert rootns.substitute( rootabs, newnssingle ) == ":nns:rootnsabs" assert rootns.substitute( rootabs, newnsmulti ) == ":nns1:nns2:rootnsabs" assert ns2.substitute( multins, newnssingle ) == "ns1:nns:multinsrel" assert ns2.substitute( multins, newnsmulti ) == "ns1:nns1:nns2:multinsrel" assert ns2.substitute( multinabs, newnssingle ) == ":ns1:nns:ns3:nns:multinabs" assert ns2.substitute( multinabs, newnsmulti ) == ":ns1:nns1:nns2:ns3:nns1:nns2:multinabs" # empty replacement - remove ns assert ns2.substitute( multins, "" ) == "ns1:multinsrel" assert ns.substitute( nsabs, "" ) == ":absolute" # namespaces have slots self.failUnlessRaises( AttributeError, setattr, ns, "myattr", 2 )
def test_createNodes(self): names = ["hello","bla|world","this|world|here","that|this|world|here"] nsnames = ["a:hello","blab|b:world","c:this|b:world","d:that|c:this|b:world|a:b:c:d:here"] types = ["facade", "nurbsCurve", "nurbsSurface", "subdiv"] # SIMPLE CREATION: Paths + nested namespaces for i in range(len(names)): ntype = types[i] newnode = nt.createNode(names[i], ntype) assert isinstance(newnode, getattr(nt, capitalize(ntype))) assert newnode.isValid() and newnode.isAlive() # test undo cmds.undo() assert not newnode.isValid() and newnode.isAlive() cmds.redo() assert newnode.isValid() and newnode.isAlive() newnsnode = nt.createNode(nsnames[i], ntype) assert isinstance(newnsnode, getattr(nt, capitalize(ntype))) assert newnsnode.isValid() and newnsnode.isAlive() # test undo cmds.undo() assert not newnsnode.isValid() and newnsnode.isAlive() cmds.redo() assert newnsnode.isValid() and newnsnode.isAlive() # END for each created object # EMPTY NAME and ROOT self.failUnlessRaises(RuntimeError, nt.createNode, '|', "facade") self.failUnlessRaises(RuntimeError, nt.createNode, '', "facade") # CHECK DIFFERENT ROOT TYPES depnode = nt.createNode("blablub", "facade") self.failUnlessRaises(NameError, nt.createNode, "|blablub|:this", "transform", renameOnClash = False) # DIFFERENT TYPES AT END OF PATH nt.createNode("this|mesh", "mesh") self.failUnlessRaises(NameError, nt.createNode, "this|mesh", "nurbsSurface", forceNewLeaf = False) # renameOnClash - it fails if the dep node exists first nt.createNode("node", "facade") self.failUnlessRaises(NameError, nt.createNode, "this|that|node", "mesh", renameOnClash = False) # obj exists should match dg nodes with dag node like path (as they occupy the same # namespace after all assert nt.objExists("|node") # it also clashes if the dg node is created after a dag node with the same name nt.createNode("that|nodename", "mesh") self.failUnlessRaises(NameError, nt.createNode, "nodename", "facade", renameOnClash = False) # it should be fine to have the same name in several dag levels though ! newmesh = nt.createNode("parent|nodename", "transform") newmesh1 = nt.createNode("parent|nodename|nodename", "mesh") newmesh2 = nt.createNode("otherparent|nodename|nodename", "mesh") assert newmesh != newmesh1 assert newmesh1 != newmesh2 # FORCE NEW ############## oset = nt.createNode("objset", "objectSet", forceNewLeaf = False) newoset = nt.createNode("objset", "objectSet", forceNewLeaf = True) assert oset != newoset # would expect same set to be returned sameoset = nt.createNode("objset", "objectSet", forceNewLeaf = False) assert sameoset == oset # force new and dag paths newmesh3 = nt.createNode("otherparent|nodename|nodename", "mesh", forceNewLeaf = True) assert newmesh3 != newmesh2
def test_wrapDepNode(self): node = nt.Node("defaultRenderGlobals") # SKIP CHECKS TEST self.failUnlessRaises(ValueError, nt.Node, "this") # does not exist # results inconsitent between maya 8.5 and 2008, thus we skip it as it is not of much value # self.failUnlessRaises(TypeError, nt.Node, "this", 1) # skip check , maya throws # string should be name assert str(node) == node.name() repr(node) # must not have methods that require undo try: node.create("this") node.setName("this") except AttributeError: pass except: raise # could fail, but raise is better # get simple attributes for attr in ["preMel", "postMel"]: plug = getattr(node, attr) assert plug == getattr(node, attr) assert plug == node.findPlug(attr) assert not plug.isNull() # check connection methods cons = node.connections() assert len(cons) # DEPENDENCY INFO persp = nt.Node("persp") affected_attrs = persp.dependencyInfo("t", by=0) assert len(affected_attrs) > 1 affected_attrs = persp.dependencyInfo("t", by=1) assert len(affected_attrs) > 1 assert isinstance(affected_attrs[0], nt.Attribute) # CHECK LAZY WRAPPING # get mfn lazy wrapped attributes t = node.attributeCount() for state in [1,0]: node.setLocked(state) assert node.isLocked() == state # ATTRIBUTES attr = node.attribute(0) attr.name() assert not attr.isNull() for i in xrange(node.attributeCount()): attr = node.attribute(i) assert not attr.isNull() and attr.name() # CHECK namespaces - should be root namespace ns = node.namespace() assert ns == nt.Namespace.rootpath # RENAME DEP NODES ###################### node = nt.createNode("mynode", "facade") renamed = node.rename("myrenamednode") assert renamed.name() == "myrenamednode" assert node == renamed # undo - redo cmds.undo() assert node.name() == "mynode" cmds.redo() assert node.name() == "myrenamednode" # trigger namespace error self.failUnlessRaises(RuntimeError, node.rename, "nsdoesnotexist:othername", autocreateNamespace = False) # now it should work node.rename("nsdoesnotexist:othername", autocreateNamespace=True) # multi undo - namespace is one operation in this one cmds.undo() assert not nsm.existsNamespace("nsdoesnotexist") cmds.redo() assert node.isValid() # rename to same name renamed = node.rename("nsdoesnotexist") # should be fine assert renamed == node # othernode with different type exists othernode = nt.createNode("othernode", "groupId") self.failUnlessRaises(RuntimeError, node.rename, "othernode", renameOnClash = False) # locking othernode.setLocked(1) assert othernode.isLocked() cmds.undo() assert not othernode.isLocked() cmds.redo() assert othernode.isLocked() othernode.setLocked(0) assert not othernode.isLocked() # works if rename enabeld though node.rename("othernode")
def test_wrapDagNode(self): mesh = nt.createNode("parent|mesh", "mesh") parent = mesh.parent() # simple rename mesh.rename("fancymesh") # simple dupl test duplbase = nt.createNode("parent|this|other|duplbase", "mesh") transcopy = duplbase.transform().duplicate() copy = duplbase.duplicate("parent|this|other|duplcopy") assert copy != duplbase assert str(copy) != str(duplbase) assert str(copy) == "|parent|this|other|duplcopy" # TEST ADDITIONAL OPTIONS for i in range(1,3): ocopy = duplbase.duplicate() assert str(ocopy) == str(duplbase) + str(i) ocopy = duplbase.duplicate(newTransform=1) assert ocopy.basename() == duplbase.basename() assert str(ocopy.parent()) == str(duplbase.parent()) + str(i + 1) # undo both duplications and redo # CRASHES MAYA AFTER REDO # and if someone tries to access an object already created #cmds.undo() #cmds.undo() #assert duplbase.isValid() #cmds.redo() #cmds.redo() # END for each copy # simple reparent otherparent = nt.createNode("oparent", "transform") mesh.reparent(otherparent) # REPARENT UNDO TEST cmds.undo() assert mesh.parent() == parent cmds.redo() assert mesh.parent() == otherparent # REPARENT RENAME CLASH origmesh = nt.createNode("parent|fancymesh", "mesh") # "|parent|fancymesh" self.failUnlessRaises(RuntimeError, mesh.reparent, parent , renameOnClash = False) # RENAME CLASH DAG NODE othermesh = nt.createNode("parent|mesh", "mesh") self.failUnlessRaises(RuntimeError, origmesh.rename, "mesh", renameOnClash = False) # now it works othermesh.rename("mesh", renameOnClash = True) assert othermesh.basename() == "mesh" # shape under root self.failUnlessRaises(RuntimeError, mesh.reparent, None) # REPARENT AGAIN # should just work as the endresult is the same mesh.reparent(otherparent) # it will also trigger a new undo event, so undo will react as it should mesh.reparent(otherparent) # "|otherparent|fancymesh" # REPARENT UNDER SELF self.failUnlessRaises(RuntimeError, mesh.reparent, mesh) # reparent transform to world wtrans = nt.createNode("parent2|worldtrans", "transform") parent = nt.Node("parent2") oparent = nt.createNode("oparent2", "transform") wtrans = wtrans.reparent(None) wtransnewparent = wtrans.setParent(parent) assert wtrans == wtransnewparent assert wtransnewparent.instanceCount(1) == 1 wtransnewparent.addParent(oparent) assert wtransnewparent.instanceCount(1) == 2 wtransnewparent.removeParent(oparent) assert wtrans.instanceCount(1) == 1 # OBJECT NAVIGATION ####################### # TODO: navigate the object properly # DUPLICATE (Dag only) ######################### newmesh = mesh.duplicate("|duplparent|duplmesh") assert str(newmesh) == "|duplparent|duplmesh" self.failUnlessRaises(RuntimeError, mesh.duplicate, "|duplparent2|doesntexistns:duplmesh", autocreateNamespace = False) assert newmesh != mesh instbase = nt.createNode("|duplparent2|newnamespace:instmesh", "transform") meshinst = instbase.addInstancedChild(mesh) meshinstname = str(meshinst) # UNDO DUPLICATE ################# cmds.undo() # this object will end up pointing to the same object , as it came from, use string test assert not nt.objExists(meshinstname) cmds.redo() cmds.undo() cmds.redo() assert meshinst != mesh assert meshinst.isAlive() and meshinst.isValid() and str(meshinst) == meshinstname # Duplicate TRANSFORM (just a name given) # dag paths should be different although object is the same mesh = nt.createNode("|parent|mybeautifuluniquemeshname", "mesh") meshassert = nt.createNode("|parent|mesh", "mesh") meshself = nt.Node("|parent|mybeautifuluniquemeshname") assert mesh == meshself # connect it, to track the instance by connection persp = nt.Node("persp") perspplug = persp.t.mchildByName('tx') triplug = meshself.maxTriangles perspplug.mconnectTo(triplug) # target does exist # this is blocking the target instance name with an incorrect type nt.createNode("parent|this|mybeautifuluniquemeshname", "transform") self.failUnlessRaises(RuntimeError, mesh.duplicate, "|parent|this") # if the path is too short ... self.failUnlessRaises(NameError, mesh.duplicate, str(mesh.transform())) self.failUnlessRaises(NameError, mesh.parent().duplicate, '|') meshinstname = mesh.transform().fullChildName("newns:meshinst") assert isinstance(meshinst, nt.Mesh)
def test_childEditing(self): base = nt.createNode("basenode", "transform") obase = nt.createNode("otherbasenode", "transform") trans = nt.createNode("trans", "transform") otrans = nt.createNode("parent|trans", "transform") mesh = nt.createNode("meshparent|meshshape", "mesh") curve = nt.createNode("nurbsparent|ncurve", "nurbsCurve") itemlist = [trans, mesh, curve] instlist = list() # MULTIPLE ADDS #################### # Returns the same instance - its what the user wants for item in itemlist: assert item.instanceCount(0) == 1 inst = base.addInstancedChild(item) assert base.addInstancedChild(item) == inst assert item.instanceCount(0) == 2 assert item != inst and inst.isValid() and item.isValid() instlist.append(inst) # UNDO TEST # undo basemesh cmds.undo() assert not inst.isValid() and inst.isAlive() cmds.redo() assert inst.isValid() and inst.isAlive() # END for each object including undo/redo # KEEP PARENT FALSE - USE INSTANCES # TODO: this test needs a redo - the original purpose got lost when # the addChild method has been changed, additionally it needs to be # better documented as this instancing stuff is not easily understood if # one just sees the code for orig,inst in zip(itemlist, instlist): assert orig.object() == inst.object() and orig != inst ninst = obase.addChild(inst, keepExistingParent=False) # # duplicate adds are not problem, but it can't be tested as the inst is already invalid # assert obase.addChild(inst, keepExistingParent=False) == ninst assert not inst.isValid() assert ninst.isValid() and ninst.isAlive() assert orig.isValid() # original may not be influenced by that operation # undo / redo cmds.undo() assert orig.isValid() assert not ninst.isValid() assert inst.isValid() cmds.redo() assert not inst.isValid() and orig.isValid() and ninst.isValid() # END for each instance # RENAME ON CLASH = False self.failUnlessRaises(RuntimeError, obase.addChild, otrans, renameOnClash = False) # RENAME ON CLASH = True otransname = str(otrans) renamedtrans = obase.addChild(otrans, renameOnClash = True) assert renamedtrans.isValid() renamedname = str(renamedtrans) assert renamedname != otransname cmds.undo() assert nt.objExists(otransname) and not nt.objExists(renamedname) cmds.redo()
def test_memberHandling( self ): s = nt.createNode( "memberSet", "objectSet" ) # ADD/REMOVE SINGLE MEMBER #################### # add Node ( dgnode ) memberlist = self._getMemberList( ) for i,member in enumerate( memberlist ): s.addMember( member ) assert s.members( ).length() == i+1 assert s.isMember( member ) cmds.undo() assert s.members( ).length() == i cmds.redo() # end for each member # clear the set by undoing it all for i in range( len( memberlist ) ): cmds.undo() assert s.members().length() == 0 # get it back for i in range( len( memberlist ) ): cmds.redo() assert s.members().length() == len( memberlist ) # MULTI-MEMBER UNDO/REDO ########################## s.removeMembers( memberlist ) assert s.members().length() == 0 cmds.undo() assert s.members().length() == len( memberlist ) cmds.redo() assert s.members().length() == 0 # add members again s.addMembers( memberlist ) assert s.members().length() == len( memberlist ) cmds.undo() assert s.members().length() == 0 cmds.redo() assert s.members().length() == len( memberlist ) # remove all members for i,member in enumerate( memberlist ): s.removeMember( member ) assert s.members().length() == 0 # ADD/REMOVE MULTIPLE MEMBER ###################### # add node list s.addMembers( memberlist ) assert s.members().length() == len( memberlist ) s.clear() assert s.members().length() == 0 # Add selection listx sellist = nt.toSelectionList( memberlist ) s.addMembers( sellist ) assert s.members().length() == sellist.length() # remove members from sellist s.removeMembers( sellist ) assert s.members().length() == 0 cmds.undo() assert s.members().length() == sellist.length() cmds.redo() assert s.members().length() == 0 # test smart add s.add(sellist) assert len(s) == len(sellist) single_item = sellist.mtoIter().next() s.add(single_item) assert len(s) == len(sellist) s.discard(single_item) assert single_item not in s s.discard(sellist.mtoList()) assert len(s) == 0 # TEST CLEAR ############# s.addMembers( sellist ) assert s.members().length() == sellist.length() s.clear() assert s.members().length() == 0 cmds.undo() assert s.members().length() == sellist.length() # USING SETMEMBERS ######################## members = self._getMemberList() # replace s.setMembers( members[0:2], 0 ) assert s.members().length() == 2 # add s.setMembers( members[-2:-1], 1 ) assert s.members().length() == 3 # remove s.setMembers( members[0:2], 2 ) assert s.members().length() == 1 cmds.undo() assert s.members().length() == 3 # TEST SET PROTOCOLS #################### assert len(s) == 3 assert [ m for m in s ] == s.members().mtoList() assert iter(s).next() in s
def test_undo_stack(self): mrvmaya.Scene.new(force=1) ur = undo.UndoRecorder() # works inside of another undo level as well p = Node("persp") t = Node("top") # =================== undo.startUndo() p.t.mconnectTo(t.t) ######################## # startRecording needs to come first self.failUnlessRaises(AssertionError, ur.stopRecording) ur.startRecording() ur.startRecording() # doesnt matter p.r.mconnectTo(t.r) # second instance will fail ur2 = undo.UndoRecorder() self.failUnlessRaises(AssertionError, ur2.startRecording) self.failUnlessRaises(AssertionError, ur2.stopRecording) ur.stopRecording() ur.stopRecording() # doesnt matter ######################## assert p.r.misConnectedTo(t.r) assert p.t.misConnectedTo(t.t) ur.undo() assert not p.r.misConnectedTo(t.r) assert p.t.misConnectedTo(t.t) ur.redo() assert p.r.misConnectedTo(t.r) ur.undo() assert not p.r.misConnectedTo(t.r) undo.endUndo() # =================== assert p.t.misConnectedTo(t.t) cmds.undo() assert not p.t.misConnectedTo(t.t) cmds.redo() assert p.t.misConnectedTo(t.t) # we should be able to selectively redo it, even after messing with the queue ur.redo() assert p.r.misConnectedTo(t.r) cmds.undo() assert not p.t.misConnectedTo(t.t) # TEST UNDO GETS ENABLED try: cmds.undoInfo(swf=0) ur = undo.UndoRecorder() ur.startRecording() assert cmds.undoInfo(q=1, swf=1) p.s.mconnectTo(t.s) ur.stopRecording() assert not cmds.undoInfo(q=1, swf=1) assert p.s.misConnectedTo(t.s) ur.undo() assert not p.s.misConnectedTo(t.s) ur.redo() assert p.s.misConnectedTo(t.s) finally: cmds.undoInfo(swf=1) # END assure it gets turned back on # TEST UNDO QUEUE INTEGRATION # if we never called startRecording, it will not do anything ur = undo.UndoRecorder() p.tx.mconnectTo(t.tx) del (ur) assert p.tx.misConnectedTo(t.tx) cmds.undo() assert not p.tx.misConnectedTo(t.tx) cmds.redo() assert p.tx.misConnectedTo(t.tx) # If we recorded something, it will be part of the undo queue if # undo was not called ur = undo.UndoRecorder() ur.startRecording() p.ty.mconnectTo(t.ty) ur.stopRecording() assert p.ty.misConnectedTo(t.ty) cmds.undo() assert not p.ty.misConnectedTo(t.ty) cmds.redo() assert p.ty.misConnectedTo(t.ty)
def test_createNodes( self ): runs = [ 100,2500 ] all_elapsed = [] numObjs = len( cmds.ls() ) for numNodes in runs: nslist = genNestedNamesList( numNodes / 100, (0,3), genRandomNames(10,(3,8)),":" ) nodenames = genNodeNames( numNodes, (1,5),(3,8),nslist ) st = time.time( ) undoobj = undo.StartUndo( ) for nodename in nodenames: try: # it can happen that he creates dg and dag nodes with the same name self._createNodeFromName( nodename ) except NameError: pass # END for each node del( undoobj ) # good if we raise runtime errors ( shouldnt happend ) elapsed = time.time() - st all_elapsed.append( elapsed ) print >>sys.stderr, "Created %i nodes in %f s ( %f / s )" % ( numNodes, elapsed, numNodes / elapsed ) # UNDO OPERATION st = time.time() cmds.undo() elapsed = time.time() - st print >>sys.stderr, "Undone Operation in %f s" % elapsed # END for each run # assure the scene is the same as we undo everything assert len( cmds.ls() ) == numObjs # TEST MAYA NODE CREATION RATE ################################# # redo last operation to get lots of nodes cmds.redo( ) nodenames = cmds.ls( l=1 ) nodes = list() st = time.time( ) for name in nodenames: nodes.append(Node(name)) elapsed = time.time() - st print >>sys.stderr, "Created %i WRAPPED Nodes ( from STRING ) in %f s ( %f / s )" % ( len( nodenames ), elapsed, len( nodenames ) / elapsed ) # NodeFromStr st = time.time( ) tmplist = list() for name in nodenames: tmplist.append(NodeFromStr(name)) elapsed = time.time() - st print >>sys.stderr, "Created %i WRAPPED Nodes ( from STRING using NodeFromStr ) in %f s ( %f / s )" % ( len( nodenames ), elapsed, len( nodenames ) / elapsed ) nodes_apiobjects = [ node.apiObject() for node in nodes ] # CREATE MAYA NODES FROM DAGPATHS AND OBJECTS st = time.time( ) tmplist = list() # previously we measured the time it took to append the node as well for apiobj in nodes_apiobjects: tmplist.append(Node(apiobj)) # END for each wrapped node api_elapsed = time.time() - st print >>sys.stderr, "Created %i WRAPPED Nodes ( from APIOBJ ) in %f s ( %f / s ) -> %f %% faster" % ( len( nodenames ), api_elapsed, len( nodenames ) / api_elapsed, (elapsed / api_elapsed) * 100 ) # CREATE MAYA NODES USING THE FAST CONSTRUCTOR st = time.time( ) tmplist = list() # previously we measured the time it took to append the node as well for apiobj in nodes_apiobjects: tmplist.append(NodeFromObj(apiobj)) # END for each wrapped node api_elapsed = time.time() - st print >>sys.stderr, "Created %i WRAPPED Nodes ( from APIOBJ using NodeFromObj) in %f s ( %f / s ) -> %f %% faster" % ( len( nodenames ), api_elapsed, len( nodenames ) / api_elapsed, (elapsed / api_elapsed) * 100 ) # RENAME PERFORMANCE st = time.time() for node in tmplist: node.rename(node.basename()[:-1]) # END for each node elapsed = time.time() - st ltl = len(tmplist) print >>sys.stderr, "Renamed %i WRAPPED Nodes in %f s ( %f / s )" % ( ltl, elapsed, ltl / elapsed )
def test_general( self ): mrvmaya.Scene.new( force = True ) s1 = nt.createNode( "storage1", "storageNode" ) s2 = nt.createNode( "storage2", "storageNode" ) s1msg = s1.storagePlug( "s1", plugType = 1, autoCreate = True ) s2msg = s1.storagePlug( "s1", plugType = 1, autoCreate = True ) # connect the message attributes respectively def measurePlugConnection( msg, func, callNumberList ): """Call func of named operation msg number of times as in callNumberList""" for numcons in callNumberList: undoObj = undo.StartUndo() starttime = time.time() for i in xrange( numcons ): func( i ) elapsed = time.time( ) - starttime print >> sys.stderr, "%i %s in %f s ( %f / s )" % ( numcons, msg, elapsed, numcons / elapsed ) del( undoObj ) starttime = time.time() cmds.undo() undoelapsed = time.time() - starttime starttime = time.time() cmds.redo() redoelapsed = time.time() - starttime print >> sys.stderr, "UNDO / REDO Time = %f / %f ( %f * faster than initial creation )" % ( undoelapsed, redoelapsed, elapsed / max( redoelapsed, 0.001) ) # END for each amount of plugs to connct # END measure function conlist = [ 250, 1000, 2000, 4000 ] # CONNECT MULTI PLUGS ###################### multifunc = lambda i: s1msg.elementByLogicalIndex( i ).mconnectTo(s2msg.elementByLogicalIndex( i )) measurePlugConnection( "MULTI PLUG Connected", multifunc, conlist ) # CONNECT SINGLE PLUGS persp = nt.Node( "persp" ) front = nt.Node( "front" ) def singleFunc( i ): persp.message.mconnectTo(front.isHistoricallyInteresting) persp.message.mdisconnectFrom(front.isHistoricallyInteresting) measurePlugConnection( "SINGLE PLUGS Connected", singleFunc, conlist ) # SET AND GET ############## persp = nt.Node( "persp" ) perspshape = persp[0] plugs = [ persp.tx, perspshape.fl ] num_iterations = 2500 iterations = range( num_iterations ) undoObj = undo.StartUndo() starttime = time.time() for plug in plugs: for i in iterations: value = plug.asFloat() plug.msetFloat( value ) # END get set plug # END for each plug elapsed = time.time() - starttime del( undoObj ) total_count = num_iterations * len( plugs ) print >> sys.stderr, "Get/Set %i plugs %i times ( total = %i ) in %f ( %g / s )" % ( len( plugs ), num_iterations, total_count, elapsed, total_count / elapsed ) starttime = time.time() cmds.undo() undoelapsed = time.time() - starttime starttime = time.time() cmds.redo() redoelapsed = time.time() - starttime print >> sys.stderr, "UNDO / REDO Time = %f / %f ( %f * faster than initial set/get )" % ( undoelapsed, redoelapsed, elapsed / max( redoelapsed, 0.001) )