diff --git a/lib/mayaUsd/ufe/UsdAttributes.cpp b/lib/mayaUsd/ufe/UsdAttributes.cpp index 364fbf5663..3989b7d21b 100644 --- a/lib/mayaUsd/ufe/UsdAttributes.cpp +++ b/lib/mayaUsd/ufe/UsdAttributes.cpp @@ -455,6 +455,24 @@ bool UsdAttributes::canRemoveAttribute(const UsdSceneItem::Ptr& item, const std: } return false; } +static void removeConnections(const PXR_NS::UsdPrim& prim, const PXR_NS::SdfPath& srcPropertyPath) +{ + // Remove the connections with source srcPropertyPath. + for (const auto& node : prim.GetChildren()) { + for (const auto& attribute : node.GetAttributes()) { + PXR_NS::UsdAttribute attr = attribute.As(); + PXR_NS::SdfPathVector sources; + attr.GetConnections(&sources); + + for (size_t i = 0; i < sources.size(); ++i) { + if (sources[i] == srcPropertyPath) { + attr.RemoveConnection(srcPropertyPath); + break; + } + } + } + } +} bool UsdAttributes::doRemoveAttribute(const UsdSceneItem::Ptr& item, const std::string& name) { @@ -470,16 +488,23 @@ bool UsdAttributes::doRemoveAttribute(const UsdSceneItem::Ptr& item, const std:: PXR_NS::UsdShadeNodeGraph ngPrim(prim); PXR_NS::UsdShadeConnectableAPI connectApi(prim); if (ngPrim && connectApi) { - auto baseNameAndType = PXR_NS::UsdShadeUtils::GetBaseNameAndType(nameAsToken); + const SdfPath kPrimPath = prim.GetPath(); + const SdfPath kPropertyPath = kPrimPath.AppendProperty(attribute.GetName()); + auto baseNameAndType = PXR_NS::UsdShadeUtils::GetBaseNameAndType(nameAsToken); if (baseNameAndType.second == PXR_NS::UsdShadeAttributeType::Output) { auto output = connectApi.GetOutput(baseNameAndType.first); if (output) { + auto parent = prim.GetParent(); + if (parent) { + removeConnections(parent, kPropertyPath); + } connectApi.ClearSources(output); return prim.RemoveProperty(nameAsToken); } } else if (baseNameAndType.second == PXR_NS::UsdShadeAttributeType::Input) { auto input = connectApi.GetInput(baseNameAndType.first); if (input) { + removeConnections(prim, kPropertyPath); connectApi.ClearSources(input); return prim.RemoveProperty(nameAsToken); } @@ -560,7 +585,6 @@ Ufe::Attribute::Ptr UsdAttributes::doRenameAttribute( auto propertyHandle = editTarget.GetPropertySpecForScenePath(kPropertyPath); auto baseNameAndType = PXR_NS::UsdShadeUtils::GetBaseNameAndType(nameAsToken); - prim.GetAttributes(); PXR_NS::UsdShadeNodeGraph ngPrim(prim); if (!propertyHandle) { diff --git a/test/lib/ufe/testAttributes.py b/test/lib/ufe/testAttributes.py index a3e3c10769..746d7fb213 100644 --- a/test/lib/ufe/testAttributes.py +++ b/test/lib/ufe/testAttributes.py @@ -183,6 +183,64 @@ def testAddRemoveAttribute(self): with self.assertRaisesRegex(KeyError, "Attribute 'MyAttribute' does not exist") as cm: attr = ball35Attrs.attribute("MyAttribute") + @unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '4024', 'Test requires remove attribute and its connections feature only available on Ufe 0.4.24 and later') + def testRemoveCompoundAttribute(self): + '''Test removing compound attributes''' + + # Load a scene with a compound. + + testFile = testUtils.getTestScene("MaterialX", "MayaSurfaces.usda") + shapeNode,shapeStage = mayaUtils.createProxyFromFile(testFile) + ufeItem = ufeUtils.createUfeSceneItem(shapeNode, + "/pCube2/Looks/standardSurface2SG/MayaNG_standardSurface2SG") + self.assertIsNotNone(ufeItem) + + # Then create the attributes interface for that item. + compoundAttrs = ufe.Attributes.attributes(ufeItem) + self.assertIsNotNone(compoundAttrs) + + # Remove an output compound attribute. + cmd = compoundAttrs.removeAttributeCmd("outputs:baseColor") + self.assertIsNotNone(cmd) + + ufeCmd.execute(cmd) + + self.assertNotIn("outputs:baseColor", compoundAttrs.attributeNames) + + # Test we removed the connection. + + ufeItemStandardSurface2 = ufeUtils.createUfeSceneItem(shapeNode, + "/pCube2/Looks/standardSurface2SG/standardSurface2") + self.assertIsNotNone(ufeItemStandardSurface2) + + connectionHandler = ufe.RunTimeMgr.instance().connectionHandler(ufeItemStandardSurface2.runTimeId()) + self.assertIsNotNone(connectionHandler) + connections = connectionHandler.sourceConnections(ufeItemStandardSurface2) + self.assertIsNotNone(connectionHandler) + conns = connections.allConnections() + self.assertEqual(len(conns), 0) + + # Remove an input compound attribute. + cmd = compoundAttrs.removeAttributeCmd("inputs:file2:varnameStr") + self.assertIsNotNone(cmd) + + ufeCmd.execute(cmd) + + self.assertNotIn("inputs:file2:varnameStr", compoundAttrs.attributeNames) + + # Test we removed the connection. + + ufeItemTexture = ufeUtils.createUfeSceneItem(shapeNode, + "/pCube2/Looks/standardSurface2SG/MayaNG_standardSurface2SG/place2dTexture2") + self.assertIsNotNone(ufeItemTexture) + + connectionHandler = ufe.RunTimeMgr.instance().connectionHandler(ufeItemTexture.runTimeId()) + self.assertIsNotNone(connectionHandler) + connections = connectionHandler.sourceConnections(ufeItemTexture) + self.assertIsNotNone(connectionHandler) + conns = connections.allConnections() + self.assertEqual(len(conns), 0) + @unittest.skipIf(os.getenv('UFE_PREVIEW_VERSION_NUM', '0000') < '4024', 'Test for UFE preview version 0.4.24 and later') def testUniqueNameAttribute(self): '''Test unique name attribute'''