From 65b934a787cf420a3720bac092c8ff194629e75b Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Tue, 22 Oct 2024 13:10:17 -0400 Subject: [PATCH] EMSUSD-1521 modify AE section order - Accumulate all schemas and their attributes before creating any UI for them. - Re-ordered schemas according to the design. - Renamed Xformable to Transforms. - Made all sections be collapsed by default except the prim type section, Light and Light type - Removed the "Applied Schemas" section. - Add a unit test and fix some tests that assumed sections would be expanded. --- .../resources/ae/usdschemabase/ae_template.py | 147 ++++++++----- .../scripts/mayaUsdLibRegisterStrings.py | 2 +- test/lib/testAttributeEditorTemplate.py | 207 ++++++++++++++---- 3 files changed, 266 insertions(+), 90 deletions(-) diff --git a/lib/mayaUsd/resources/ae/usdschemabase/ae_template.py b/lib/mayaUsd/resources/ae/usdschemabase/ae_template.py index e4c819662f..d331570ad9 100644 --- a/lib/mayaUsd/resources/ae/usdschemabase/ae_template.py +++ b/lib/mayaUsd/resources/ae/usdschemabase/ae_template.py @@ -219,7 +219,7 @@ def __init__(self, ufeSceneItem): # Get the UFE Attributes interface for this scene item. self.attrS = ufe.Attributes.attributes(self.item) - self.addedAttrs = [] + self.addedAttrs = set() self.suppressedAttrs = [] self.hasConnectionObserver = False @@ -232,9 +232,50 @@ def __init__(self, ufeSceneItem): if cmds.optionVar(exists='attrEditorIsLongName'): self.useNiceName = (cmds.optionVar(q='attrEditorIsLongName') ==1) + self.addedMaterialSection = False + + self.suppressArrayAttribute() + + # Build the list of schemas with their associated attributes. + schemasAttributes = {} + schemasAttributes.update(self.findAppliedSchemas()) + schemasAttributes.update(self.findClassSchemas()) + + # Order schema sections according to designer's choices. + availableSchemas = list(schemasAttributes.keys()) + + desiredFirstSchemas = [ + 'LightAPI', + '.* Light', + 'lightLinkCollectionAPI', + 'shadowLinkCollectionAPI', + ] + + desiredLastSchemas = [ + ] + + def addSchemas(desiredOrder, availableSchemas): + orderedSchemas = [] + for order in desiredOrder: + if '*' in order: + for avail in availableSchemas[:]: + if re.match(order, avail): + availableSchemas.remove(avail) + orderedSchemas.append(avail) + elif order in availableSchemas: + availableSchemas.remove(order) + orderedSchemas.append(order) + return orderedSchemas + + firstSchemas = addSchemas(desiredFirstSchemas, availableSchemas) + lastSchemas = addSchemas(desiredLastSchemas, availableSchemas) + + orderedSchemas = firstSchemas + availableSchemas + lastSchemas + + # Build the section UI. cmds.editorTemplate(beginScrollLayout=True) - self.buildUI() - self.createAppliedSchemasSection() + self.createSchemasSections(orderedSchemas, schemasAttributes) + self.createSpecialSections() self.createCustomExtraAttrs() self.createMetadataSection() cmds.editorTemplate(endScrollLayout=True) @@ -268,7 +309,7 @@ def addControls(self, attrNames): except Exception as ex: # Do not let one custom control failure affect others. print('Failed to create control %s: %s' % (attrName, ex)) - self.addedAttrs.append(attrName) + self.addedAttrs.add(attrName) def suppress(self, control): cmds.editorTemplate(suppress=control) @@ -302,6 +343,7 @@ def sectionNameFromSchema(self, schemaTypeName): ('UsdAbc', ''), ('UsdGeomGprim', 'GeometricPrim'), ('UsdGeomImageable', mel.eval('uiRes(\"m_AEdagNodeTemplate.kDisplay\");')), + ('UsdGeomXformable', getMayaUsdLibString('kTransforms')), ('UsdGeom', ''), ('UsdHydra', ''), ('UsdImagingGL', ''), @@ -333,7 +375,7 @@ def sectionNameFromSchema(self, schemaTypeName): def addShaderLayout(self, group): """recursively create the full attribute layout section""" - with ufeAeTemplate.Layout(self, group.name): + with ufeAeTemplate.Layout(self, group.name, collapse=True): for item in group.items: if isinstance(item, AEShaderLayout.Group): self.addShaderLayout(item) @@ -382,7 +424,7 @@ def createTransformAttributesSection(self, sectionName, attrsToAdd): xformOpOrderNames.append(UsdGeom.Tokens.xformOpOrder) # Don't use createSection because we want a sub-sections. - with ufeAeTemplate.Layout(self, sectionName): + with ufeAeTemplate.Layout(self, sectionName, collapse=True): attrsToAdd.remove(UsdGeom.Tokens.xformOpOrder) self.addControls(xformOpOrderNames) @@ -427,10 +469,7 @@ def createCustomExtraAttrs(self): sectionName = mel.eval("uiRes(\"s_TPStemplateStrings.rExtraAttributes\");") self.createSection(sectionName, extraAttrs, True) - def createAppliedSchemasSection(self): - usdVer = Usd.GetVersion() - showAppliedSchemasSection = False - + def findAppliedSchemas(self): # loop on all applied schemas and store all those # schema into a dictionary with the attributes. # Storing the schema into a dictionary allow us to @@ -449,7 +488,8 @@ def createAppliedSchemasSection(self): # "Collection Light Link Include Root" and a comparison with the schema nice name # "Collection Light Link" will allow of to trim the nice name to "Include Root" # - schemaAttrsDict = {} + schemasAttributes = {} + usdVer = Usd.GetVersion() appliedSchemas = self.prim.GetAppliedSchemas() for schema in appliedSchemas: typeAndInstance = Usd.SchemaRegistry().GetTypeNameAndInstance(schema) @@ -471,48 +511,68 @@ def createAppliedSchemasSection(self): prefix = namespace + ":" + instanceName + ":" attrList = [prefix + i for i in attrList] - schemaAttrsDict[instanceName + typeName] = attrList + typeName = instanceName + typeName else: attrList = schemaType.pythonClass.GetSchemaAttributeNames(False) - schemaAttrsDict[typeName] = attrList - - # The "Applied Schemas" will be only visible if at least - # one applied Schemas has attribute. - if not showAppliedSchemasSection: - for attr in attrList: - if self.attrS.hasAttribute(attr): - showAppliedSchemasSection = True - break - # Create the "Applied Schemas" section - # with all the applied schemas - if showAppliedSchemasSection: - with ufeAeTemplate.Layout(self, getMayaUsdLibString('kLabelAppliedSchemas'), collapse=True): - for typeName, attrs in schemaAttrsDict.items(): - typeName = self.sectionNameFromSchema(typeName) - self.createSection(typeName, attrs, False) + schemasAttributes[typeName] = attrList + return schemasAttributes - def buildUI(self): + def findClassSchemas(self): + schemasAttributes = {} + usdSch = Usd.SchemaRegistry() - self.suppressArrayAttribute() - - # Track if we already added a connection observer. - self.hasConnectionObserver = False - - # Material has NodeGraph as base. We want to process once for both schema types: - hasProcessedMaterial = False + specialSchemas = { + 'UsdShadeShader', 'UsdShadeNodeGraph', 'UsdShadeMaterial', 'UsdGeomXformable', 'UsdGeomImageable' } + # We use UFE for the ancestor node types since it caches the + # results by node type. + for schemaType in self.item.ancestorNodeTypes(): + schemaType = usdSch.GetTypeFromName(schemaType) + schemaTypeName = schemaType.typeName + sectionName = self.sectionNameFromSchema(schemaTypeName) + if schemaType.pythonClass: + attrsToAdd = schemaType.pythonClass.GetSchemaAttributeNames(False) + if schemaTypeName in specialSchemas: + continue + schemasAttributes[sectionName] = attrsToAdd + + return schemasAttributes + + def createSchemasSections(self, schemasOrder, schemasAttributes): # We want material to be either after the mesh section of the Xformable section, # whichever comes first, so that it is not too far down in the AE. - self.addedMaterialSection = False primTypeName = self.sectionNameFromSchema(self.prim.GetTypeName()) def addMatSection(): if not self.addedMaterialSection: self.addedMaterialSection = True self.createMaterialAttributeSection() + def isSectionOpen(sectionName): + if sectionName == primTypeName: + return True + lowerName = sectionName.lower() + return 'light' in lowerName and 'link' not in lowerName + + for typeName in schemasOrder: + attrs = schemasAttributes[typeName] + sectionName = self.sectionNameFromSchema(typeName) + collapse = not isSectionOpen(sectionName) + self.createSection(sectionName, attrs, collapse) + if sectionName == primTypeName: + addMatSection() + + # In case there was neither a Mesh nor Xformable section, add material section now. + addMatSection() + + def createSpecialSections(self): + usdSch = Usd.SchemaRegistry() + + # Material has NodeGraph as base. We want to process once for both schema types: + hasProcessedMaterial = False + # We use UFE for the ancestor node types since it caches the # results by node type. for schemaType in self.item.ancestorNodeTypes(): @@ -535,17 +595,7 @@ def addMatSection(): self.createTransformAttributesSection(sectionName, attrsToAdd) elif schemaTypeName == 'UsdGeomImageable': self.createDisplaySection(sectionName, attrsToAdd) - else: - sectionsToCollapse = ['Curves', 'Point Based', 'Geometric Prim', 'Boundable', - 'Imageable', 'Field Asset', 'Light'] - collapse = sectionName in sectionsToCollapse - self.createSection(sectionName, attrsToAdd, collapse) - if sectionName == primTypeName: - addMatSection() - - # In case there was neither a Mesh nor Xformable section, add material section now. - addMatSection() def createMaterialAttributeSection(self): if not UsdShade.MaterialBindingAPI.CanApply(self.prim): @@ -555,8 +605,7 @@ def createMaterialAttributeSection(self): if not mat: return layoutName = getMayaUsdLibString('kLabelMaterial') - collapse = False - with ufeAeTemplate.Layout(self, layoutName, collapse): + with ufeAeTemplate.Layout(self, layoutName, collapse=True): createdControl = MaterialCustomControl(self.item, self.prim, self.useNiceName) self.defineCustom(createdControl) diff --git a/lib/mayaUsd/resources/scripts/mayaUsdLibRegisterStrings.py b/lib/mayaUsd/resources/scripts/mayaUsdLibRegisterStrings.py index 322e79a8fd..74fc9528d9 100644 --- a/lib/mayaUsd/resources/scripts/mayaUsdLibRegisterStrings.py +++ b/lib/mayaUsd/resources/scripts/mayaUsdLibRegisterStrings.py @@ -40,7 +40,6 @@ def mayaUsdLibUnregisterStrings(): 'kMenuPrintValue': 'Print to Script Editor', 'kLabelUnusedTransformAttrs': 'Unused', 'kLabelMetadata': 'Metadata', - 'kLabelAppliedSchemas': 'Applied Schemas', 'kOpenImage': 'Open', 'kLabelMaterial': 'Material', 'kLabelAssignedMaterial': 'Assigned Material', @@ -57,6 +56,7 @@ def mayaUsdLibUnregisterStrings(): 'kLabelMaterialNewTab': 'New Tab...', 'kUseOutlinerColorAnn': 'Apply the Outliner color to the display of the prim name in the Outliner.', 'kOutlinerColorAnn': 'The color of the text displayed in the Outliner.', + 'kTransforms': 'Transforms', # mayaUsdAddMayaReference.py 'kErrorGroupPrimExists': 'Group prim "^1s" already exists under "^2s". Choose prim name other than "^1s" to proceed.', diff --git a/test/lib/testAttributeEditorTemplate.py b/test/lib/testAttributeEditorTemplate.py index 9dfa72df90..78f3a62055 100644 --- a/test/lib/testAttributeEditorTemplate.py +++ b/test/lib/testAttributeEditorTemplate.py @@ -19,16 +19,15 @@ import unittest import mayaUtils -import ufeUtils import fixturesUtils import testUtils -import mayaUsd.lib - from maya import cmds +from maya import mel from maya import standalone from mayaUsdLibRegisterStrings import getMayaUsdLibString +import mayaUsd_createStageWithNewLayer from pxr import Usd @@ -66,6 +65,68 @@ def searchForMayaControl(self, controlOrLayout, mayaControlCmd, labelToFind): if mayaControlCmd(child, q=True, label=True) == labelToFind: return child return None + + def findAllFrameLayoutLabels(self, startLayout): + ''' + Find all child frame layout and return a list of their labels. + ''' + found = [] + + if not startLayout: + return found + + childrenOfLayout = cmds.layout(startLayout, q=True, ca=True) + if not childrenOfLayout: + return found + + for child in childrenOfLayout: + child = startLayout + '|' + child + if cmds.frameLayout(child, exists=True): + found.append(cmds.frameLayout(child, q=True, label=True)) + elif cmds.layout(child, exists=True): + found += self.findAllFrameLayoutLabels(child) + + return found + + def findExpandedFrameLayout(self, startLayout, labelToFind, primPath): + ''' + Find and expand the given frame layout and return the new updated layout. + We unfortunately need to explicitly update the AE for this to work. + ''' + frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, labelToFind) + self.assertIsNotNone(frameLayout, 'Could not find "%s" frameLayout' % labelToFind) + self.expandFrameLayout(frameLayout, primPath) + return self.searchForMayaControl(startLayout, cmds.frameLayout, labelToFind) + + def expandFrameLayout(self, frameLayout, primPath): + ''' + Expand the given frame layout and refresh the AE. + You will need to find the layout again after that since it might have changed name. + ''' + cmds.frameLayout(frameLayout, edit=True, collapse=False) + mel.eval('''updateAE "%s"''' % primPath) + + def findAllNonLayout(self, startLayout): + ''' + Find all child non-layout controls and return them as a list. + ''' + found = [] + + if not startLayout: + return found + + childrenOfLayout = cmds.layout(startLayout, q=True, ca=True) + if not childrenOfLayout: + return found + + for child in childrenOfLayout: + child = startLayout + '|' + child + if cmds.layout(child, exists=True): + found += self.findAllNonLayout(child) + else: + found.append(child) + + return found def attrEdFormLayoutName(self, obj): # Ufe runtime name (USD) was added to formLayout name in 2022.2. @@ -81,7 +142,6 @@ def testAETemplate(self): # Create a simple USD scene with a single prim. cmds.file(new=True, force=True) - import mayaUsd_createStageWithNewLayer proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path(proxyShape) proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) @@ -90,7 +150,8 @@ def testAETemplate(self): # select the prim from 'Add New Prim', so always select it here. proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Capsule']) - cmds.select(proxyShape + ",/Capsule1") + fullPrimPath = proxyShape + ",/Capsule1" + cmds.select(fullPrimPath) # Enable the display of Array Attributes. cmds.optionVar(intValue=('mayaUSD_AEShowArrayAttributes', 1)) @@ -112,7 +173,7 @@ def testAETemplate(self): # We should have a frameLayout called 'Capsule' in the template. # If there is a scripting error in the template, this layout will be missing. - frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Capsule') + frameLayout = self.findExpandedFrameLayout(startLayout, 'Capsule', fullPrimPath) self.assertIsNotNone(frameLayout, 'Could not find Capsule frameLayout') # We should also have float slider controls for 'Height' & 'Radius'. @@ -134,7 +195,7 @@ def testAETemplate(self): # We should have a frameLayout called 'Metadata' in the template. # If there is a scripting error in the template, this layout will be missing. - frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, getMayaUsdLibString('kLabelMetadata')) + frameLayout = self.findExpandedFrameLayout(startLayout, getMayaUsdLibString('kLabelMetadata'), fullPrimPath) self.assertIsNotNone(frameLayout, 'Could not find Metadata frameLayout') # Create a SphereLight via contextOps menu. @@ -150,16 +211,6 @@ def testAETemplate(self): startLayout = cmds.formLayout(lightFormLayout, query=True, fullPathName=True) self.assertIsNotNone(startLayout, 'Could not get full path for SphereLight formLayout') - # -------------------------------------------------------------------------------- - # Test the 'createAppliedSchemasSection' method of template. - # Requires USD 0.21.2 - # -------------------------------------------------------------------------------- - if Usd.GetVersion() >= (0, 21, 2): - # We should have a frameLayout called 'Applied Schemas' in the template. - # If there is a scripting error in the template, this layout will be missing. - frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, getMayaUsdLibString('kLabelAppliedSchemas')) - self.assertIsNotNone(frameLayout, 'Could not find Applied Schemas frameLayout') - # -------------------------------------------------------------------------------- # Test the 'createCustomExtraAttrs' method of template. # -------------------------------------------------------------------------------- @@ -187,7 +238,6 @@ def testCreateDisplaySection(self): # Create a simple USD scene with a single prim. cmds.file(new=True, force=True) - import mayaUsd_createStageWithNewLayer proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() proxyShapePath = ufe.PathString.path(proxyShape) proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) @@ -196,7 +246,8 @@ def testCreateDisplaySection(self): # select the prim from 'Add New Prim', so always select it here. proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) proxyShapeContextOps.doOp(['Add New Prim', 'Capsule']) - cmds.select(proxyShape + ",/Capsule1") + fullPrimPath = proxyShape + ",/Capsule1" + cmds.select(fullPrimPath) # Make sure the AE is visible. import maya.mel @@ -206,7 +257,8 @@ def findDisplayLayout(): capsuleFormLayout = self.attrEdFormLayoutName('Capsule') startLayout = cmds.formLayout(capsuleFormLayout, query=True, fullPathName=True) kDisplay = maya.mel.eval('uiRes(\"m_AEdagNodeTemplate.kDisplay\");') - return self.searchForMayaControl(startLayout, cmds.frameLayout, kDisplay) + return self.findExpandedFrameLayout(startLayout, kDisplay, fullPrimPath) + # -------------------------------------------------------------------------------- # Test the 'createDisplaySection' method of template. @@ -217,15 +269,6 @@ def findDisplayLayout(): # Maya 2022 doesn't remember and restore the expand/collapse state, # so skip this part of the test. if mayaUtils.mayaMajorVersion() > 2022: - # Expand the Display layout then update the AE again. - # We must do this because when we expand it doesn't happen right away. - # But by expanding it when we updateAE the capsule the display section - # expansion is remembered and auto-expanded. - cmds.frameLayout(displayLayout, edit=True, collapse=False) - maya.mel.eval('updateAE "|stage1|stageShape1,/Capsule1"') - displayLayout = findDisplayLayout() - self.assertIsNotNone(displayLayout, 'Could not find Display frameLayout') - # We should have 'Visibility' optionMenuGrp (which is a label (text) and optionMenu). visControl = self.searchForMayaControl(displayLayout, cmds.text, 'Visibility') self.assertIsNotNone(visControl) @@ -242,7 +285,8 @@ def testAECustomMaterialControl(self): shapeNode,shapeStage = mayaUtils.createProxyFromFile(testFile) # Select this prim which has a custom material control attribute. - cmds.select('|stage|stageShape,/pCube1', r=True) + fullPrimPath = shapeNode + ',/pCube1' + cmds.select(fullPrimPath, r=True) # Make sure the AE is visible. import maya.mel @@ -257,7 +301,7 @@ def testAECustomMaterialControl(self): # In the AE there is a formLayout for each USD prim type. We start # by making sure we can find the one for the mesh. - materialFormLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Material') + materialFormLayout = self.findExpandedFrameLayout(startLayout, 'Material', fullPrimPath) self.assertIsNotNone(materialFormLayout, 'Could not find "Material" frameLayout') # We can now search for the control for the meterial. @@ -273,7 +317,8 @@ def testAECustomImageControl(self): shapeNode,shapeStage = mayaUtils.createProxyFromFile(testFile) # Select this prim which has a custom image control attribute. - cmds.select('|stage|stageShape,/TypeSampler/MaterialX/D_filename', r=True) + fullPrimPath = shapeNode + ',/TypeSampler/MaterialX/D_filename' + cmds.select(fullPrimPath, r=True) # Make sure the AE is visible. import maya.mel @@ -289,7 +334,7 @@ def testAECustomImageControl(self): if mayaUtils.mayaMajorVersion() > 2024: # We should have a frameLayout called 'Shader: Dot' in the template. # If there is a scripting error in the template, this layout will be missing. - frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Shader: Dot') + frameLayout = self.findExpandedFrameLayout(startLayout, 'Shader: Dot', fullPrimPath) self.assertIsNotNone(frameLayout, 'Could not find "Shader: Dot" frameLayout') # We should also have custom image control for 'In'. @@ -309,7 +354,8 @@ def testAECustomEnumControl(self): shapeNode,shapeStage = mayaUtils.createProxyFromFile(testFile) # Select this prim which has a custom image control attribute. - cmds.select('|stage|stageShape,/Material1/gltf_pbr1', r=True) + fullPrimPath = shapeNode + ',/Material1/gltf_pbr1' + cmds.select(fullPrimPath, r=True) # Make sure the AE is visible. import maya.mel @@ -321,12 +367,35 @@ def testAECustomEnumControl(self): self.assertTrue(cmds.formLayout(shaderFormLayout, exists=True)) startLayout = cmds.formLayout(shaderFormLayout, query=True, fullPathName=True) self.assertIsNotNone(startLayout, 'Could not get full path for Shader formLayout') - + + frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Shader: glTF PBR') + self.assertIsNotNone(frameLayout, 'Could not find "Shader: glTF PBR" frameLayout') + + self.expandFrameLayout(frameLayout, fullPrimPath) + + shaderFormLayout = self.attrEdFormLayoutName('Shader') + self.assertTrue(cmds.formLayout(shaderFormLayout, exists=True)) + startLayout = cmds.formLayout(shaderFormLayout, query=True, fullPathName=True) + self.assertIsNotNone(startLayout, 'Could not get full path for Shader formLayout') + frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Shader: glTF PBR') + self.assertIsNotNone(frameLayout, 'Could not find "Shader: glTF PBR" frameLayout') + # We should have a frameLayout called 'Alpha' in the template. # If there is a scripting error in the template, this layout will be missing. frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Alpha') self.assertIsNotNone(frameLayout, 'Could not find "Alpha" frameLayout') - + + self.expandFrameLayout(frameLayout, fullPrimPath) + + shaderFormLayout = self.attrEdFormLayoutName('Shader') + self.assertTrue(cmds.formLayout(shaderFormLayout, exists=True)) + startLayout = cmds.formLayout(shaderFormLayout, query=True, fullPathName=True) + self.assertIsNotNone(startLayout, 'Could not get full path for Shader formLayout') + frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Shader: glTF PBR') + self.assertIsNotNone(frameLayout, 'Could not find "Shader: glTF PBR" frameLayout') + frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Alpha') + self.assertIsNotNone(frameLayout, 'Could not find "Alpha" frameLayout') + # We should also have custom enum control for 'Inputs Alpha Mode'. InputsAlphaModeControl = self.searchForMayaControl(frameLayout, cmds.text, 'Alpha Mode') self.assertIsNotNone(InputsAlphaModeControl, 'Could not find gltf_pbr1 "Alpha Mode" control') @@ -339,7 +408,8 @@ def testAEConnectionsCustomControl(self): testPath,shapeStage = mayaUtils.createProxyFromFile(testFile) # Select this prim which has an attribute with a connection. - cmds.select('|stage|stageShape,/Material1/fractal3d1', r=True) + fullPrimPath = testPath + ',/Material1/fractal3d1' + cmds.select(fullPrimPath, r=True) # Make sure the AE is visible. import maya.mel @@ -355,7 +425,7 @@ def testAEConnectionsCustomControl(self): if mayaUtils.mayaMajorVersion() > 2024: # We should have a frameLayout called 'Shader: 3D Fractal Noise' in the template. # If there is a scripting error in the template, this layout will be missing. - frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Shader: 3D Fractal Noise') + frameLayout = self.findExpandedFrameLayout(startLayout, 'Shader: 3D Fractal Noise', fullPrimPath) self.assertIsNotNone(frameLayout, 'Could not find "Shader: 3D Fractal Noise" frameLayout') # We should also have an attribute called 'Amplitude' which has a connection. @@ -369,7 +439,8 @@ def testAEConnectionsCustomControlWithComponents(self): testPath,shapeStage = mayaUtils.createProxyFromFile(testFile) # Select this prim which has an attribute with a connection. - cmds.select('|stage|stageShape,/Material1/component_add', r=True) + fullPrimPath = testPath + ',/Material1/component_add' + cmds.select(fullPrimPath, r=True) # Make sure the AE is visible. import maya.mel @@ -385,7 +456,7 @@ def testAEConnectionsCustomControlWithComponents(self): if mayaUtils.mayaMajorVersion() > 2024: # We should have a frameLayout called 'Shader: Add' in the template. # If there is a scripting error in the template, this layout will be missing. - frameLayout = self.searchForMayaControl(startLayout, cmds.frameLayout, 'Shader: Add') + frameLayout = self.findExpandedFrameLayout(startLayout, 'Shader: Add', fullPrimPath) self.assertIsNotNone(frameLayout, 'Could not find "Shader: Add" frameLayout') # We should also have an attribute called 'In1' which has component connections. @@ -400,6 +471,62 @@ def testAEConnectionsCustomControlWithComponents(self): for menuItem in cmds.popupMenu(popupMenu, q=True, itemArray=True): self.assertIn(cmds.menuItem(popupMenu + "|" + menuItem, q=True, l=True), expectedLabels) + def testAEForLight(self): + '''Test that the expected sections are created for lights.''' + proxyShape = mayaUsd_createStageWithNewLayer.createStageWithNewLayer() + proxyShapePath = ufe.PathString.path(proxyShape) + proxyShapeItem = ufe.Hierarchy.createItem(proxyShapePath) + + # Create a cylinder light via contextOps menu. Not all versions of Maya automatically + # select the prim from 'Add New Prim', so always select it here. + proxyShapeContextOps = ufe.ContextOps.contextOps(proxyShapeItem) + proxyShapeContextOps.doOp(['Add New Prim', 'All Registered', 'Lighting', 'CylinderLight']) + primName = 'CylinderLight1' + fullPrimPath = proxyShape + ',/%s' % primName + cmds.select(fullPrimPath) + + # Make sure the AE is visible. + import maya.mel + maya.mel.eval('openAEWindow') + + primFormLayout = self.attrEdFormLayoutName('CylinderLight') + self.assertTrue(cmds.formLayout(primFormLayout, exists=True), 'Layout for %s was not found\n' % primName) + + startLayout = cmds.formLayout(primFormLayout, query=True, fullPathName=True) + self.assertIsNotNone(startLayout, 'Could not get full path for %s formLayout' % primName) + + # Augment the maixmum diff size to get better error message when comparing the lists. + self.maxDiff = 2000 + + actualSectionLabels = self.findAllFrameLayoutLabels(startLayout) + + # Note: different version of USD can have different schemas, + # so we only compare the ones we are interested in verifying. + expectedInitialSectionLabels = [ + 'Light ', + 'Cylinder Light', + 'Light Link Collection ', + 'Shadow Link Collection '] + self.assertListEqual( + actualSectionLabels[0:len(expectedInitialSectionLabels)], + expectedInitialSectionLabels) + + # Note: there are no extra attributes in Maya 2022. + if mayaUtils.mayaMajorMinorVersions() >= (2023, 0): + expectedFinalSectionLabels = [ + 'Transforms', + 'Display', + 'Extra Attributes', + 'Metadata'] + else: + expectedFinalSectionLabels = [ + 'Transforms', + 'Display', + 'Metadata'] + self.assertListEqual( + actualSectionLabels[-len(expectedFinalSectionLabels):], + expectedFinalSectionLabels) + if __name__ == '__main__': fixturesUtils.runTests(globals())