From fe33a0ac055add04e4ca93077457474cd384c759 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Fri, 7 Feb 2020 14:24:04 -0800 Subject: [PATCH 1/6] WIP: context ops. --- lib/CMakeLists.txt | 4 ++ lib/ufe/Global.cpp | 4 +- lib/ufe/UsdContextOps.cpp | 111 +++++++++++++++++++++++++++++++ lib/ufe/UsdContextOps.h | 65 ++++++++++++++++++ lib/ufe/UsdContextOpsHandler.cpp | 53 +++++++++++++++ lib/ufe/UsdContextOpsHandler.h | 56 ++++++++++++++++ 6 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 lib/ufe/UsdContextOps.cpp create mode 100644 lib/ufe/UsdContextOps.h create mode 100644 lib/ufe/UsdContextOpsHandler.cpp create mode 100644 lib/ufe/UsdContextOpsHandler.h diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ee2c49c121..d25ec67f45 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -244,6 +244,8 @@ if(UFE_FOUND) ufe/UsdAttribute.cpp ufe/UsdAttributes.cpp ufe/UsdAttributesHandler.cpp + ufe/UsdContextOps.cpp + ufe/UsdContextOpsHandler.cpp ufe/UsdObject3d.cpp ufe/UsdObject3dHandler.cpp ufe/UsdUndoCreateGroupCommand.cpp @@ -388,6 +390,8 @@ if(UFE_FOUND) ufe/UsdAttribute.h ufe/UsdAttributes.h ufe/UsdAttributesHandler.h + ufe/UsdContextOps.h + ufe/UsdContextOpsHandler.h ufe/UsdObject3d.h ufe/UsdObject3dHandler.h ufe/UsdUndoCreateGroupCommand.h diff --git a/lib/ufe/Global.cpp b/lib/ufe/Global.cpp index a414aaf3ab..397374533b 100644 --- a/lib/ufe/Global.cpp +++ b/lib/ufe/Global.cpp @@ -30,6 +30,7 @@ // Note: must come after include of ufe files so we have the define. #include "UsdAttributesHandler.h" #include "UsdObject3dHandler.h" +#include "UsdContextOpsHandler.h" #else #include "UfeVersionCompat.h" #endif @@ -97,10 +98,11 @@ MStatus initialize() auto usdSceneItemOpsHandler = UsdSceneItemOpsHandler::create(); UFE_V2(auto usdAttributesHandler = UsdAttributesHandler::create();) UFE_V2(auto usdObject3dHandler = UsdObject3dHandler::create();) + UFE_V2(auto usdContextOpsHandler = UsdContextOpsHandler::create();) g_USDRtid = Ufe::RunTimeMgr::instance().register_( kUSDRunTimeName, usdHierHandler, usdTrans3dHandler, usdSceneItemOpsHandler - UFE_V2(, usdAttributesHandler, usdObject3dHandler)); + UFE_V2(, usdAttributesHandler, usdObject3dHandler, usdContextOpsHandler)); #if !defined(NDEBUG) assert(g_USDRtid != 0); diff --git a/lib/ufe/UsdContextOps.cpp b/lib/ufe/UsdContextOps.cpp new file mode 100644 index 0000000000..cbcecbd395 --- /dev/null +++ b/lib/ufe/UsdContextOps.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2020 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "UsdContextOps.h" +#include "Utils.h" + +#include + +MAYAUSD_NS_DEF { +namespace ufe { + +UsdContextOps::UsdContextOps() + : Ufe::ContextOps() +{ +} + +UsdContextOps::~UsdContextOps() +{ +} + +/*static*/ +UsdContextOps::Ptr UsdContextOps::create() +{ + return std::make_shared(); +} + +void UsdContextOps::setItem(const UsdSceneItem::Ptr& item) +{ + fPrim = item->prim(); + fItem = item; +} + +const Ufe::Path& UsdContextOps::path() const +{ + return fItem->path(); +} + +//------------------------------------------------------------------------------ +// Ufe::ContextOps overrides +//------------------------------------------------------------------------------ + +Ufe::SceneItem::Ptr UsdContextOps::sceneItem() const +{ + return fItem; +} + +Ufe::ContextOps::Items UsdContextOps::getItems( + const Ufe::ContextOps::ItemPath& itemPath +) +{ + Ufe::ContextOps::Items items; + if (itemPath.empty()) { + // Top-level items. Just variant sets for now. + if (fPrim.HasVariantSets()) { + Ufe::ContextItem variantSetsOp("Variant Sets", "Variant Sets", true); + items.push_back(variantSetsOp); + } + } + else { + if (itemPath[0] == "Variant Sets") { + UsdVariantSets varSets = fPrim.GetVariantSets(); + std::vector varSetsNames; + varSets.GetNames(&varSetsNames); + + if (itemPath.size() == 1u) { + // Variant sets list. + for (auto i = varSetsNames.crbegin(); i != varSetsNames.crend(); ++i) { + + items.push_back(Ufe::ContextItem(*i, *i, true)); + } + } + else { + // Variants of a given variant set. Second item in the path is + // the variant set name. + assert(itemPath.size() == 2u); + + UsdVariantSet varSet = varSets.GetVariantSet(itemPath[1]); + auto current = varSet.GetVariantSelection(); + + const auto varNames = varSet.GetVariantNames(); + for (const auto& vn : varNames) { + const bool isCurrent(vn == current); + items.push_back(Ufe::ContextItem(vn, vn, false, isCurrent)); + } + } // Variants of a variant set + } // Variant sets + } // Top-level items + + return items; +} + +bool UsdContextOps::doOp(const ItemPath& itemPath) +{ + return false; +} + +} // namespace ufe +} // namespace MayaUsd diff --git a/lib/ufe/UsdContextOps.h b/lib/ufe/UsdContextOps.h new file mode 100644 index 0000000000..9edd5cc05c --- /dev/null +++ b/lib/ufe/UsdContextOps.h @@ -0,0 +1,65 @@ +// +// Copyright 2020 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include "../base/api.h" + +#include "UsdSceneItem.h" + +#include +#include + +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +MAYAUSD_NS_DEF { +namespace ufe { + +//! \brief Interface for scene item operations. +class MAYAUSD_CORE_PUBLIC UsdContextOps : public Ufe::ContextOps +{ +public: + typedef std::shared_ptr Ptr; + + UsdContextOps(); + ~UsdContextOps() override; + + // Delete the copy/move constructors assignment operators. + UsdContextOps(const UsdContextOps&) = delete; + UsdContextOps& operator=(const UsdContextOps&) = delete; + UsdContextOps(UsdContextOps&&) = delete; + UsdContextOps& operator=(UsdContextOps&&) = delete; + + //! Create a UsdContextOps. + static UsdContextOps::Ptr create(); + + void setItem(const UsdSceneItem::Ptr& item); + const Ufe::Path& path() const; + + // Ufe::ContextOps overrides + Ufe::SceneItem::Ptr sceneItem() const override; + Items getItems(const ItemPath& itemPath) override; + bool doOp(const ItemPath& itemPath) override; + +private: + UsdSceneItem::Ptr fItem; + UsdPrim fPrim; + +}; // UsdContextOps + +} // namespace ufe +} // namespace MayaUsd diff --git a/lib/ufe/UsdContextOpsHandler.cpp b/lib/ufe/UsdContextOpsHandler.cpp new file mode 100644 index 0000000000..4430c3262f --- /dev/null +++ b/lib/ufe/UsdContextOpsHandler.cpp @@ -0,0 +1,53 @@ +// +// Copyright 2020 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "UsdContextOpsHandler.h" + +MAYAUSD_NS_DEF { +namespace ufe { + +UsdContextOpsHandler::UsdContextOpsHandler() + : Ufe::ContextOpsHandler() +{ + fUsdContextOps = UsdContextOps::create(); +} + +UsdContextOpsHandler::~UsdContextOpsHandler() +{ +} + +/*static*/ +UsdContextOpsHandler::Ptr UsdContextOpsHandler::create() +{ + return std::make_shared(); +} + +//------------------------------------------------------------------------------ +// Ufe::ContextOpsHandler overrides +//------------------------------------------------------------------------------ + +Ufe::ContextOps::Ptr UsdContextOpsHandler::contextOps(const Ufe::SceneItem::Ptr& item) const +{ + UsdSceneItem::Ptr usdItem = std::dynamic_pointer_cast(item); +#if !defined(NDEBUG) + assert(usdItem); +#endif + fUsdContextOps->setItem(usdItem); + return fUsdContextOps; +} + +} // namespace ufe +} // namespace MayaUsd diff --git a/lib/ufe/UsdContextOpsHandler.h b/lib/ufe/UsdContextOpsHandler.h new file mode 100644 index 0000000000..7f5a601532 --- /dev/null +++ b/lib/ufe/UsdContextOpsHandler.h @@ -0,0 +1,56 @@ +// +// Copyright 2020 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include "../base/api.h" + +#include "UsdContextOps.h" + +#include + +//PXR_NAMESPACE_USING_DIRECTIVE + +MAYAUSD_NS_DEF { +namespace ufe { + +//! \brief Interface to create a UsdContextOps interface object. +class MAYAUSD_CORE_PUBLIC UsdContextOpsHandler : public Ufe::ContextOpsHandler +{ +public: + typedef std::shared_ptr Ptr; + + UsdContextOpsHandler(); + ~UsdContextOpsHandler() override; + + // Delete the copy/move constructors assignment operators. + UsdContextOpsHandler(const UsdContextOpsHandler&) = delete; + UsdContextOpsHandler& operator=(const UsdContextOpsHandler&) = delete; + UsdContextOpsHandler(UsdContextOpsHandler&&) = delete; + UsdContextOpsHandler& operator=(UsdContextOpsHandler&&) = delete; + + //! Create a UsdContextOpsHandler. + static UsdContextOpsHandler::Ptr create(); + + // Ufe::ContextOpsHandler overrides + Ufe::ContextOps::Ptr contextOps(const Ufe::SceneItem::Ptr& item) const override; + +private: + UsdContextOps::Ptr fUsdContextOps; + +}; // UsdContextOpsHandler + +} // namespace ufe +} // namespace MayaUsd From 6eca98d06170bab3f5620a955d61bc677971b5cc Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Sun, 16 Feb 2020 18:23:34 -0800 Subject: [PATCH 2/6] Added context ops implementation. --- lib/ufe/UsdContextOps.cpp | 167 +++++++++++++++++++++++++++++++++++++- lib/ufe/UsdContextOps.h | 1 + 2 files changed, 164 insertions(+), 4 deletions(-) diff --git a/lib/ufe/UsdContextOps.cpp b/lib/ufe/UsdContextOps.cpp index cbcecbd395..f6a8c66f31 100644 --- a/lib/ufe/UsdContextOps.cpp +++ b/lib/ufe/UsdContextOps.cpp @@ -17,8 +17,42 @@ #include "UsdContextOps.h" #include "Utils.h" +#include +#include + #include +#include + +#include + +namespace { + +class SetVariantSelectionUndoableCommand : public Ufe::UndoableCommand +{ +public: + + SetVariantSelectionUndoableCommand( + const UsdPrim& prim, + const Ufe::ContextOps::ItemPath& itemPath + ) : fVarSet(prim.GetVariantSets().GetVariantSet(itemPath[1])), + fOldSelection(fVarSet.GetVariantSelection()), + fNewSelection(itemPath[2]) + {} + + void undo() override { fVarSet.SetVariantSelection(fOldSelection); } + + void redo() override { fVarSet.SetVariantSelection(fNewSelection); } + +private: + + UsdVariantSet fVarSet; + const std::string fOldSelection; + const std::string fNewSelection; +}; + +} + MAYAUSD_NS_DEF { namespace ufe { @@ -63,11 +97,27 @@ Ufe::ContextOps::Items UsdContextOps::getItems( { Ufe::ContextOps::Items items; if (itemPath.empty()) { - // Top-level items. Just variant sets for now. + // Top-level items. Variant sets, visibility, and list attributes. if (fPrim.HasVariantSets()) { Ufe::ContextItem variantSetsOp("Variant Sets", "Variant Sets", true); items.push_back(variantSetsOp); } + auto attributes = Ufe::Attributes::attributes(sceneItem()); + if (attributes) { + auto visibility = + std::dynamic_pointer_cast( + attributes->attribute("visibility")); + if (visibility) { + auto current = visibility->get(); + const std::string l = (current == "invisible") ? + std::string("Make Visible") : std::string("Make Invisible"); + Ufe::ContextItem visibilityOp("Toggle Visibility", l); + items.push_back(visibilityOp); + } + Ufe::ContextItem listAttribsOp( + "List Attributes", "List Attributes..."); + items.push_back(listAttribsOp); + } } else { if (itemPath[0] == "Variant Sets") { @@ -88,12 +138,13 @@ Ufe::ContextOps::Items UsdContextOps::getItems( assert(itemPath.size() == 2u); UsdVariantSet varSet = varSets.GetVariantSet(itemPath[1]); - auto current = varSet.GetVariantSelection(); + auto selected = varSet.GetVariantSelection(); const auto varNames = varSet.GetVariantNames(); for (const auto& vn : varNames) { - const bool isCurrent(vn == current); - items.push_back(Ufe::ContextItem(vn, vn, false, isCurrent)); + const bool isSelected(vn == selected); + items.push_back( + Ufe::ContextItem(vn, vn, false, true, isSelected, true)); } } // Variants of a variant set } // Variant sets @@ -104,8 +155,116 @@ Ufe::ContextOps::Items UsdContextOps::getItems( bool UsdContextOps::doOp(const ItemPath& itemPath) { + // Empty argument means no operation was specified, error. + if (itemPath.empty()) { + return false; + } + + if (itemPath[0] == "Variant Sets") { + // Operation is to set a variant in a variant set. Need both the + // variant set and the variant as arguments to the operation. + if (itemPath.size() != 3u) { + return false; + } + + // At this point we know we have enough arguments to execute the + // operation. Get the variant set. + UsdVariantSets varSets = fPrim.GetVariantSets(); + UsdVariantSet varSet = varSets.GetVariantSet(itemPath[1]); + return varSet.SetVariantSelection(itemPath[2]); + } // Variant sets + else if (itemPath[0] == "Toggle Visibility") { + auto attributes = Ufe::Attributes::attributes(sceneItem()); + assert(attributes); + auto visibility = std::dynamic_pointer_cast( + attributes->attribute("visibility")); + assert(visibility); + auto current = visibility->get(); + visibility->set(current == "invisible" ? "inherited" : "invisible"); + } // Visibility + else if (itemPath[0] == "List Attributes") { + MGlobal::executePythonCommand("import maya.cmds as cmds"); + MGlobal::executePythonCommand("cmds.confirmDialog()"); + } + return false; } +Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) +{ + // Empty argument means no operation was specified, error. + if (itemPath.empty()) { + return nullptr; + } + + if (itemPath[0] == "Variant Sets") { + // Operation is to set a variant in a variant set. Need both the + // variant set and the variant as arguments to the operation. + if (itemPath.size() != 3u) { + return nullptr; + } + + // At this point we know we have enough arguments to execute the + // operation. + return std::make_shared( + fPrim, itemPath); + } // Variant sets + else if (itemPath[0] == "Toggle Visibility") { + auto attributes = Ufe::Attributes::attributes(sceneItem()); + assert(attributes); + auto visibility = std::dynamic_pointer_cast( + attributes->attribute("visibility")); + assert(visibility); + auto current = visibility->get(); + return visibility->setCmd( + current == "invisible" ? "inherited" : "invisible"); + } // Visibility + else if (itemPath[0] == "List Attributes") { + const char* script = R"( +import maya.cmds as cmds +from functools import partial + +def dismissUSDListAttributes(data, msg): + cmds.layoutDialog(dismiss=msg) + +def buildUSDListAttributesDialog(): + form = cmds.setParent(q=True) + + # layoutDialog's are unfortunately not resizable, so hard code a size + # here, to make sure all UI elements are visible. + # + cmds.formLayout(form, e=True, width=500) + + sceneItem = next(iter(ufe.GlobalSelection.get())) + + # Listing the attributes requires Attributes interface support. This + # should have been validated by the context ops interface. + attr = ufe.Attributes.attributes(sceneItem) + attrNames = attr.attributeNames + + attributesWidget = cmds.textField(editable=False, text='\n'.join(attrNames)) + + okBtn = cmds.button(label='OK', + command=partial(dismissUSDListAttributes, msg='ok')) + + vSpc = 10 + hSpc = 10 + + cmds.formLayout( + form, edit=True, + attachForm=[(attributesWidget, 'top', vSpc), + (attributesWidget, 'left', 0), + (attributesWidget, 'right', 0), + (okBtn, 'bottom', vSpc), + (okBtn, 'right', hSpc)]) + +cmds.layoutDialog(ui=buildUSDListAttributesDialog, title='Attributes') +)"; + MGlobal::executePythonCommand(script); + } + + return nullptr; +} + } // namespace ufe } // namespace MayaUsd diff --git a/lib/ufe/UsdContextOps.h b/lib/ufe/UsdContextOps.h index 9edd5cc05c..833d9602b0 100644 --- a/lib/ufe/UsdContextOps.h +++ b/lib/ufe/UsdContextOps.h @@ -54,6 +54,7 @@ class MAYAUSD_CORE_PUBLIC UsdContextOps : public Ufe::ContextOps Ufe::SceneItem::Ptr sceneItem() const override; Items getItems(const ItemPath& itemPath) override; bool doOp(const ItemPath& itemPath) override; + Ufe::UndoableCommand::Ptr doOpCmd(const ItemPath& itemPath) override; private: UsdSceneItem::Ptr fItem; From 6c0e3eed67627fa35615fdbbbd875edec321b1f5 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Tue, 18 Feb 2020 08:40:48 -0800 Subject: [PATCH 3/6] doOpCmd() code cleanup. --- lib/ufe/UsdContextOps.cpp | 122 ++++++++++++++------------------------ lib/ufe/UsdContextOps.h | 1 - 2 files changed, 44 insertions(+), 79 deletions(-) diff --git a/lib/ufe/UsdContextOps.cpp b/lib/ufe/UsdContextOps.cpp index f6a8c66f31..42de91b3cd 100644 --- a/lib/ufe/UsdContextOps.cpp +++ b/lib/ufe/UsdContextOps.cpp @@ -28,6 +28,49 @@ namespace { +// Production-quality code would use a separate Python module file for this +// purpose, for ease of maintenance and separation of concerns. PPT, 18-Feb-20. +const char* listAttributesScript = R"( +import maya.cmds as cmds +from functools import partial + +def dismissUSDListAttributes(data, msg): + cmds.layoutDialog(dismiss=msg) + +def buildUSDListAttributesDialog(): + form = cmds.setParent(q=True) + + # layoutDialog's are unfortunately not resizable, so hard code a size + # here, to make sure all UI elements are visible. + # + cmds.formLayout(form, e=True, width=500) + + sceneItem = next(iter(ufe.GlobalSelection.get())) + + # Listing the attributes requires Attributes interface support. This + # should have been validated by the context ops interface. + attr = ufe.Attributes.attributes(sceneItem) + attrNames = attr.attributeNames + + attributesWidget = cmds.textField(editable=False, text='\n'.join(attrNames)) + + okBtn = cmds.button(label='OK', + command=partial(dismissUSDListAttributes, msg='ok')) + + vSpc = 10 + hSpc = 10 + + cmds.formLayout( + form, edit=True, + attachForm=[(attributesWidget, 'top', vSpc), + (attributesWidget, 'left', 0), + (attributesWidget, 'right', 0), + (okBtn, 'bottom', vSpc), + (okBtn, 'right', hSpc)]) + +cmds.layoutDialog(ui=buildUSDListAttributesDialog, title='Attributes') +)"; + class SetVariantSelectionUndoableCommand : public Ufe::UndoableCommand { public: @@ -153,43 +196,6 @@ Ufe::ContextOps::Items UsdContextOps::getItems( return items; } -bool UsdContextOps::doOp(const ItemPath& itemPath) -{ - // Empty argument means no operation was specified, error. - if (itemPath.empty()) { - return false; - } - - if (itemPath[0] == "Variant Sets") { - // Operation is to set a variant in a variant set. Need both the - // variant set and the variant as arguments to the operation. - if (itemPath.size() != 3u) { - return false; - } - - // At this point we know we have enough arguments to execute the - // operation. Get the variant set. - UsdVariantSets varSets = fPrim.GetVariantSets(); - UsdVariantSet varSet = varSets.GetVariantSet(itemPath[1]); - return varSet.SetVariantSelection(itemPath[2]); - } // Variant sets - else if (itemPath[0] == "Toggle Visibility") { - auto attributes = Ufe::Attributes::attributes(sceneItem()); - assert(attributes); - auto visibility = std::dynamic_pointer_cast( - attributes->attribute("visibility")); - assert(visibility); - auto current = visibility->get(); - visibility->set(current == "invisible" ? "inherited" : "invisible"); - } // Visibility - else if (itemPath[0] == "List Attributes") { - MGlobal::executePythonCommand("import maya.cmds as cmds"); - MGlobal::executePythonCommand("cmds.confirmDialog()"); - } - - return false; -} - Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) { // Empty argument means no operation was specified, error. @@ -220,47 +226,7 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) current == "invisible" ? "inherited" : "invisible"); } // Visibility else if (itemPath[0] == "List Attributes") { - const char* script = R"( -import maya.cmds as cmds -from functools import partial - -def dismissUSDListAttributes(data, msg): - cmds.layoutDialog(dismiss=msg) - -def buildUSDListAttributesDialog(): - form = cmds.setParent(q=True) - - # layoutDialog's are unfortunately not resizable, so hard code a size - # here, to make sure all UI elements are visible. - # - cmds.formLayout(form, e=True, width=500) - - sceneItem = next(iter(ufe.GlobalSelection.get())) - - # Listing the attributes requires Attributes interface support. This - # should have been validated by the context ops interface. - attr = ufe.Attributes.attributes(sceneItem) - attrNames = attr.attributeNames - - attributesWidget = cmds.textField(editable=False, text='\n'.join(attrNames)) - - okBtn = cmds.button(label='OK', - command=partial(dismissUSDListAttributes, msg='ok')) - - vSpc = 10 - hSpc = 10 - - cmds.formLayout( - form, edit=True, - attachForm=[(attributesWidget, 'top', vSpc), - (attributesWidget, 'left', 0), - (attributesWidget, 'right', 0), - (okBtn, 'bottom', vSpc), - (okBtn, 'right', hSpc)]) - -cmds.layoutDialog(ui=buildUSDListAttributesDialog, title='Attributes') -)"; - MGlobal::executePythonCommand(script); + MGlobal::executePythonCommand(listAttributesScript); } return nullptr; diff --git a/lib/ufe/UsdContextOps.h b/lib/ufe/UsdContextOps.h index 833d9602b0..ef1b5e3aad 100644 --- a/lib/ufe/UsdContextOps.h +++ b/lib/ufe/UsdContextOps.h @@ -53,7 +53,6 @@ class MAYAUSD_CORE_PUBLIC UsdContextOps : public Ufe::ContextOps // Ufe::ContextOps overrides Ufe::SceneItem::Ptr sceneItem() const override; Items getItems(const ItemPath& itemPath) override; - bool doOp(const ItemPath& itemPath) override; Ufe::UndoableCommand::Ptr doOpCmd(const ItemPath& itemPath) override; private: From f44233830a4ce4a69bac5fea43be7669a34c4995 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Tue, 25 Feb 2020 07:43:29 -0800 Subject: [PATCH 4/6] Updated to latest context ops interface, added test. --- lib/ufe/UsdContextOps.cpp | 69 +++------------- lib/ufe/UsdContextOps.h | 2 +- test/lib/ufe/CMakeLists.txt | 1 + test/lib/ufe/testContextOps.py | 142 +++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 60 deletions(-) create mode 100644 test/lib/ufe/testContextOps.py diff --git a/lib/ufe/UsdContextOps.cpp b/lib/ufe/UsdContextOps.cpp index 42de91b3cd..fed28e3e54 100644 --- a/lib/ufe/UsdContextOps.cpp +++ b/lib/ufe/UsdContextOps.cpp @@ -28,49 +28,6 @@ namespace { -// Production-quality code would use a separate Python module file for this -// purpose, for ease of maintenance and separation of concerns. PPT, 18-Feb-20. -const char* listAttributesScript = R"( -import maya.cmds as cmds -from functools import partial - -def dismissUSDListAttributes(data, msg): - cmds.layoutDialog(dismiss=msg) - -def buildUSDListAttributesDialog(): - form = cmds.setParent(q=True) - - # layoutDialog's are unfortunately not resizable, so hard code a size - # here, to make sure all UI elements are visible. - # - cmds.formLayout(form, e=True, width=500) - - sceneItem = next(iter(ufe.GlobalSelection.get())) - - # Listing the attributes requires Attributes interface support. This - # should have been validated by the context ops interface. - attr = ufe.Attributes.attributes(sceneItem) - attrNames = attr.attributeNames - - attributesWidget = cmds.textField(editable=False, text='\n'.join(attrNames)) - - okBtn = cmds.button(label='OK', - command=partial(dismissUSDListAttributes, msg='ok')) - - vSpc = 10 - hSpc = 10 - - cmds.formLayout( - form, edit=True, - attachForm=[(attributesWidget, 'top', vSpc), - (attributesWidget, 'left', 0), - (attributesWidget, 'right', 0), - (okBtn, 'bottom', vSpc), - (okBtn, 'right', hSpc)]) - -cmds.layoutDialog(ui=buildUSDListAttributesDialog, title='Attributes') -)"; - class SetVariantSelectionUndoableCommand : public Ufe::UndoableCommand { public: @@ -136,14 +93,14 @@ Ufe::SceneItem::Ptr UsdContextOps::sceneItem() const Ufe::ContextOps::Items UsdContextOps::getItems( const Ufe::ContextOps::ItemPath& itemPath -) +) const { Ufe::ContextOps::Items items; if (itemPath.empty()) { - // Top-level items. Variant sets, visibility, and list attributes. + // Top-level items. Variant sets and visibility. if (fPrim.HasVariantSets()) { - Ufe::ContextItem variantSetsOp("Variant Sets", "Variant Sets", true); - items.push_back(variantSetsOp); + items.emplace_back( + "Variant Sets", "Variant Sets", Ufe::ContextItem::kHasChildren); } auto attributes = Ufe::Attributes::attributes(sceneItem()); if (attributes) { @@ -154,12 +111,8 @@ Ufe::ContextOps::Items UsdContextOps::getItems( auto current = visibility->get(); const std::string l = (current == "invisible") ? std::string("Make Visible") : std::string("Make Invisible"); - Ufe::ContextItem visibilityOp("Toggle Visibility", l); - items.push_back(visibilityOp); + items.emplace_back("Toggle Visibility", l); } - Ufe::ContextItem listAttribsOp( - "List Attributes", "List Attributes..."); - items.push_back(listAttribsOp); } } else { @@ -172,7 +125,7 @@ Ufe::ContextOps::Items UsdContextOps::getItems( // Variant sets list. for (auto i = varSetsNames.crbegin(); i != varSetsNames.crend(); ++i) { - items.push_back(Ufe::ContextItem(*i, *i, true)); + items.emplace_back(*i, *i, Ufe::ContextItem::kHasChildren); } } else { @@ -185,9 +138,10 @@ Ufe::ContextOps::Items UsdContextOps::getItems( const auto varNames = varSet.GetVariantNames(); for (const auto& vn : varNames) { - const bool isSelected(vn == selected); - items.push_back( - Ufe::ContextItem(vn, vn, false, true, isSelected, true)); + const bool checked(vn == selected); + items.emplace_back(vn, vn, Ufe::ContextItem::kNoChildren, + Ufe::ContextItem::kCheckable, checked, + Ufe::ContextItem::kExclusive); } } // Variants of a variant set } // Variant sets @@ -225,9 +179,6 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) return visibility->setCmd( current == "invisible" ? "inherited" : "invisible"); } // Visibility - else if (itemPath[0] == "List Attributes") { - MGlobal::executePythonCommand(listAttributesScript); - } return nullptr; } diff --git a/lib/ufe/UsdContextOps.h b/lib/ufe/UsdContextOps.h index ef1b5e3aad..bd4cbd33a1 100644 --- a/lib/ufe/UsdContextOps.h +++ b/lib/ufe/UsdContextOps.h @@ -52,7 +52,7 @@ class MAYAUSD_CORE_PUBLIC UsdContextOps : public Ufe::ContextOps // Ufe::ContextOps overrides Ufe::SceneItem::Ptr sceneItem() const override; - Items getItems(const ItemPath& itemPath) override; + Items getItems(const ItemPath& itemPath) const override; Ufe::UndoableCommand::Ptr doOpCmd(const ItemPath& itemPath) override; private: diff --git a/test/lib/ufe/CMakeLists.txt b/test/lib/ufe/CMakeLists.txt index eef035e08c..6beefe3def 100644 --- a/test/lib/ufe/CMakeLists.txt +++ b/test/lib/ufe/CMakeLists.txt @@ -34,6 +34,7 @@ if(CMAKE_UFE_V2_FEATURES_AVAILABLE) testGroupCmd.py testAttribute.py testAttributes.py + testContextOps.py # The following files test UFE_V1 interfaces, and therefore should not # depend on UFE_V2. However, the test code relies on capability to # retrieve a USD prim from a UFE scene item, which in turn depends on diff --git a/test/lib/ufe/testContextOps.py b/test/lib/ufe/testContextOps.py new file mode 100644 index 0000000000..7aef836d92 --- /dev/null +++ b/test/lib/ufe/testContextOps.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +# +# Copyright 2020 Autodesk +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import maya.cmds as cmds +import maya.internal.ufeSupport.ufeCmdWrapper as ufeCmd + +from pxr import UsdGeom + +from ufeTestUtils import usdUtils, mayaUtils +import ufe + +import unittest + +class ContextOpsTestCase(unittest.TestCase): + '''Verify the ContextOps interface for the USD runtime.''' + + pluginsLoaded = False + + @classmethod + def setUpClass(cls): + if not cls.pluginsLoaded: + cls.pluginsLoaded = mayaUtils.isMayaUsdPluginLoaded() + + def setUp(self): + ''' Called initially to set up the maya test environment ''' + # Load plugins + self.assertTrue(self.pluginsLoaded) + + # Open top_layer.ma scene in test-samples + mayaUtils.openTopLayerScene() + + # Clear selection to start off. + ufe.GlobalSelection.get().clear() + + # Select Ball_35. + ball35Path = ufe.Path([ + mayaUtils.createUfePathSegment("|world|transform1|proxyShape1"), + usdUtils.createUfePathSegment("/Room_set/Props/Ball_35")]) + self.ball35Item = ufe.Hierarchy.createItem(ball35Path) + + ufe.GlobalSelection.get().append(self.ball35Item) + + # Create a ContextOps interface for it. + self.contextOps = ufe.ContextOps.contextOps(self.ball35Item) + + def testGetItems(self): + # The top-level context items are visibility (for all prims) and + # variant sets, for those prims that have them (such as Ball_35). + contextItems = self.contextOps.getItems([]) + contextItemStrings = [c.item for c in contextItems] + + self.assertIn('Variant Sets', contextItemStrings) + self.assertIn('Toggle Visibility', contextItemStrings) + + # Ball_35 has a single variant set, which has children. + contextItems = self.contextOps.getItems(['Variant Sets']) + contextItemStrings = [c.item for c in contextItems] + + self.assertListEqual(['shadingVariant'], contextItemStrings) + self.assertTrue(contextItems[0].hasChildren) + + # Initial shadingVariant is "Ball_8" + contextItems = self.contextOps.getItems( + ['Variant Sets', 'shadingVariant']) + self.assertGreater(len(contextItems), 1) + for c in contextItems: + self.assertFalse(c.hasChildren) + self.assertTrue(c.checkable) + if c.checked: + self.assertEqual(c.item, 'Ball_8') + + 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() From da00bb29150024d2cc8cca325bc1a912368b8056 Mon Sep 17 00:00:00 2001 From: Pierre Tremblay Date: Tue, 25 Feb 2020 10:46:00 -0800 Subject: [PATCH 5/6] Added conditional compilation for UFE 0.2.9 --- lib/CMakeLists.txt | 8 ++++++-- lib/ufe/Global.cpp | 10 +++++++++- test/lib/ufe/CMakeLists.txt | 6 +++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d25ec67f45..586604f859 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -244,12 +244,16 @@ if(UFE_FOUND) ufe/UsdAttribute.cpp ufe/UsdAttributes.cpp ufe/UsdAttributesHandler.cpp - ufe/UsdContextOps.cpp - ufe/UsdContextOpsHandler.cpp ufe/UsdObject3d.cpp ufe/UsdObject3dHandler.cpp ufe/UsdUndoCreateGroupCommand.cpp ) + if(UFE_PREVIEW_VERSION_NUM GREATER_EQUAL 2009) + list(APPEND mayaUsd_src + ufe/UsdContextOps.cpp + ufe/UsdContextOpsHandler.cpp + ) + endif() endif() endif() diff --git a/lib/ufe/Global.cpp b/lib/ufe/Global.cpp index 397374533b..98973309df 100644 --- a/lib/ufe/Global.cpp +++ b/lib/ufe/Global.cpp @@ -30,7 +30,9 @@ // Note: must come after include of ufe files so we have the define. #include "UsdAttributesHandler.h" #include "UsdObject3dHandler.h" +#if UFE_PREVIEW_VERSION_NUM >= 2009 #include "UsdContextOpsHandler.h" +#endif #else #include "UfeVersionCompat.h" #endif @@ -98,11 +100,17 @@ MStatus initialize() auto usdSceneItemOpsHandler = UsdSceneItemOpsHandler::create(); UFE_V2(auto usdAttributesHandler = UsdAttributesHandler::create();) UFE_V2(auto usdObject3dHandler = UsdObject3dHandler::create();) +#if UFE_PREVIEW_VERSION_NUM >= 2009 UFE_V2(auto usdContextOpsHandler = UsdContextOpsHandler::create();) +#endif g_USDRtid = Ufe::RunTimeMgr::instance().register_( kUSDRunTimeName, usdHierHandler, usdTrans3dHandler, usdSceneItemOpsHandler - UFE_V2(, usdAttributesHandler, usdObject3dHandler, usdContextOpsHandler)); + UFE_V2(, usdAttributesHandler, usdObject3dHandler) +#if UFE_PREVIEW_VERSION_NUM >= 2009 + UFE_V2(, usdContextOpsHandler) +#endif + ); #if !defined(NDEBUG) assert(g_USDRtid != 0); diff --git a/test/lib/ufe/CMakeLists.txt b/test/lib/ufe/CMakeLists.txt index 6beefe3def..ba4964ee9a 100644 --- a/test/lib/ufe/CMakeLists.txt +++ b/test/lib/ufe/CMakeLists.txt @@ -34,7 +34,6 @@ if(CMAKE_UFE_V2_FEATURES_AVAILABLE) testGroupCmd.py testAttribute.py testAttributes.py - testContextOps.py # The following files test UFE_V1 interfaces, and therefore should not # depend on UFE_V2. However, the test code relies on capability to # retrieve a USD prim from a UFE scene item, which in turn depends on @@ -52,6 +51,11 @@ if(CMAKE_UFE_V2_FEATURES_AVAILABLE) # testTransform3dTranslate.py testRename.py ) + if(UFE_PREVIEW_VERSION_NUM GREATER_EQUAL 2009) + list(APPEND test_script_files + testContextOps.py + ) + endif() endif() # copy ufe tests to ${CMAKE_CURRENT_BINARY_DIR} and run them from there From 156ede5e96bdcffd660e0558d660e3cb5bfa8d25 Mon Sep 17 00:00:00 2001 From: Krystian Ligenza Date: Tue, 3 Mar 2020 12:25:45 -0500 Subject: [PATCH 6/6] Review comments --- lib/ufe/UsdContextOps.cpp | 4 ++++ lib/ufe/UsdContextOps.h | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/ufe/UsdContextOps.cpp b/lib/ufe/UsdContextOps.cpp index fed28e3e54..7956bd38b7 100644 --- a/lib/ufe/UsdContextOps.cpp +++ b/lib/ufe/UsdContextOps.cpp @@ -21,6 +21,7 @@ #include #include +#include "pxr/base/tf/diagnostic.h" #include @@ -28,6 +29,7 @@ namespace { +//! \brief Undoable command for variant selection change class SetVariantSelectionUndoableCommand : public Ufe::UndoableCommand { public: @@ -154,6 +156,7 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) { // Empty argument means no operation was specified, error. if (itemPath.empty()) { + TF_CODING_ERROR("Empty path means no operation was specified"); return nullptr; } @@ -161,6 +164,7 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) // Operation is to set a variant in a variant set. Need both the // variant set and the variant as arguments to the operation. if (itemPath.size() != 3u) { + TF_CODING_ERROR("Wrong number of arguments"); return nullptr; } diff --git a/lib/ufe/UsdContextOps.h b/lib/ufe/UsdContextOps.h index bd4cbd33a1..deaf98464b 100644 --- a/lib/ufe/UsdContextOps.h +++ b/lib/ufe/UsdContextOps.h @@ -29,7 +29,15 @@ PXR_NAMESPACE_USING_DIRECTIVE MAYAUSD_NS_DEF { namespace ufe { -//! \brief Interface for scene item operations. +//! \brief Interface for scene item context operations. +/*! + This class defines the interface that USD run-time implements to + provide contextual operation support (example Outliner context menu). + + This class is not copy-able, nor move-able. + + \see UFE ContextOps class documentation for more details +*/ class MAYAUSD_CORE_PUBLIC UsdContextOps : public Ufe::ContextOps { public: