From cfc4887419099e2b401012cb408f243fd8af904c Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Fri, 16 Jul 2021 14:53:03 -0400 Subject: [PATCH 01/52] ENH: Supporting External Expressions Supporting "External" Expressions ================================= There are use cases where the workflow may want to store expressions in a separate Attribute Resource. The core of SMTK already supported this but the UI system assumed that the Attribute Resource which owned the ValueItem was also the source for expressions. This is no longer the case. qtInstancedView can now optionally take in an Attribute Resource instead of solely relying on the one associated with the UI Manager. This allows classes like the qtAttributeEditor to supply the Attribute Resource. Added a new query function called: findResourceContainingDefinition that will return an Attribute Resource that contains an Attribute Definition referred to by its typename. If the optional Attribute Resource provided to the function also contains the Definition, it is returned immediately without doing any additional searching. This maintains the original use case where the expressions are stored in the same resource. qtInputItem no longer assumes the Item's Attribute Resource is the one being used as a source for expressions. Added two template files that can be used to demo the functionality. data/attribute/attribute_collection/externalExpressionsPt1.sbt - Contains an Attribute Definition with an Item that can use an expression that is not defined in that template data/attribute/attribute_collection/externalExpressionsPt2.sbt - Contains an Attribute Definition that represents the expressions used in Pt1. --- .../externalExpressionsPt1.sbt | 3 + .../externalExpressionsPt2.sbt | 3 + .../notes/supportingExternalExpressions.rst | 18 ++++++ smtk/attribute/utility/Queries.cxx | 62 +++++++++++++++++++ smtk/attribute/utility/Queries.h | 17 +++++ smtk/extension/qt/qtAttributeEditorDialog.cxx | 3 +- smtk/extension/qt/qtInputsItem.cxx | 29 +++++++-- smtk/extension/qt/qtInstancedView.cxx | 14 +++-- smtk/extension/qt/qtInstancedView.h | 12 +++- 9 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 data/attribute/attribute_collection/externalExpressionsPt1.sbt create mode 100644 data/attribute/attribute_collection/externalExpressionsPt2.sbt create mode 100644 doc/release/notes/supportingExternalExpressions.rst diff --git a/data/attribute/attribute_collection/externalExpressionsPt1.sbt b/data/attribute/attribute_collection/externalExpressionsPt1.sbt new file mode 100644 index 0000000000..700ab9aa24 --- /dev/null +++ b/data/attribute/attribute_collection/externalExpressionsPt1.sbt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e248ed259011f0c9561d4ecda7739999b4fe8c23fd2cb1ca24f7ebe3679eff9 +size 655 diff --git a/data/attribute/attribute_collection/externalExpressionsPt2.sbt b/data/attribute/attribute_collection/externalExpressionsPt2.sbt new file mode 100644 index 0000000000..aa77be2339 --- /dev/null +++ b/data/attribute/attribute_collection/externalExpressionsPt2.sbt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:762389e4f4b89efac5939a53ae6adbf9c07eaffd2115e5bdf6e58261353055d6 +size 707 diff --git a/doc/release/notes/supportingExternalExpressions.rst b/doc/release/notes/supportingExternalExpressions.rst new file mode 100644 index 0000000000..1d5f7b606d --- /dev/null +++ b/doc/release/notes/supportingExternalExpressions.rst @@ -0,0 +1,18 @@ +Supporting "External" Expressions +================================= +There are use cases where the workflow may want to store expressions in a separate Attribute Resource. +The core of SMTK already supported this but the UI system assumed that the Attribute Resource which owned the ValueItem was also the source for expressions. This is no longer the case. + +qtInstancedView can now optionally take in an Attribute Resource instead of solely relying on the one associated with the UI Manager. This allows classes like the qtAttributeEditor to supply the Attribute Resource. + +Added a new query function called: findResourceContainingDefinition that will return an Attribute Resource that contains an Attribute Definition referred to by its typename. If the optional Attribute Resource provided to the function also contains the Definition, it is returned immediately without doing any additional searching. This maintains the original use case where the expressions are stored in the same resource. + +qtInputItem no longer assumes the Item's Attribute Resource is the one being used as a source for expressions. + +Added two template files that can be used to demo the functionality. + +data/attribute/attribute_collection/externalExpressionsPt1.sbt - Contains an Attribute Definition with an Item that can use an expression that is not defined in that template + +data/attribute/attribute_collection/externalExpressionsPt2.sbt - Contains an Attribute Definition that represents the expressions used in Pt1. + +Closes #439 diff --git a/smtk/attribute/utility/Queries.cxx b/smtk/attribute/utility/Queries.cxx index d233535639..8e6f5b33fc 100644 --- a/smtk/attribute/utility/Queries.cxx +++ b/smtk/attribute/utility/Queries.cxx @@ -244,6 +244,68 @@ std::set associatableObjects( } return checkUniquenessCondition(compItem, candidates); } + +smtk::attribute::ResourcePtr findResourceContainingDefinition( + const std::string& defType, + smtk::attribute::ResourcePtr& sourceAttResource, + smtk::resource::ManagerPtr& resManager, + const smtk::common::UUID& ignoreResource) +{ + if (defType.empty()) + { + return nullptr; // No definition was given + } + + // Are we dealing with a source attribute resource? + if (sourceAttResource) + { + // Does it contain the definition? + if (sourceAttResource->findDefinition(defType)) + { + return sourceAttResource; + } + // Are there Resources associated with the sourceAttResource? + if (sourceAttResource->hasAssociations()) + { + auto resources = sourceAttResource->associations(); + // Lets see if any of the resources are attribute resources + for (const auto& resource : resources) + { + if (resource->id() == ignoreResource) + { + continue; + } + smtk::attribute::ResourcePtr attRes = + std::dynamic_pointer_cast(resource); + if (attRes && attRes->findDefinition(defType)) + { + return attRes; + } + } + } + } + if (resManager == nullptr) // There is no other place to search + { + return nullptr; + } + // Get all of the Attribute Resources stored in the Manager + auto managedResources = resManager->find(smtk::common::typeName()); + for (const auto& resource : managedResources) + { + if (resource->id() == ignoreResource) + { + continue; + } + smtk::attribute::ResourcePtr attRes = + std::dynamic_pointer_cast(resource); + if (attRes && attRes->findDefinition(defType)) + { + return attRes; + } + } + // Couldn't find it + return nullptr; +} } // namespace utility } // namespace attribute } // namespace smtk diff --git a/smtk/attribute/utility/Queries.h b/smtk/attribute/utility/Queries.h index d1cd54b4bd..9ec518f834 100644 --- a/smtk/attribute/utility/Queries.h +++ b/smtk/attribute/utility/Queries.h @@ -63,6 +63,23 @@ std::set associatableObjects( smtk::attribute::ResourcePtr& attResource, smtk::resource::ManagerPtr& resManager, const smtk::common::UUID& ignoreResource = smtk::common::UUID::null()); +///\brief Find an Attribute Resource that contains an Attribute Definition type. +/// +/// If ignoreResource is specified the corresponding resource will not participate in determining +/// which objects can be associated. The main use case would be updating the widget because a +/// resource is about to be removed from the system. Since it is still in memory we needed a way to ignore it. +/// There are 3 possible sources of Attribute Resources: +/// +/// * The sourceAttResource (if specified) contains the Definition - in that case, it is returned +/// * One of the Attribute Resources associated with the sourceAttResource (if specified), contains the Definition +/// * The resManager (if specified) has an Attribute Resource that contains the Definition +/// The above is also the order of precedence in terms of the search order +SMTKCORE_EXPORT +smtk::attribute::ResourcePtr findResourceContainingDefinition( + const std::string& defType, + smtk::attribute::ResourcePtr& sourceAttResource, + smtk::resource::ManagerPtr& resManager, + const smtk::common::UUID& ignoreResource = smtk::common::UUID::null()); } // namespace utility } // namespace attribute } // namespace smtk diff --git a/smtk/extension/qt/qtAttributeEditorDialog.cxx b/smtk/extension/qt/qtAttributeEditorDialog.cxx index 690af18ab9..33189de835 100644 --- a/smtk/extension/qt/qtAttributeEditorDialog.cxx +++ b/smtk/extension/qt/qtAttributeEditorDialog.cxx @@ -49,7 +49,8 @@ qtAttributeEditorDialog::qtAttributeEditorDialog( child.setAttribute("Name", m_attribute->name()).setAttribute("Type", m_attribute->type()); ViewInfo v(m_instancedViewDef, this->m_widget->attributeFrame, m_uiManager); - qtInstancedView* iview = dynamic_cast(qtInstancedView::createViewWidget(v)); + qtInstancedView* iview = dynamic_cast( + qtInstancedView::createViewWidget(v, m_attribute->attributeResource())); m_instancedView.reset(iview); m_widget->attributeName->setText(m_attribute->name().c_str()); diff --git a/smtk/extension/qt/qtInputsItem.cxx b/smtk/extension/qt/qtInputsItem.cxx index e66f153508..7a3b7b72cc 100644 --- a/smtk/extension/qt/qtInputsItem.cxx +++ b/smtk/extension/qt/qtInputsItem.cxx @@ -11,12 +11,14 @@ #include "smtk/extension/qt/qtInputsItem.h" #include "smtk/attribute/Definition.h" +#include "smtk/attribute/utility/Queries.h" #include "smtk/extension/qt/qtAttributeEditorDialog.h" #include "smtk/extension/qt/qtBaseAttributeView.h" #include "smtk/extension/qt/qtDiscreteValueEditor.h" #include "smtk/extension/qt/qtDoubleLineEdit.h" #include "smtk/extension/qt/qtOverlay.h" #include "smtk/extension/qt/qtUIManager.h" +#include "smtk/io/Logger.h" #include #include @@ -1188,19 +1190,31 @@ void qtInputsItem::displayExpressionWidget(bool checkstate) } auto inputitem = m_itemInfo.itemAs(); - ResourcePtr lAttResource = inputitem->attribute()->attributeResource(); - if (!inputitem) { return; } + ResourcePtr sourceAttResource = inputitem->attribute()->attributeResource(); + if (checkstate) { m_internals->m_expressionCombo->blockSignals(true); m_internals->m_expressionCombo->clear(); auto valItemDef = inputitem->definitionAs(); - smtk::attribute::DefinitionPtr attDef = valItemDef->expressionDefinition(lAttResource); + // Lets find the attribute resource that contains the expression information + ResourcePtr lAttResource = smtk::attribute::utility::findResourceContainingDefinition( + valItemDef->expressionType(), sourceAttResource, this->uiManager()->resourceManager()); + if (lAttResource == nullptr) + { + smtkErrorMacro( + smtk::io::Logger::instance(), + " Could not find any AttributeResource containing Expressions of Type: " + << valItemDef->expressionType()); + return; + } + smtk::attribute::DefinitionPtr attDef = + lAttResource->findDefinition(valItemDef->expressionType()); QStringList attNames; int setIndex = 0; @@ -1312,6 +1326,12 @@ void qtInputsItem::onExpressionReferenceChanged() { return; } + smtk::attribute::ResourcePtr sourceAttResource = inputitem->attribute()->attributeResource(); + auto valItemDef = inputitem->definitionAs(); + // Lets find the attribute resource that contains the expression information + ResourcePtr lAttResource = smtk::attribute::utility::findResourceContainingDefinition( + valItemDef->expressionType(), sourceAttResource, this->uiManager()->resourceManager()); + smtk::attribute::ComponentItemPtr item = inputitem->expressionReference(); if (!item) { @@ -1326,8 +1346,6 @@ void qtInputsItem::onExpressionReferenceChanged() } else if (curIdx == 1) { - smtk::attribute::ResourcePtr lAttResource = item->attribute()->attributeResource(); - auto valItemDef = inputitem->definitionAs(); smtk::attribute::DefinitionPtr attDef = valItemDef->expressionDefinition(lAttResource); smtk::attribute::AttributePtr newAtt = lAttResource->createAttribute(attDef->type()); auto* editor = @@ -1372,7 +1390,6 @@ void qtInputsItem::onExpressionReferenceChanged() } else { - smtk::attribute::ResourcePtr lAttResource = item->attribute()->attributeResource(); AttributePtr attPtr = lAttResource->findAttribute(m_internals->m_expressionCombo->currentText().toStdString()); if (inputitem->isSet() && attPtr == inputitem->expression()) diff --git a/smtk/extension/qt/qtInstancedView.cxx b/smtk/extension/qt/qtInstancedView.cxx index 908e8b1ec8..2cf6f459f0 100644 --- a/smtk/extension/qt/qtInstancedView.cxx +++ b/smtk/extension/qt/qtInstancedView.cxx @@ -53,15 +53,20 @@ public: smtk::operation::Observers::Key m_observerKey; }; -qtBaseView* qtInstancedView::createViewWidget(const smtk::view::Information& info) +qtBaseView* qtInstancedView::createViewWidget( + const smtk::view::Information& info, + smtk::attribute::ResourcePtr overrideResource) { - qtInstancedView* view = new qtInstancedView(info); + qtInstancedView* view = new qtInstancedView(info, overrideResource); view->buildUI(); return view; } -qtInstancedView::qtInstancedView(const smtk::view::Information& info) +qtInstancedView::qtInstancedView( + const smtk::view::Information& info, + smtk::attribute::ResourcePtr overrideResource) : qtBaseAttributeView(info) + , m_overrideResource(overrideResource) { this->Internals = new qtInstancedViewInternals; } @@ -136,7 +141,8 @@ void qtInstancedView::updateUI() return; } - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = + (m_overrideResource ? m_overrideResource : this->uiManager()->attResource()); std::string attName, defName; smtk::attribute::AttributePtr att; smtk::attribute::DefinitionPtr attDef; diff --git a/smtk/extension/qt/qtInstancedView.h b/smtk/extension/qt/qtInstancedView.h index 56a109af15..97ec1b43c2 100644 --- a/smtk/extension/qt/qtInstancedView.h +++ b/smtk/extension/qt/qtInstancedView.h @@ -35,9 +35,16 @@ class SMTKQTEXT_EXPORT qtInstancedView : public qtBaseAttributeView public: smtkTypenameMacro(qtInstancedView); - static qtBaseView* createViewWidget(const smtk::view::Information& info); + ///\brief Create an instance view using an optionally specified attribute resource instead of the one + /// associated with the UI Manager - qtInstancedView(const smtk::view::Information& info); + static qtBaseView* createViewWidget( + const smtk::view::Information& info, + smtk::attribute::ResourcePtr overrideResource = nullptr); + + qtInstancedView( + const smtk::view::Information& info, + smtk::attribute::ResourcePtr overrideResource = nullptr); ~qtInstancedView() override; // Returns true if all attributes in the view are valid bool isValid() const override; @@ -64,6 +71,7 @@ protected: private: qtInstancedViewInternals* Internals; + smtk::attribute::ResourcePtr m_overrideResource; }; // class }; // namespace extension -- GitLab From f2b661798c3c49bb23129ced8d01fb0fca22dd41 Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Mon, 19 Jul 2021 18:59:09 -0400 Subject: [PATCH 02/52] ENH: Changing JSON Expression Format JSON will now store a ValueItem's Expression in ComponentItem format using the key "ExpressionReference" instead of 2 keys called "Expression" and "ExpressionName". This no only simplifies things format wise but will also support expressions stored in different resources. **Note** The older format is still supported so this change is backward compatible. **Note** The XML format is still using the older style. --- doc/release/notes/expressionIO_changes.rst | 6 ++++++ smtk/attribute/json/jsonValueItem.h | 20 ++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 doc/release/notes/expressionIO_changes.rst diff --git a/doc/release/notes/expressionIO_changes.rst b/doc/release/notes/expressionIO_changes.rst new file mode 100644 index 0000000000..b148bffd65 --- /dev/null +++ b/doc/release/notes/expressionIO_changes.rst @@ -0,0 +1,6 @@ +Changing Expression Format +========================== +JSON will now store a ValueItem's Expression in ComponentItem format using the key "ExpressionReference" instead of 2 keys called "Expression" and "ExpressionName". This no only simplifies things format wise but will also support expressions stored in different resources. + +**Note** The older format is still supported so this change is backward compatible. +**Note** The XML format is still using the older style. diff --git a/smtk/attribute/json/jsonValueItem.h b/smtk/attribute/json/jsonValueItem.h index 474c5b1d19..ff2715a2ce 100644 --- a/smtk/attribute/json/jsonValueItem.h +++ b/smtk/attribute/json/jsonValueItem.h @@ -12,8 +12,10 @@ #include "smtk/PublicPointerDefs.h" #include "smtk/attribute/Attribute.h" +#include "smtk/attribute/ComponentItem.h" #include "smtk/attribute/Resource.h" #include "smtk/attribute/ValueItem.h" +#include "smtk/attribute/json/jsonComponentItem.h" #include "smtk/attribute/json/jsonHelperFunction.h" #include "smtk/attribute/json/jsonItem.h" @@ -58,8 +60,7 @@ static void processDerivedValueToJson(json& j, ItemType itemPtr) } if (itemPtr->isExpression()) { - j["Expression"] = true; - j["ExpressionName"] = itemPtr->expression()->name(); + j["ExpressionReference"] = itemPtr->expressionReference(); return; } if ((itemPtr->numberOfRequiredValues() == 1) && !itemPtr->isExtensible()) @@ -100,7 +101,7 @@ static void processDerivedValueFromJson( const json& j, ItemType itemPtr, std::vector& itemExpressionInfo, - std::vector& /*attRefInfo*/) + std::vector& attRefInfo) { auto resPtr = itemPtr->attribute()->attributeResource(); if (itemPtr->isDiscrete()) @@ -119,7 +120,18 @@ static void processDerivedValueFromJson( { json expression; { - auto query = j.find("Expression"); + // This is the latest version for expressions + auto query = j.find("ExpressionReference"); + if (query != j.end()) + { + std::set convertedAttDefs; + ItemPtr expressionItem = itemPtr->expressionReference(); + smtk::attribute::JsonHelperFunction::processItemTypeFromJson( + *query, expressionItem, itemExpressionInfo, attRefInfo, convertedAttDefs); + return; + } + // This is the older implementation + query = j.find("Expression"); if (query != j.end()) { expression = *query; -- GitLab From e0fbab834e40cb0f9c60bd9b93c16ff9720073b0 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Fri, 23 Jul 2021 19:02:16 -0400 Subject: [PATCH 03/52] Deprecation: add macros for 21.08 deprecations --- smtk/common/Deprecation.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/smtk/common/Deprecation.h b/smtk/common/Deprecation.h index 9911f88284..8bd7e930bb 100644 --- a/smtk/common/Deprecation.h +++ b/smtk/common/Deprecation.h @@ -56,6 +56,12 @@ #define SMTK_DEPRECATION_REASON(version_major, version_minor, reason) \ "SMTK Deprecated in " #version_major "." #version_minor ": " #reason +#if SMTK_DEPRECATION_LEVEL >= SMTK_VERSION_CHECK(21, 8) +#define SMTK_DEPRECATED_IN_21_08(reason) SMTK_DEPRECATION(SMTK_DEPRECATION_REASON(21, 08, reason)) +#else +#define SMTK_DEPRECATED_IN_21_08(reason) +#endif + #if SMTK_DEPRECATION_LEVEL >= SMTK_VERSION_CHECK(21, 07) #define SMTK_DEPRECATED_IN_21_07(reason) SMTK_DEPRECATION(SMTK_DEPRECATION_REASON(21, 07, reason)) #else -- GitLab From ce8eabede6a6a35a6e0ecee5bda08ca6fe74ed96 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Fri, 23 Jul 2021 19:02:43 -0400 Subject: [PATCH 04/52] Deprecation: avoid double-quoting the reason in error messages --- smtk/common/Deprecation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smtk/common/Deprecation.h b/smtk/common/Deprecation.h index 8bd7e930bb..e53fd865a6 100644 --- a/smtk/common/Deprecation.h +++ b/smtk/common/Deprecation.h @@ -54,7 +54,7 @@ #endif #define SMTK_DEPRECATION_REASON(version_major, version_minor, reason) \ - "SMTK Deprecated in " #version_major "." #version_minor ": " #reason + "SMTK Deprecated in " #version_major "." #version_minor ": " reason #if SMTK_DEPRECATION_LEVEL >= SMTK_VERSION_CHECK(21, 8) #define SMTK_DEPRECATED_IN_21_08(reason) SMTK_DEPRECATION(SMTK_DEPRECATION_REASON(21, 08, reason)) -- GitLab From 761248b6dbf2abe0ec9ab5bf66e12c89fac88551 Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Tue, 27 Jul 2021 10:35:09 -0400 Subject: [PATCH 05/52] DOC: Updating New Release Issue Template * Updated to reflect we are now using rst files for release notes * Added some clarification text about the branchpoint --- .gitlab/issue_templates/new-release.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/issue_templates/new-release.md b/.gitlab/issue_templates/new-release.md index dec45344e4..c0865d89cc 100644 --- a/.gitlab/issue_templates/new-release.md +++ b/.gitlab/issue_templates/new-release.md @@ -6,7 +6,7 @@ following strings with the associated values: - `MAJOR`: e.g. yy is the year - `MINOR`: e.g. mm is the month - `PATCH`: e.g. the release sequence number (start at 0) - - `BRANCHPOINT`: The commit where the release should be started + - `BRANCHPOINT`: The commit where the release should be started - it is a point on master where the release process branch is started from. The release process branch will have multiple commits including the assembling of release notes and changing of the version. Please remove this comment. --> @@ -31,7 +31,7 @@ Please remove this comment. - Integrate changes. - Make a commit for each of these `release`-only changes on a single topic (suggested branch name: `update-to-vVERSION`): - - Assemble release notes into `doc/release/notes/smtk-MAJOR.MINOR.md`. + - [ ] Assemble release notes into `doc/release/notes/smtk-MAJOR.MINOR.rst`. - [ ] If `PATCH` is greater than 0, add items to the end of this file. - [ ] Update `version.txt` and tag the commit (tag this commit below) - [ ] `git checkout -b update-to-vVERSION BRANCHPOINT` -- GitLab From 699bb78b3ce3e55c9c085bd226f925c744310dc2 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 27 Jul 2021 14:19:35 -0400 Subject: [PATCH 06/52] ci: update to CMake 3.21.1 --- .gitlab/ci/cmake.ps1 | 4 ++-- .gitlab/ci/cmake.sh | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab/ci/cmake.ps1 b/.gitlab/ci/cmake.ps1 index 5d01e3f9e3..11e37069b6 100755 --- a/.gitlab/ci/cmake.ps1 +++ b/.gitlab/ci/cmake.ps1 @@ -1,7 +1,7 @@ $erroractionpreference = "stop" -$version = "3.21.0" -$sha256sum = "C7B88C907A753F4EC86E43DDC89F91F70BF1B011859142F7F29E6D51EA4ABB3C" +$version = "3.21.1" +$sha256sum = "9FBA6DF0B89BE0DC0377F2E77CA272B3F8C38691FE237699DE275EA0C2254242" $filename = "cmake-$version-windows-x86_64" $tarball = "$filename.zip" diff --git a/.gitlab/ci/cmake.sh b/.gitlab/ci/cmake.sh index 1547e30aa2..0a1d13682b 100755 --- a/.gitlab/ci/cmake.sh +++ b/.gitlab/ci/cmake.sh @@ -2,17 +2,17 @@ set -e -readonly version="3.21.0" +readonly version="3.21.1" case "$( uname -s )" in Linux) shatool="sha256sum" - sha256sum="d54ef6909f519740bc85cec07ff54574cd1e061f9f17357d9ace69f61c6291ce" + sha256sum="bf496ce869d0aa8c1f57e4d1a2e50c8f2fb12a6cd7ccb37ad743bb88f6b76a1e" platform="linux-x86_64" ;; Darwin) shatool="shasum -a 256" - sha256sum="c1c6f19dfc9c658a48b5aed22806595b2337bb3aedb71ab826552f74f568719f" + sha256sum="9dc2978c4d94a44f71336fa88c15bb0eee47cf44b6ece51b10d1dfae95f82279" platform="macos-universal" ;; *) -- GitLab From e20a6d61b2332a29c42c6b7b90a954da0c05dedc Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 27 Jul 2021 14:19:47 -0400 Subject: [PATCH 07/52] CTestCustom: drop exclusion for CMake warnings now fixed --- CMake/CTestCustom.cmake.in | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMake/CTestCustom.cmake.in b/CMake/CTestCustom.cmake.in index a2ad18fc40..3aba728952 100644 --- a/CMake/CTestCustom.cmake.in +++ b/CMake/CTestCustom.cmake.in @@ -34,9 +34,6 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION "_server_manager_data\\.h.*modernize-deprecated-headers" "note: make as 'inline'" - # CMake-generated sources. (cmake/cmake!6352) - "UnitTests_smtk_" - # Warnings from the delaunay submodule. "thirdparty/delaunay" ) -- GitLab From a4013c944eeb1b970f1855f620bb60adc944406a Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 27 Jul 2021 14:20:08 -0400 Subject: [PATCH 08/52] Deprecation: fix 21.04 references --- smtk/common/Deprecation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smtk/common/Deprecation.h b/smtk/common/Deprecation.h index e53fd865a6..e88b0bbab8 100644 --- a/smtk/common/Deprecation.h +++ b/smtk/common/Deprecation.h @@ -19,8 +19,8 @@ #define SMTK_DEPRECATION_LEVEL SMTK_VERSION_NUMBER #endif -// API deprecated before 21.4 have already been removed. -#define SMTK_MINIMUM_DEPRECATION_LEVEL SMTK_VERSION_CHECK(21, 4) +// API deprecated before 21.04 have already been removed. +#define SMTK_MINIMUM_DEPRECATION_LEVEL SMTK_VERSION_CHECK(21, 04) // Force the deprecation level to be at least that of SMTK's build // configuration. -- GitLab From b51033a9283967be31492855bd977ae5b9358bbe Mon Sep 17 00:00:00 2001 From: David Thompson Date: Wed, 28 Jul 2021 15:50:24 -0400 Subject: [PATCH 09/52] Eliminate $O(n^2)$ behavior when removing resources. This should fix #443. --- doc/release/notes/view-badge-speed.rst | 7 +++ .../appcomponents/VisibilityBadge.cxx | 54 ++++++++++++++----- 2 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 doc/release/notes/view-badge-speed.rst diff --git a/doc/release/notes/view-badge-speed.rst b/doc/release/notes/view-badge-speed.rst new file mode 100644 index 0000000000..ca0eb87eae --- /dev/null +++ b/doc/release/notes/view-badge-speed.rst @@ -0,0 +1,7 @@ +Visibility badge improvements +============================= + +The ParaView visibility-badge extension had an issue when large numbers +of phrase-model instances existed and a resource was closed: the visibility +was updated by completely rebuilding the map of visible entities which +is slow. This is now fixed. diff --git a/smtk/extension/paraview/appcomponents/VisibilityBadge.cxx b/smtk/extension/paraview/appcomponents/VisibilityBadge.cxx index c98ee0c5d7..cc15326818 100644 --- a/smtk/extension/paraview/appcomponents/VisibilityBadge.cxx +++ b/smtk/extension/paraview/appcomponents/VisibilityBadge.cxx @@ -462,11 +462,11 @@ void VisibilityBadge::activeViewChanged(pqView* view) void VisibilityBadge::representationAddedToActiveView(pqRepresentation* rep) { - auto* modelRep = dynamic_cast(rep); - if (modelRep) + auto* smtkRep = dynamic_cast(rep); + if (smtkRep) { QObject::connect( - modelRep, + smtkRep, SIGNAL(componentVisibilityChanged(smtk::resource::ComponentPtr, bool)), this, SLOT(componentVisibilityChanged(smtk::resource::ComponentPtr, bool))); @@ -475,21 +475,49 @@ void VisibilityBadge::representationAddedToActiveView(pqRepresentation* rep) void VisibilityBadge::representationRemovedFromActiveView(pqRepresentation* rep) { - auto* modelRep = dynamic_cast(rep); - if (modelRep) + auto* smtkRep = dynamic_cast(rep); + if (smtkRep) { + auto* pipeline = dynamic_cast(smtkRep->getInput()); + if (pipeline) + { + auto resource = pipeline->getResource(); + // Ensure that when a representation is removed due to + // the resource being closed that we "forget" the visibility + // state of its components — otherwise, reloading the resource + // will result in inconsistent state. + if (!this->phraseModel()->root()) + { + return; + } + auto rsrcPhrases = this->phraseModel()->root()->subphrases(); + std::function updater = + [this, &resource, &updater](const smtk::view::DescriptivePhrase::Ptr& phrase) { + if (phrase && phrase->relatedResource() == resource) + { + m_visibleThings.erase(phrase->relatedObject()->id()); + } + if (phrase->areSubphrasesBuilt()) + { + for (const auto& child : phrase->subphrases()) + { + updater(child); + } + } + }; + for (const auto& rsrcPhrase : rsrcPhrases) + { + updater(rsrcPhrase); + } + // Indicate to the Qt model that it needs to refresh every row, + // since in theory visibility may be altered on each one: + this->phraseModel()->triggerDataChanged(); + } QObject::disconnect( - modelRep, + smtkRep, SIGNAL(componentVisibilityChanged(smtk::resource::ComponentPtr, bool)), this, SLOT(componentVisibilityChanged(smtk::resource::ComponentPtr, bool))); - // Now, call activeViewChanged() to reset m_visibleThings; - // this ensures that when a representation is removed due to - // the resource being closed that we "forget" the visibility - // state of its components — otherwise, reloading the resource - // will result in inconsistent state. - auto* view = pqActiveObjects::instance().activeView(); - this->activeViewChanged(view); } } -- GitLab From 5a34463ea6853fe1965d18036181a976a0385bb5 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 29 Jul 2021 11:30:16 -0400 Subject: [PATCH 10/52] Revert "CDash: disable submission to the private CDash for now" It should be back now. This reverts commit e95511ab2860470374ecb0f0af962b8ffbebb3df. --- CTestConfig.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CTestConfig.cmake b/CTestConfig.cmake index 1dcdb9b5fd..f44a82eda3 100644 --- a/CTestConfig.cmake +++ b/CTestConfig.cmake @@ -7,8 +7,7 @@ set(CTEST_PROJECT_NAME "SuperBuild-ConceptualModelBuilder") set(CTEST_NIGHTLY_START_TIME "21:00:00 EDT") -#set(drop_sites kitware) -set(drop_sites) +set(drop_sites kitware) set(CTEST_DROP_METHOD_kitware "https") set(CTEST_DROP_SITE_kitware "www.kitware.com/CDash") -- GitLab From 37103f038a62a657e3988e54a73254865e01f278 Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Mon, 2 Aug 2021 17:53:36 -0400 Subject: [PATCH 11/52] Mark void items used in analysis views to disable automatic scrolling --- smtk/extension/qt/qtAnalysisView.cxx | 21 +++++++++++++++++++++ smtk/extension/qt/qtVoidItem.cxx | 9 +++++++++ 2 files changed, 30 insertions(+) diff --git a/smtk/extension/qt/qtAnalysisView.cxx b/smtk/extension/qt/qtAnalysisView.cxx index 55218e3259..a3f2f52995 100644 --- a/smtk/extension/qt/qtAnalysisView.cxx +++ b/smtk/extension/qt/qtAnalysisView.cxx @@ -12,11 +12,13 @@ #include "smtk/attribute/Attribute.h" #include "smtk/attribute/GroupItem.h" +#include "smtk/attribute/Item.h" #include "smtk/attribute/StringItem.h" #include "smtk/extension/qt/qtAttribute.h" #include "smtk/extension/qt/qtUIManager.h" #include "smtk/io/AttributeWriter.h" #include "smtk/io/Logger.h" +#include "smtk/simulation/UserData.h" #include "smtk/view/Configuration.h" @@ -33,6 +35,7 @@ #include #include +#include using namespace smtk::attribute; using namespace smtk::extension; @@ -94,6 +97,24 @@ void qtAnalysisView::createWidget() attChanged = true; } +#if !defined(__APPLE__) + // This is a workaround for an unwanted scrolling behavoir that has been observed + // on Windows and linux builds. More details can be found at + // https://gitlab.kitware.com/cmb/smtk/-/issues/442 + // The following code marks all void items in the analyis attribute with a UserData + // instance that is read by qtVoidItem. This is only done for analysis views. + std::vector items; + auto filter = [](smtk::attribute::Item::Ptr item) { + return item->type() == smtk::attribute::Item::VoidType; + }; + m_analysisAttribute->filterItems(items, filter, false); + auto uData = smtk::simulation::UserDataInt::New(); + for (auto& item : items) + { + item->setUserData("smtk.extensions.void_item.no_focus", uData); + } +#endif + // OK Now lets create a qtAttribute for the Analysis Attribute int labelWidth = this->uiManager()->getWidthOfAttributeMaxLabel(attDef, this->uiManager()->advancedFont()); diff --git a/smtk/extension/qt/qtVoidItem.cxx b/smtk/extension/qt/qtVoidItem.cxx index 5c09cdd1b3..4cb1183cea 100644 --- a/smtk/extension/qt/qtVoidItem.cxx +++ b/smtk/extension/qt/qtVoidItem.cxx @@ -20,6 +20,7 @@ #include "smtk/attribute/VoidItem.h" #include "smtk/attribute/VoidItemDefinition.h" +#include "smtk/simulation/UserData.h" using namespace smtk::extension; @@ -86,6 +87,14 @@ void qtVoidItem::createWidget() if (dataObj->isOptional()) { QCheckBox* optionalCheck = new QCheckBox(m_widget); + + // Check for "no_focus" user data + auto udata = dataObj->userData("smtk.extensions.void_item.no_focus"); + if (udata != nullptr) + { + optionalCheck->setFocusPolicy(Qt::NoFocus); + } + optionalCheck->setChecked(dataObj->definition()->isEnabledByDefault()); optionalCheck->setSizePolicy(sizeFixedPolicy); -- GitLab From 2e35c748e090b52c7369081e149b32f7e097dda3 Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Sat, 7 Aug 2021 15:22:54 -0400 Subject: [PATCH 12/52] SP: Updating ReadMe * Needed to refer to the 21.07 release notes * Also added an instruction in the new release template to do so. --- .gitlab/issue_templates/new-release.md | 1 + ReadMe.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab/issue_templates/new-release.md b/.gitlab/issue_templates/new-release.md index c0865d89cc..9d7e38e0d5 100644 --- a/.gitlab/issue_templates/new-release.md +++ b/.gitlab/issue_templates/new-release.md @@ -32,6 +32,7 @@ Please remove this comment. - Make a commit for each of these `release`-only changes on a single topic (suggested branch name: `update-to-vVERSION`): - [ ] Assemble release notes into `doc/release/notes/smtk-MAJOR.MINOR.rst`. + - [ ] Update the ReadMe file to refer to the new release notes - [ ] If `PATCH` is greater than 0, add items to the end of this file. - [ ] Update `version.txt` and tag the commit (tag this commit below) - [ ] `git checkout -b update-to-vVERSION BRANCHPOINT` diff --git a/ReadMe.md b/ReadMe.md index 5383954b5c..6c0cd04766 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -62,7 +62,7 @@ See [CONTRIBUTING.md][] for instructions to contribute. Latest Release Notes ==================== -Can be found [here](doc/release/smtk-21.05.rst). +Can be found [here](doc/release/smtk-21.07.rst). License ======= -- GitLab From 93d445ae8a67a72a864ede643ef31502741f8de3 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Mon, 9 Aug 2021 08:18:37 -0400 Subject: [PATCH 13/52] gitlab-ci: do not make tests wait for half an hour on branches --- .gitlab-ci.yml | 24 ++++++++++++------------ .gitlab/rules.yml | 8 ++++++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5a092893ae..8dbdd489c4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -47,7 +47,7 @@ test:fedora33: - .cmake_test_linux - .cmake_test_artifacts - .linux_test_tags - - .run_automatically + - .run_dependent dependencies: - build:fedora33 needs: @@ -67,7 +67,7 @@ test:fedora33-vtk-python3: - .cmake_test_linux - .cmake_test_artifacts - .linux_test_tags - - .run_automatically + - .run_dependent dependencies: - build:fedora33-vtk-python3 needs: @@ -87,7 +87,7 @@ test:fedora33-paraview: - .cmake_test_linux - .cmake_test_artifacts - .linux_test_tags - - .run_automatically + - .run_dependent dependencies: - build:fedora33-paraview needs: @@ -107,7 +107,7 @@ test:fedora33-nodata: - .cmake_test_linux - .cmake_test_artifacts - .linux_test_tags - - .run_automatically + - .run_dependent dependencies: - build:fedora33-nodata needs: @@ -129,7 +129,7 @@ test:fedora33-asan: - .cmake_memcheck_linux - .cmake_test_artifacts - .linux_test_priv_tags - - .run_automatically + - .run_dependent dependencies: - build:fedora33-asan needs: @@ -149,7 +149,7 @@ test:fedora33-ubsan: - .cmake_memcheck_linux - .cmake_test_artifacts - .linux_test_priv_tags - - .run_automatically + - .run_dependent dependencies: - build:fedora33-ubsan needs: @@ -169,7 +169,7 @@ build:fedora33-coverage: - .cmake_build_linux - .cmake_build_artifacts - .linux_builder_tags - - .run_automatically + - .run_dependent test:fedora33-coverage: extends: @@ -177,7 +177,7 @@ test:fedora33-coverage: - .cmake_test_linux - .linux_test_tags - .cmake_coverage_artifacts - - .run_automatically + - .run_dependent dependencies: - build:fedora33-coverage needs: @@ -188,7 +188,7 @@ analyze:fedora33-coverage: - .fedora33_coverage - .cmake_coverage_linux - .linux_builder_tags - - .run_automatically + - .run_dependent dependencies: - test:fedora33-coverage needs: @@ -212,7 +212,7 @@ test:macos-arm64: - .cmake_test_macos - .cmake_test_artifacts - .macos_arm64_builder_tags - - .run_automatically + - .run_dependent dependencies: - build:macos-arm64 needs: @@ -232,7 +232,7 @@ test:macos-x86_64: - .cmake_test_macos - .cmake_test_artifacts - .macos_builder_tags - - .run_automatically + - .run_dependent dependencies: - build:macos-x86_64 needs: @@ -256,7 +256,7 @@ test:windows-vs2019-ninja: - .cmake_test_windows - .cmake_test_artifacts - .windows_builder_tags - - .run_automatically + - .run_dependent dependencies: - build:windows-vs2019-ninja needs: diff --git a/.gitlab/rules.yml b/.gitlab/rules.yml index 6c7db2fbd4..76f23103b9 100644 --- a/.gitlab/rules.yml +++ b/.gitlab/rules.yml @@ -8,3 +8,11 @@ when: delayed start_in: 30 minutes - when: never + +.run_dependent: + rules: + - if: '$CI_MERGE_REQUEST_ID' + when: on_success + - if: '$CI_PROJECT_PATH == "cmb/smtk"' + when: on_success + - when: never -- GitLab From 7b75da5e0360b791c9a0bc569bf2647892c01adf Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Mon, 9 Aug 2021 21:07:33 -0400 Subject: [PATCH 14/52] Take out reference in polygon session to vtk session headers --- smtk/session/polygon/CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/smtk/session/polygon/CMakeLists.txt b/smtk/session/polygon/CMakeLists.txt index c64eb60b52..17d28a86cb 100644 --- a/smtk/session/polygon/CMakeLists.txt +++ b/smtk/session/polygon/CMakeLists.txt @@ -134,12 +134,7 @@ smtk_install_library(smtkPolygonSession) # Install the headers smtk_public_headers(smtkPolygonSession ${polygonHeaders}) - install(FILES PointerDefs.h DESTINATION include/smtk/${SMTK_VERSION}/smtk/session/polygon) -target_include_directories(smtkPolygonSession - PUBLIC - $ -) if (SMTK_ENABLE_VTK_SUPPORT) set(module_files -- GitLab From e2181fa079a6165ff2d10e623b455c025d40f2df Mon Sep 17 00:00:00 2001 From: David Thompson Date: Fri, 6 Aug 2021 17:19:12 -0400 Subject: [PATCH 15/52] Task serialization. Provide a framework to serialize tasks as JSON. This also renames existing task tests to be less confusing. --- doc/release/notes/task-json.rst | 6 + doc/userguide/task/io.rst | 28 ++++ smtk/common/Instances.h | 3 + smtk/task/CMakeLists.txt | 8 + smtk/task/Registrar.cxx | 6 + smtk/task/TaskNeedsResources.cxx | 16 ++ smtk/task/TaskNeedsResources.h | 6 + smtk/task/json/Helper.cxx | 150 +++++++++++++++++ smtk/task/json/Helper.h | 153 ++++++++++++++++++ smtk/task/json/jsonManager.cxx | 110 +++++++++++++ smtk/task/json/jsonManager.h | 53 ++++++ smtk/task/json/jsonTask.cxx | 73 +++++++++ smtk/task/json/jsonTask.h | 43 +++++ smtk/task/json/jsonTaskNeedsResources.cxx | 74 +++++++++ smtk/task/json/jsonTaskNeedsResources.h | 36 +++++ smtk/task/testing/cxx/CMakeLists.txt | 5 +- .../{TestActive.cxx => TestActiveTask.cxx} | 2 +- .../cxx/{TestTask.cxx => TestTaskBasics.cxx} | 2 +- smtk/task/testing/cxx/TestTaskJSON.cxx | 136 ++++++++++++++++ 19 files changed, 906 insertions(+), 4 deletions(-) create mode 100644 doc/release/notes/task-json.rst create mode 100644 doc/userguide/task/io.rst create mode 100644 smtk/task/json/Helper.cxx create mode 100644 smtk/task/json/Helper.h create mode 100644 smtk/task/json/jsonManager.cxx create mode 100644 smtk/task/json/jsonManager.h create mode 100644 smtk/task/json/jsonTask.cxx create mode 100644 smtk/task/json/jsonTask.h create mode 100644 smtk/task/json/jsonTaskNeedsResources.cxx create mode 100644 smtk/task/json/jsonTaskNeedsResources.h rename smtk/task/testing/cxx/{TestActive.cxx => TestActiveTask.cxx} (99%) rename smtk/task/testing/cxx/{TestTask.cxx => TestTaskBasics.cxx} (99%) create mode 100644 smtk/task/testing/cxx/TestTaskJSON.cxx diff --git a/doc/release/notes/task-json.rst b/doc/release/notes/task-json.rst new file mode 100644 index 0000000000..c4d824950d --- /dev/null +++ b/doc/release/notes/task-json.rst @@ -0,0 +1,6 @@ +Task serialization/deserialization +================================== + +The task classes and task manager now support serialization +and deserialization (to/from JSON). See the TestTaskJSON +test and user guide for more details. diff --git a/doc/userguide/task/io.rst b/doc/userguide/task/io.rst new file mode 100644 index 0000000000..b0bba9cc42 --- /dev/null +++ b/doc/userguide/task/io.rst @@ -0,0 +1,28 @@ +.. _smtk-task-io: + +Serialization and deserialization +================================= + +Tasks are created by passing JSON configuration data; +this constitutes the majority of deserialization. +However, beyond configuration, deserialization involves +connecting tasks via dependencies and potentially other +information – such as functors and references to SMTK's +various managers – that cannot be simply encoded into JSON. + +For this reason, the task manager may be serialized and +deserialized. It uses a :smtk:`helper ` +to swizzle_ pointers to tasks +into integer indices during serialization and adds a phase +during deserialization that un-swizzles the integers back +into pointers to the objects it created. + +Serialization also requires a functor for each class that +examines a task and returns a JSON object with its +full serialization. +See :smtk:`smtk::task::json::jsonTask` for an example +and note that subclasses of task should invoke the +functor for their superclass rather than trying to +reproduce its logic. + +.. _swizzle: https://en.wikipedia.org/wiki/Pointer_swizzling diff --git a/smtk/common/Instances.h b/smtk/common/Instances.h index 3932bf5696..0ab0562970 100644 --- a/smtk/common/Instances.h +++ b/smtk/common/Instances.h @@ -188,6 +188,9 @@ public: return smtk::common::Visit::Continue; } + /// Return the number of instances being managed. + std::size_t size() const { return m_instances.size(); } + private: /// The container that owns managed instances. using Container = std::set>; diff --git a/smtk/task/CMakeLists.txt b/smtk/task/CMakeLists.txt index f6096393c2..641199c0cb 100644 --- a/smtk/task/CMakeLists.txt +++ b/smtk/task/CMakeLists.txt @@ -4,6 +4,10 @@ set(taskSrcs Registrar.cxx Task.cxx TaskNeedsResources.cxx + json/Helper.cxx + json/jsonManager.cxx + json/jsonTask.cxx + json/jsonTaskNeedsResources.cxx ) set(taskHeaders @@ -13,6 +17,10 @@ set(taskHeaders Registrar.h Task.h TaskNeedsResources.h + json/Helper.h + json/jsonManager.h + json/jsonTask.h + json/jsonTaskNeedsResources.h ) if (SMTK_ENABLE_PYTHON_WRAPPING) diff --git a/smtk/task/Registrar.cxx b/smtk/task/Registrar.cxx index 7786ea78f6..5dc54b0ae1 100644 --- a/smtk/task/Registrar.cxx +++ b/smtk/task/Registrar.cxx @@ -12,6 +12,9 @@ #include "smtk/task/Registrar.h" #include "smtk/task/Task.h" #include "smtk/task/TaskNeedsResources.h" +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonTask.h" +#include "smtk/task/json/jsonTaskNeedsResources.h" #include "smtk/plugin/Manager.h" @@ -23,6 +26,7 @@ namespace task { using TaskList = std::tuple; +using JSONList = std::tuple; void Registrar::registerTo(const smtk::common::Managers::Ptr& managers) { @@ -53,12 +57,14 @@ void Registrar::registerTo(const smtk::task::Manager::Ptr& taskManager) { auto& instances = taskManager->instances(); instances.registerTypes(); + json::Helper::registerTypes(); } void Registrar::unregisterFrom(const smtk::task::Manager::Ptr& taskManager) { auto& instances = taskManager->instances(); instances.unregisterTypes(); + json::Helper::unregisterTypes(); } } // namespace task diff --git a/smtk/task/TaskNeedsResources.cxx b/smtk/task/TaskNeedsResources.cxx index 4310d8d484..3104fc386e 100644 --- a/smtk/task/TaskNeedsResources.cxx +++ b/smtk/task/TaskNeedsResources.cxx @@ -140,6 +140,22 @@ void TaskNeedsResources::configure(const Configuration& config) } } +smtk::common::Visit TaskNeedsResources::visitPredicates(PredicateVisitor visitor) +{ + if (!visitor) + { + return smtk::common::Visit::Halt; + } + for (const auto& entry : m_resourcesByRole) + { + if (visitor(entry.second) == smtk::common::Visit::Halt) + { + return smtk::common::Visit::Halt; + } + } + return smtk::common::Visit::Continue; +} + void TaskNeedsResources::updateResources( smtk::resource::Resource& resource, smtk::resource::EventType event) diff --git a/smtk/task/TaskNeedsResources.h b/smtk/task/TaskNeedsResources.h index 06d261101e..6aa57b11ad 100644 --- a/smtk/task/TaskNeedsResources.h +++ b/smtk/task/TaskNeedsResources.h @@ -15,6 +15,8 @@ #include "smtk/resource/Resource.h" #include "smtk/task/Task.h" +#include "smtk/common/Visit.h" + namespace smtk { namespace task @@ -56,6 +58,8 @@ public: /// The set of resources being managed that are selected by the validator. std::set> m_resources; }; + /// Signature of functors that visit resources-by-role predicates. + using PredicateVisitor = std::function; TaskNeedsResources(); TaskNeedsResources( @@ -70,6 +74,8 @@ public: void configure(const Configuration& config); + smtk::common::Visit visitPredicates(PredicateVisitor visitor); + protected: /// Respond to resource changes that may change task state. void updateResources(smtk::resource::Resource& resource, smtk::resource::EventType event); diff --git a/smtk/task/json/Helper.cxx b/smtk/task/json/Helper.cxx new file mode 100644 index 0000000000..438d09dd2d --- /dev/null +++ b/smtk/task/json/Helper.cxx @@ -0,0 +1,150 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/json/Helper.h" + +#include "smtk/io/Logger.h" + +#include + +namespace +{ +std::mutex g_types; +thread_local std::unique_ptr g_instance; +} // anonymous namespace + +namespace smtk +{ +namespace task +{ +namespace json +{ +std::unordered_map Helper::s_types; + +Helper::Helper() = default; + +Helper::~Helper() = default; + +bool Helper::registerType(const std::string& typeName, ConfigurationHelper helper) +{ + std::cout << "Register " << typeName << "\n"; + std::lock_guard lock(g_types); + return s_types.insert({ typeName, helper }).second; +} + +bool Helper::unregisterType(const std::string& typeName) +{ + std::lock_guard lock(g_types); + return s_types.erase(typeName) > 0; +} + +Helper& Helper::instance() +{ + if (!g_instance) + { + g_instance = std::unique_ptr(new Helper); + } + return *g_instance; +} + +void Helper::setManagers(const smtk::common::Managers::Ptr& managers) +{ + m_managers = managers; +} + +smtk::common::Managers::Ptr Helper::managers() +{ + return m_managers; +} + +Task::Configuration Helper::configuration(const Task* task) +{ + Task::Configuration config; + if (task) + { + auto typeName = task->typeName(); + ConfigurationHelper taskHelper = nullptr; + HelperTypeMap::const_iterator it; + { + std::lock_guard lock(g_types); + it = s_types.find(typeName); + if (it == s_types.end()) + { + return config; + } + taskHelper = it->second; + } + this->swizzleId(task); // Assign a task ID as early as possible. + config = taskHelper(task, *this); + } + return config; +} + +void Helper::clear() +{ + m_swizzleFwd.clear(); + m_swizzleBck.clear(); + m_nextSwizzle = 1; +} + +std::size_t Helper::swizzleId(const Task* task) +{ + if (!task) + { + return 0; + } + auto* nctask = const_cast(task); // Need a non-const Task in some cases. + const auto& it = m_swizzleFwd.find(nctask); + if (it != m_swizzleFwd.end()) + { + return it->second; + } + std::size_t id = m_nextSwizzle++; + m_swizzleFwd[nctask] = id; + m_swizzleBck[id] = nctask; + return id; +} + +Helper::json Helper::swizzleDependencies(const Task::PassedDependencies& deps) +{ + auto ids = Helper::json::array({}); + for (const auto& dep : deps) + { + if (dep) + { + std::size_t id = this->swizzleId(const_cast(dep.get())); + if (id) + { + ids.push_back(id); + } + } + } + return ids; +} + +Task::PassedDependencies Helper::unswizzleDependencies(const json& ids) const +{ + Task::PassedDependencies deps; + for (const auto& id : ids) + { + auto taskId = id.get(); + auto it = m_swizzleBck.find(taskId); + if (it == m_swizzleBck.end() || !it->second) + { + smtkWarningMacro( + smtk::io::Logger::instance(), "No task or null task for ID " << taskId << ". Skipping."); + } + deps.insert(it->second->shared_from_this()); + } + return deps; +} + +} // namespace json +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/Helper.h b/smtk/task/json/Helper.h new file mode 100644 index 0000000000..6600347cd9 --- /dev/null +++ b/smtk/task/json/Helper.h @@ -0,0 +1,153 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_Helper_h +#define smtk_task_json_Helper_h + +#include "smtk/task/Task.h" + +#include "smtk/common/Managers.h" +#include "smtk/common/TypeName.h" + +#include +#include + +namespace smtk +{ +namespace task +{ +namespace json +{ + +/// A helper for serializing task configurations. +/// +/// This is needed in order to serialized dependencies among tasks which +/// are stored as pointers that could, in theory, form a cycle. +class SMTKCORE_EXPORT Helper +{ +public: + /// Methods that can produce a configuration for a task have this signature. + using ConfigurationHelper = std::function; + /// How ConfigurationHelper functions are stored. + /// + /// Keys are task class-names; values are functors that produce a JSON + /// object given a task of that type. + using HelperTypeMap = std::unordered_map; + /// JSON data type + using json = nlohmann::json; + + /// Destructor is public, but you shouldn't use it. + ~Helper(); + + ///@{ + /// Methods used in registrars to register/unregister types. + /// + /// These are piggybacked onto the task-manager instance registration (i.e., + /// called within the Registrar's method accepting an smtk::task::Manager), + /// so a Schwarz counter is not required to ensure these are only called + /// when needed. See smtk::task::Registrar for an example of how to use + /// these methods. + /// + /// Also, because serialization and deserialization are inherently a + /// run-time activity, we don't make an attempt at compile-time type-safety. + template + static bool registerTypes() + { + static_assert( + std::tuple_size::value == std::tuple_size::value, + "Class and helper tuples must be of same length."); + return registerTypes<0, ClassList, HelperList>(); + } + template + static bool unregisterTypes() + { + return unregisterTypes<0, ClassList>(); + } + + template + static typename std::enable_if::value, bool>::type registerTypes() + { + auto typeName = smtk::common::typeName::type>(); + using HelperType = + typename std::tuple_element::type; // smtk::task::json::jsonTask; + HelperType helper; + bool registered = Helper::registerType(typeName, helper); + return registered && registerTypes(); + } + template + static typename std::enable_if::value, bool>::type registerTypes() + { + return true; + } + + template + static typename std::enable_if::value, bool>::type + unregisterTypes() + { + auto typeName = smtk::common::typeName::type>(); + bool unregistered = Helper::unregisterType(typeName); + return unregistered && unregisterTypes(); + } + template + static typename std::enable_if::value, bool>::type + unregisterTypes() + { + return true; + } + ///@} + + static bool registerType(const std::string& typeName, ConfigurationHelper helper); + static bool unregisterType(const std::string& typeName); + + /// Return the helper singleton. + static Helper& instance(); + + /// Set/get the managers to use when serializing/deserializing. + /// + /// Call setManagers() with an instance of all your application's + /// managers before attempting to serialize/deserialize as helpers + /// are allowed to use managers as needed. + void setManagers(const smtk::common::Managers::Ptr& managers); + smtk::common::Managers::Ptr managers(); + + /// Return json configuration for the given task using registered helpers. + Task::Configuration configuration(const Task*); + + /// Reset the helper's state. + /// + /// This should be called before beginning serialization or deserialization. + /// Additionally, calling it after each of these tasks is recommended since + /// it will free memory. + void clear(); + + /// Return the ID of a task as computed by the swizzler. + /// This will allocate a new ID if none exists. + std::size_t swizzleId(const Task* task); + + /// Return a serialization of task-references that is consistent within + /// the scope of serializing a set of tasks. + json swizzleDependencies(const Task::PassedDependencies& deps); + + /// Return a deserialization of de-swizzled task-references. + Task::PassedDependencies unswizzleDependencies(const json& ids) const; + +protected: + Helper(); + smtk::common::Managers::Ptr m_managers; + std::unordered_map m_swizzleFwd; + std::unordered_map m_swizzleBck; + std::size_t m_nextSwizzle = 1; + static HelperTypeMap s_types; +}; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_Helper_h diff --git a/smtk/task/json/jsonManager.cxx b/smtk/task/json/jsonManager.cxx new file mode 100644 index 0000000000..304ac136f9 --- /dev/null +++ b/smtk/task/json/jsonManager.cxx @@ -0,0 +1,110 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/json/jsonManager.h" +#include "smtk/task/Manager.h" +#include "smtk/task/Task.h" +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonTask.h" + +#include "smtk/io/Logger.h" + +#include "nlohmann/json.hpp" + +#include +#include + +namespace smtk +{ +namespace task +{ +namespace json +{ + +bool jsonManager::serialize( + const std::shared_ptr& managers, + nlohmann::json& json) +{ + auto taskManager = managers->get(); + if (!taskManager) + { + // Should we succeed silently instead of failing verbosely? + smtkErrorMacro( + smtk::io::Logger::instance(), + "Could not find a task manager to serialize. " + "Add a task manager to the managers instance."); + return false; + } + + // Serialize tasks + nlohmann::json::array_t taskList; + taskManager->instances().visit([&taskList](const smtk::task::Task::Ptr& task) { + nlohmann::json jsonTask = task; + taskList.push_back(jsonTask); + return smtk::common::Visit::Continue; + }); + json["tasks"] = taskList; + return true; +} + +bool jsonManager::deserialize( + const std::shared_ptr& managers, + const nlohmann::json& json) +{ + auto taskManager = managers->get(); + if (!taskManager) + { + // Should we succeed silently instead of failing verbosely? + smtkErrorMacro(smtk::io::Logger::instance(), "Could not find a destination task manager."); + return false; + } + + try + { + auto& helper = Helper::instance(); + helper.clear(); + helper.setManagers(managers); + std::map taskMap; + for (const auto& jsonTask : json.at("tasks")) + { + auto taskId = jsonTask.at("id").get(); + Task::Ptr task = jsonTask; + taskMap[taskId] = task; + helper.swizzleId(task.get()); + } + // Do a second pass to deserialize dependencies. + for (const auto& jsonTask : json.at("tasks")) + { + if (jsonTask.contains("dependencies")) + { + auto taskId = jsonTask.at("id").get(); + auto task = taskMap[taskId]; + auto taskDeps = helper.unswizzleDependencies(jsonTask.at("dependencies")); + task->addDependencies(taskDeps); + } + } + // Now configure dependent tasks with adaptors if specified. + for (const auto& jsonAdaptor : json.at("task-adaptors")) + { + std::cout << "Adaptor " << jsonAdaptor.at("type") << "\n"; + } + + helper.clear(); + } + catch (std::exception& e) + { + smtkErrorMacro(smtk::io::Logger::instance(), "Could not deserialize: " << e.what() << "."); + return false; + } + return true; +} + +} // namespace json +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/jsonManager.h b/smtk/task/json/jsonManager.h new file mode 100644 index 0000000000..5fe08599ca --- /dev/null +++ b/smtk/task/json/jsonManager.h @@ -0,0 +1,53 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_Manager_h +#define smtk_task_json_Manager_h + +#include "smtk/task/Task.h" + +#include "smtk/common/Managers.h" + +#include "nlohmann/json.hpp" + +namespace smtk +{ +namespace task +{ +namespace json +{ + +/// Tools for saving and restoring the state of a task manager. +class SMTKCORE_EXPORT jsonManager +{ +public: + /// Serialize the task manager. + /// + /// Obviously, \a managers must hold a task manager before you + /// call this method. Depending on the task instances it holds, + /// other managers may be required. + static bool serialize( + const std::shared_ptr& managers, + nlohmann::json& json); + /// Deserialize the task manager. + /// + /// Obviously, \a managers must hold or be able to create a + /// task manager. Depending on the task instances being deserialized, + /// this method may access and modify other managers held by the + /// \a managers instance. + static bool deserialize( + const std::shared_ptr& managers, + const nlohmann::json& json); +}; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_Manager_h diff --git a/smtk/task/json/jsonTask.cxx b/smtk/task/json/jsonTask.cxx new file mode 100644 index 0000000000..9cd4db527d --- /dev/null +++ b/smtk/task/json/jsonTask.cxx @@ -0,0 +1,73 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/json/jsonTask.h" +#include "smtk/task/json/Helper.h" + +#include "smtk/task/Manager.h" + +#include "smtk/io/Logger.h" + +namespace smtk +{ +namespace task +{ +namespace json +{ + +Task::Configuration jsonTask::operator()(const Task* task, Helper& helper) const +{ + Task::Configuration config; + if (task) + { + config["id"] = helper.swizzleId(task); + config["type"] = task->typeName(); + config["title"] = task->title(); + config["state"] = stateName(task->internalState()); + auto deps = helper.swizzleDependencies(task->dependencies()); + if (!deps.empty()) + { + config["dependencies"] = deps; + } + } + return config; +} + +} // namespace json + +void to_json(nlohmann::json& j, const smtk::task::Task::Ptr& task) +{ + if (!task) + { + return; + } + auto& helper = json::Helper::instance(); + j = helper.configuration(task.get()); +} + +void from_json(const nlohmann::json& j, smtk::task::Task::Ptr& task) +{ + try + { + auto& helper = json::Helper::instance(); + auto managers = helper.managers(); + auto taskManager = managers->get>(); + auto taskType = j.at("type").get(); + task = + taskManager->instances().createFromName(taskType, const_cast(j), managers); + } + catch (std::exception& e) + { + smtkErrorMacro( + smtk::io::Logger::instance(), "Could not deserialize task (" << e.what() << ")."); + } +} + +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/jsonTask.h b/smtk/task/json/jsonTask.h new file mode 100644 index 0000000000..483b9c6cfd --- /dev/null +++ b/smtk/task/json/jsonTask.h @@ -0,0 +1,43 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_Task_h +#define smtk_task_json_Task_h + +#include "nlohmann/json.hpp" + +#include "smtk/task/Task.h" + +#include +#include + +namespace smtk +{ +namespace task +{ +namespace json +{ + +class Helper; + +struct SMTKCORE_EXPORT jsonTask +{ + Task::Configuration operator()(const Task* task, Helper& helper) const; +}; + +} // namespace json + +SMTKCORE_EXPORT void to_json(nlohmann::json& j, const smtk::task::Task::Ptr& task); + +SMTKCORE_EXPORT void from_json(const nlohmann::json& j, smtk::task::Task::Ptr& task); + +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_Task_h diff --git a/smtk/task/json/jsonTaskNeedsResources.cxx b/smtk/task/json/jsonTaskNeedsResources.cxx new file mode 100644 index 0000000000..4720319e4b --- /dev/null +++ b/smtk/task/json/jsonTaskNeedsResources.cxx @@ -0,0 +1,74 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/json/jsonTaskNeedsResources.h" +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonTask.h" + +#include "smtk/task/TaskNeedsResources.h" + +namespace smtk +{ +namespace task +{ +namespace json +{ + +Task::Configuration jsonTaskNeedsResources::operator()(const Task* task, Helper& helper) const +{ + Task::Configuration config; + auto* nctask = const_cast(task); + auto* taskNeedsResources = dynamic_cast(nctask); + if (taskNeedsResources) + { + jsonTask superclass; + config = superclass(taskNeedsResources, helper); + nlohmann::json::array_t predicates; + nlohmann::json::array_t outputs; + taskNeedsResources->visitPredicates( + [&predicates, &outputs, taskNeedsResources]( + const TaskNeedsResources::Predicate& predicate) -> smtk::common::Visit { + nlohmann::json jsonPredicate = { + { "role", predicate.m_role }, + { "type", predicate.m_type }, + }; + if (predicate.m_minimumCount != 1) + { + jsonPredicate["min"] = predicate.m_minimumCount; + } + if (predicate.m_maximumCount != -1) + { + jsonPredicate["max"] = predicate.m_maximumCount; + } + predicates.push_back(jsonPredicate); + + nlohmann::json jsonOutput = { + { "role", predicate.m_role }, + }; + nlohmann::json::array_t jsonResources; + for (const auto& weakResource : predicate.m_resources) + { + if (auto resource = weakResource.lock()) + { + jsonResources.push_back(resource->id().toString()); + } + } + jsonOutput["resources"] = jsonResources; + outputs.push_back(jsonOutput); + return smtk::common::Visit::Continue; + }); + config["resources"] = predicates; + config["output"] = outputs; + } + return config; +} + +} // namespace json +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/jsonTaskNeedsResources.h b/smtk/task/json/jsonTaskNeedsResources.h new file mode 100644 index 0000000000..a924d2794c --- /dev/null +++ b/smtk/task/json/jsonTaskNeedsResources.h @@ -0,0 +1,36 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_TaskNeedsResources_h +#define smtk_task_json_TaskNeedsResources_h + +#include "smtk/task/Task.h" + +#include +#include + +namespace smtk +{ +namespace task +{ +namespace json +{ + +class Helper; + +struct SMTKCORE_EXPORT jsonTaskNeedsResources +{ + Task::Configuration operator()(const Task* task, Helper& helper) const; +}; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_TaskNeedsResources_h diff --git a/smtk/task/testing/cxx/CMakeLists.txt b/smtk/task/testing/cxx/CMakeLists.txt index c2628128fd..d40f91337e 100644 --- a/smtk/task/testing/cxx/CMakeLists.txt +++ b/smtk/task/testing/cxx/CMakeLists.txt @@ -1,6 +1,7 @@ set(unit_tests - TestActive.cxx - TestTask.cxx + TestActiveTask.cxx + TestTaskBasics.cxx + TestTaskJSON.cxx ) find_package(Threads REQUIRED) diff --git a/smtk/task/testing/cxx/TestActive.cxx b/smtk/task/testing/cxx/TestActiveTask.cxx similarity index 99% rename from smtk/task/testing/cxx/TestActive.cxx rename to smtk/task/testing/cxx/TestActiveTask.cxx index 9cea1a72e3..cbe518620e 100644 --- a/smtk/task/testing/cxx/TestActive.cxx +++ b/smtk/task/testing/cxx/TestActiveTask.cxx @@ -31,7 +31,7 @@ using namespace smtk::task; } // namespace test_task -int TestActive(int, char*[]) +int TestActiveTask(int, char*[]) { using smtk::task::State; using smtk::task::Task; diff --git a/smtk/task/testing/cxx/TestTask.cxx b/smtk/task/testing/cxx/TestTaskBasics.cxx similarity index 99% rename from smtk/task/testing/cxx/TestTask.cxx rename to smtk/task/testing/cxx/TestTaskBasics.cxx index 04f0277c02..484bff6e4b 100644 --- a/smtk/task/testing/cxx/TestTask.cxx +++ b/smtk/task/testing/cxx/TestTaskBasics.cxx @@ -61,7 +61,7 @@ public: } // namespace test_task -int TestTask(int, char*[]) +int TestTaskBasics(int, char*[]) { using smtk::task::State; using smtk::task::Task; diff --git a/smtk/task/testing/cxx/TestTaskJSON.cxx b/smtk/task/testing/cxx/TestTaskJSON.cxx new file mode 100644 index 0000000000..809268ca31 --- /dev/null +++ b/smtk/task/testing/cxx/TestTaskJSON.cxx @@ -0,0 +1,136 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/attribute/Registrar.h" +#include "smtk/attribute/Resource.h" +#include "smtk/common/Managers.h" +#include "smtk/model/Registrar.h" +#include "smtk/model/Resource.h" +#include "smtk/operation/Manager.h" +#include "smtk/operation/Registrar.h" +#include "smtk/plugin/Registry.h" +#include "smtk/resource/Manager.h" +#include "smtk/resource/Registrar.h" +#include "smtk/task/Manager.h" +#include "smtk/task/Registrar.h" +#include "smtk/task/Task.h" +#include "smtk/task/TaskNeedsResources.h" + +#include "smtk/task/json/jsonManager.h" +#include "smtk/task/json/jsonTask.h" + +#include "smtk/common/testing/cxx/helpers.h" + +namespace test_task +{ + +using namespace smtk::task; + +} // namespace test_task + +int TestTaskJSON(int, char*[]) +{ + using smtk::task::State; + using smtk::task::Task; + + // Create managers + auto managers = smtk::common::Managers::create(); + auto attributeRegistry = smtk::plugin::addToManagers(managers); + auto resourceRegistry = smtk::plugin::addToManagers(managers); + auto operationRegistry = smtk::plugin::addToManagers(managers); + auto taskRegistry = smtk::plugin::addToManagers(managers); + + auto resourceManager = managers->get(); + auto operationManager = managers->get(); + auto taskManager = managers->get(); + + auto attributeResourceRegistry = + smtk::plugin::addToManagers(resourceManager); + auto modelRegistry = smtk::plugin::addToManagers(resourceManager); + auto taskTaskRegistry = smtk::plugin::addToManagers(taskManager); + + auto attrib = resourceManager->create(); + auto model = resourceManager->create(); + attrib->setName("simulation"); + model->setName("geometry"); + attrib->properties().get()["project_role"] = "simulation attribute"; + model->properties().get()["project_role"] = "model geometry"; + auto attribUUIDStr = attrib->id().toString(); + auto modelUUIDStr = model->id().toString(); + + std::string configString = R"({ + "tasks": [ + { + "id": 1, + "type": "smtk::task::TaskNeedsResources", + "title": "Load a model and attribute", + "state": "completed", + "resources": [ + { + "role": "model geometry", + "type": "smtk::model::Resource", + "max": 2 + }, + { + "role": "simulation attribute", + "type": "smtk::attribute::Resource" + } + ], + "output": [ + { "role": "model geometry", "resources": [ "feedface-0000-0000-0000-000000000000" ] }, + { "role": "simulation attribute", "resources": [ "deadbeef-0000-0000-0000-000000000000" ] } + ] + }, + { + "id": 2, + "type": "smtk::task::Task", + "title": "Do something", + "state": "incomplete", + "dependencies": [ 1 ] + } + ], + "//": [ + "For each dependency, we can store configuration information", + "for the object used to adapt one task's output into", + "configuration information for its dependent task. If no", + "configuration is present for a dependency, we use the", + "'Ignore' adaptor as a default – which never modifies the", + "depedent task's configuration." + ], + "task-adaptors": [ + { + "type": "smtk::task::adaptor::Ignore", + "from": 1, + "to": 2 + } + ] +} + )"; + auto cursor = configString.find("deadbeef", 0); + configString = configString.replace(cursor, attribUUIDStr.length(), attribUUIDStr); + cursor = configString.find("feedface", 0); + configString = configString.replace(cursor, modelUUIDStr.length(), modelUUIDStr); + + auto config = nlohmann::json::parse(configString); + std::cout << config.dump(2) << "\n"; + bool ok = smtk::task::json::jsonManager::deserialize(managers, config); + test(ok, "Failed to parse configuration."); + test(taskManager->instances().size() == 2, "Expected to deserialize 2 tasks."); + + // Round trip it. + nlohmann::json config2; + ok = smtk::task::json::jsonManager::serialize(managers, config2); + test(ok, "Failed to serialize task manager."); + std::cout << config2.dump(2) << "\n"; + + // TODO: Round trip a second time so we should be guaranteed to have + // two machine-(not-hand-)generated strings to compare. + + return 0; +} -- GitLab From 743a1b2d7d3d8bb349a37d30912338fe52d61583 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Tue, 10 Aug 2021 01:02:14 -0400 Subject: [PATCH 16/52] Disable sccache idle timeout for gitlab runners... ... on macos and windows. --- .gitlab/os-macos.yml | 2 ++ .gitlab/os-windows.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 7cb8f5030c..74711239a9 100644 --- a/.gitlab/os-macos.yml +++ b/.gitlab/os-macos.yml @@ -11,6 +11,8 @@ DEVELOPER_DIR: "/Applications/Xcode-12.4.app/Contents/Developer" # Avoid conflicting with other projects running on the same machine. SCCACHE_SERVER_PORT: 4231 + # Do not let the sccache server shut down due to a slow machine. + SCCACHE_IDLE_TIMEOUT: 0 ### Build and test diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index b65671aa00..a5b5923507 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml @@ -8,6 +8,8 @@ GIT_CLONE_PATH: "$CI_BUILDS_DIR\\cmb-ci-ext\\$CI_CONCURRENT_ID" # Avoid conflicting with other projects running on the same machine. SCCACHE_SERVER_PORT: 4231 + # Do not let the sccache server shut down due to a slow machine. + SCCACHE_IDLE_TIMEOUT: 0 ### Build and test -- GitLab From 4eaa7176528fd8217a22e1839114c4d7e454de4e Mon Sep 17 00:00:00 2001 From: David Thompson Date: Tue, 17 Aug 2021 10:05:41 -0400 Subject: [PATCH 17/52] Do not fail just because sccache is already running. --- .gitlab/os-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index a5b5923507..faae8be130 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml @@ -77,7 +77,7 @@ - *before_script_windows - Set-Item -Force -Path "env:PATH" -Value "$env:PATH;$env:SCCACHE_PATH" - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 - - sccache --start-server + - Invoke-Expression -Command "sccache --start-server" - sccache --show-stats - ctest -VV -S .gitlab/ci/ctest_configure.cmake - ctest -VV -S .gitlab/ci/ctest_build.cmake -- GitLab From 9dfdf175fc6a2889db183598d4d899cfa4fe0f5e Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 17 Aug 2021 11:31:03 -0400 Subject: [PATCH 18/52] gitlab-ci: use the CMB stack's sccache port 4231 ended up being documented as VTK-m's port. --- .gitlab/os-macos.yml | 2 +- .gitlab/os-windows.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 74711239a9..2074238dd1 100644 --- a/.gitlab/os-macos.yml +++ b/.gitlab/os-macos.yml @@ -10,7 +10,7 @@ # use so that different versions can be tested in a single pipeline. DEVELOPER_DIR: "/Applications/Xcode-12.4.app/Contents/Developer" # Avoid conflicting with other projects running on the same machine. - SCCACHE_SERVER_PORT: 4231 + SCCACHE_SERVER_PORT: 4228 # Do not let the sccache server shut down due to a slow machine. SCCACHE_IDLE_TIMEOUT: 0 diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index faae8be130..8250e5c67b 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml @@ -7,7 +7,7 @@ GIT_SUBMODULE_STRATEGY: recursive GIT_CLONE_PATH: "$CI_BUILDS_DIR\\cmb-ci-ext\\$CI_CONCURRENT_ID" # Avoid conflicting with other projects running on the same machine. - SCCACHE_SERVER_PORT: 4231 + SCCACHE_SERVER_PORT: 4228 # Do not let the sccache server shut down due to a slow machine. SCCACHE_IDLE_TIMEOUT: 0 -- GitLab From deb1d5892b0b9f299581d291b5a48f6794d89064 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Mon, 9 Aug 2021 09:53:34 -0400 Subject: [PATCH 19/52] Add a task for filling out attributes... ... that is complete when attributes of a given type are all valid. --- doc/userguide/task/classes.rst | 48 ++++ smtk/task/CMakeLists.txt | 4 + smtk/task/FillOutAttributes.cxx | 284 +++++++++++++++++++++++ smtk/task/FillOutAttributes.h | 105 +++++++++ smtk/task/Registrar.cxx | 8 +- smtk/task/json/Helper.cxx | 1 - smtk/task/json/jsonFillOutAttributes.cxx | 89 +++++++ smtk/task/json/jsonFillOutAttributes.h | 48 ++++ smtk/task/json/jsonManager.cxx | 9 +- smtk/task/testing/cxx/TestTaskJSON.cxx | 15 +- 10 files changed, 603 insertions(+), 8 deletions(-) create mode 100644 smtk/task/FillOutAttributes.cxx create mode 100644 smtk/task/FillOutAttributes.h create mode 100644 smtk/task/json/jsonFillOutAttributes.cxx create mode 100644 smtk/task/json/jsonFillOutAttributes.h diff --git a/doc/userguide/task/classes.rst b/doc/userguide/task/classes.rst index b574b380dc..dea4aa9110 100644 --- a/doc/userguide/task/classes.rst +++ b/doc/userguide/task/classes.rst @@ -71,3 +71,51 @@ Example: } ] } + +FillOutAttributes +----------------- + +The :smtk:`FillOutAttributes task ` +monitors operations for attribute resources with particular roles. +When an operation creates or modifies a matching resource, the +task checks whether all the attributes with matching definitions +are valid. If so, the task is Completable. If not, it is Incomplete. +It is Completable by default (i.e., if no matching resources +or attributes exist). + +This task accepts all the JSON configuration that the base Task class does, plus: + +* ``attribute-sets``: a JSON array of required attributes, organized by role. + Each array entry must be a JSON object holding: + * ``role``: an optional string holding an attribute-resource role name. + If omitted, any role is allowed. + * ``definitions``: a set of :smtk:`smtk::attribute::Definition` type-names + specifying which types of attributes to validate before allowing completion. + +Example: + +.. code:: json + + { + "type": "smtk::task::FillOutAttributes", + "title": "Assign materials and mesh sizing.", + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": ["SolidMaterial", "FluidMaterial"] + }, + { + "role": "meshing attribute", + "definitions": [ + "GlobalSizingParameters", + "FaceSize", + "EdgeSize" + ] + } + ] + } + +In the example above, you can see that two different attribute resources +(one for the simulation and one for a mesh generator) are specified with +different roles and the definitions that should be checked for resources +in those roles are different. diff --git a/smtk/task/CMakeLists.txt b/smtk/task/CMakeLists.txt index 641199c0cb..3ebbd84d8b 100644 --- a/smtk/task/CMakeLists.txt +++ b/smtk/task/CMakeLists.txt @@ -1,10 +1,12 @@ set(taskSrcs Active.cxx + FillOutAttributes.cxx Manager.cxx Registrar.cxx Task.cxx TaskNeedsResources.cxx json/Helper.cxx + json/jsonFillOutAttributes.cxx json/jsonManager.cxx json/jsonTask.cxx json/jsonTaskNeedsResources.cxx @@ -12,11 +14,13 @@ set(taskSrcs set(taskHeaders Active.h + FillOutAttributes.h Instances.h Manager.h Registrar.h Task.h TaskNeedsResources.h + json/jsonFillOutAttributes.h json/Helper.h json/jsonManager.h json/jsonTask.h diff --git a/smtk/task/FillOutAttributes.cxx b/smtk/task/FillOutAttributes.cxx new file mode 100644 index 0000000000..f1f8bc164a --- /dev/null +++ b/smtk/task/FillOutAttributes.cxx @@ -0,0 +1,284 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/FillOutAttributes.h" + +#include "smtk/project/ResourceContainer.h" + +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonFillOutAttributes.h" + +#include "smtk/operation/Manager.h" +#include "smtk/operation/SpecificationOps.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/Resource.h" + +#include "smtk/resource/Manager.h" + +#include + +namespace smtk +{ +namespace task +{ + +FillOutAttributes::FillOutAttributes() = default; + +FillOutAttributes::FillOutAttributes( + const Configuration& config, + const smtk::common::Managers::Ptr& managers) + : Task(config, managers) + , m_managers(managers) +{ + this->configure(config); +} + +FillOutAttributes::FillOutAttributes( + const Configuration& config, + const PassedDependencies& dependencies, + const smtk::common::Managers::Ptr& managers) + : Task(config, dependencies, managers) + , m_managers(managers) +{ + this->configure(config); +} + +void FillOutAttributes::configure(const Configuration& config) +{ + // The predicate from_json method needs the resource manager: + auto& helper = json::Helper::instance(); + helper.setManagers(m_managers); + + if (config.contains("attribute-sets")) + { + config.at("attribute-sets").get_to(m_attributeSets); + } + if (m_managers) + { + if (auto operationManager = m_managers->get()) + { + m_observer = operationManager->observers().insert( + [this]( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result) { return this->update(op, event, result); }, + /* priority */ 0, + /* initialize */ true, + "FillOutAttributes monitors operations for updates."); + } + } + if (!m_attributeSets.empty()) + { + this->initializeResources(); + this->internalStateChanged(this->computeInternalState()); + } +} + +smtk::common::Visit FillOutAttributes::visitAttributeSets(AttributeSetVisitor visitor) +{ + if (!visitor) + { + return smtk::common::Visit::Halt; + } + for (const auto& entry : m_attributeSets) + { + if (visitor(entry) == smtk::common::Visit::Halt) + { + return smtk::common::Visit::Halt; + } + } + return smtk::common::Visit::Continue; +} + +bool FillOutAttributes::initializeResources() +{ + bool foundResource = false; + if (m_attributeSets.empty()) + { + return foundResource; + } + if (auto resourceManager = m_managers->get()) + { + auto resources = resourceManager->find(); + for (const auto& resource : resources) + { + const std::string& role = smtk::project::detail::role(resource); + for (auto& attributeSet : m_attributeSets) + { + if ( + attributeSet.m_role.empty() || attributeSet.m_role == "*" || attributeSet.m_role == role) + { + foundResource = true; + auto it = attributeSet.m_resources.insert({ resource->id(), { {}, {} } }).first; + this->updateResourceEntry(*resource, attributeSet, it->second); + } + } + } + } + return foundResource; +} + +bool FillOutAttributes::updateResourceEntry( + smtk::attribute::Resource& resource, + const AttributeSet& predicate, + ResourceAttributes& entry) +{ + bool changesMade = false; + // I. Remove invalid entries for attributes that are valid or deleted. + std::set expunged; + std::set validated; + std::set invalidated; + for (const auto& invalidId : entry.m_invalid) + { + auto att = resource.findAttribute(invalidId); + if (att) + { + if (att->isValid()) // TODO: accept predicate override for categories? + { + validated.insert(invalidId); + } + } + else + { + expunged.insert(invalidId); + } + } + // II. Check valid attributes to see if they have been invalidated or expunged. + for (const auto& validId : entry.m_valid) + { + auto att = resource.findAttribute(validId); + if (att) + { + if (!att->isValid()) // TODO: accept predicate override for categories? + { + invalidated.insert(validId); + } + } + else + { + expunged.insert(validId); + } + } + // If the set of invalid attributes was changed, we need to re-run computeInternalState(). + changesMade |= !expunged.empty() || !validated.empty() || !invalidated.empty(); + for (const auto& id : validated) + { + entry.m_invalid.erase(id); + entry.m_valid.insert(id); + } + for (const auto& id : expunged) + { + entry.m_invalid.erase(id); + } + for (const auto& id : invalidated) + { + entry.m_invalid.insert(id); + entry.m_valid.erase(id); + } + // II. Check for newly-created attributes + std::vector attributes; + for (const auto& definition : predicate.m_definitions) + { + resource.findAttributes(definition, attributes); + for (const auto& attribute : attributes) + { + auto uid = attribute->id(); + if ( + (entry.m_invalid.find(uid) == entry.m_invalid.end()) && + (entry.m_valid.find(uid) == entry.m_valid.end())) + { + // We've found a new attribute. Classify it. + changesMade = true; + if (attribute->isValid()) // TODO: accept predicate override for categories? + { + entry.m_valid.insert(uid); + } + else + { + entry.m_invalid.insert(uid); + } + } + } + } + return changesMade; +} + +int FillOutAttributes::update( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result) +{ + (void)op; + bool predicatesUpdated = false; + switch (event) + { + case smtk::operation::EventType::DID_OPERATE: + { + auto mentionedResources = smtk::operation::extractResources(result); + + for (const auto& weakResource : mentionedResources) + { + auto resource = std::dynamic_pointer_cast(weakResource.lock()); + if (resource) + { + const std::string& role = smtk::project::detail::role(resource); + // Do we care about this resource? + for (auto& predicate : m_attributeSets) + { + auto it = predicate.m_resources.find(resource->id()); + bool doUpdate = false; + if (it != predicate.m_resources.end()) + { + doUpdate = true; + } + else if ( + predicate.m_role == role || predicate.m_role == "*" || predicate.m_role.empty()) + { + it = predicate.m_resources.insert({ resource->id(), { {}, {} } }).first; + doUpdate = true; + } + if (doUpdate) + { + predicatesUpdated |= this->updateResourceEntry(*resource, predicate, it->second); + } + } + } + } + } + break; + case smtk::operation::EventType::WILL_OPERATE: + break; + } + if (predicatesUpdated) + { + this->internalStateChanged(this->computeInternalState()); + } + return 0; +} + +State FillOutAttributes::computeInternalState() const +{ + State s = State::Completable; + for (const auto& predicate : m_attributeSets) + { + for (const auto& resourceEntry : predicate.m_resources) + { + if (!resourceEntry.second.m_invalid.empty()) + { + s = State::Incomplete; + return s; + } + } + } + return s; +} + +} // namespace task +} // namespace smtk diff --git a/smtk/task/FillOutAttributes.h b/smtk/task/FillOutAttributes.h new file mode 100644 index 0000000000..72a3712fc6 --- /dev/null +++ b/smtk/task/FillOutAttributes.h @@ -0,0 +1,105 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_FillOutAttributes_h +#define smtk_task_FillOutAttributes_h + +#include "smtk/operation/Manager.h" +#include "smtk/operation/Observer.h" +#include "smtk/operation/Operation.h" +#include "smtk/resource/Resource.h" +#include "smtk/task/Task.h" + +#include "smtk/common/Visit.h" + +namespace smtk +{ +namespace task +{ + +/**\brief FillOutAttributes is a task that is incomplete until specified + * attributes are valid. + * + * This task accepts an input attribute resource (configured by a predecessor + * task or specified via a role) and observe an operation manager for operations. + * After each operation, attributes with a definition are validated. + * If all attributes identify are valid, the task becomes completable. + * Otherwise, the task will remain (or become) incomplete. + */ +class SMTKCORE_EXPORT FillOutAttributes : public Task +{ +public: + smtkTypeMacro(smtk::task::FillOutAttributes); + smtkSuperclassMacro(smtk::task::Task); + smtkCreateMacro(smtk::task::Task); + + /// Per-resource sets of validated attributes + /// + /// We need to track attributes so incremental updates + /// can decide whether to change state. + struct ResourceAttributes + { + /// Attributes matching a definition that are validated. + std::set m_valid; + /// Attributes matching a definition that need attention. + std::set m_invalid; + }; + /// A predicate used to collect resources that fit a given role. + struct AttributeSet + { + /// The required role. If empty, any role is allowed. + std::string m_role; + /// The definitions in matching resources whose attributes should be valid. + std::set m_definitions; + /// The set of resources being managed that are selected by the validator. + std::map m_resources; + }; + /// Signature of functors that visit resources-by-role predicates. + using AttributeSetVisitor = std::function; + + FillOutAttributes(); + FillOutAttributes( + const Configuration& config, + const smtk::common::Managers::Ptr& managers = nullptr); + FillOutAttributes( + const Configuration& config, + const PassedDependencies& dependencies, + const smtk::common::Managers::Ptr& managers = nullptr); + + ~FillOutAttributes() override = default; + + void configure(const Configuration& config); + + smtk::common::Visit visitAttributeSets(AttributeSetVisitor visitor); + +protected: + /// Initialize with a list of resources from manager in m_managers. + bool initializeResources(); + /// Update a single resource in a predicate + bool updateResourceEntry( + smtk::attribute::Resource& resource, + const AttributeSet& predicate, + ResourceAttributes& entry); + /// Respond to operations that may change task state. + int update( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result); + + /// Check m_resourcesByRole to see if all requirements are met. + State computeInternalState() const; + + smtk::common::Managers::Ptr m_managers; + smtk::operation::Observers::Key m_observer; + std::vector m_attributeSets; +}; +} // namespace task +} // namespace smtk + +#endif // smtk_task_FillOutAttributes_h diff --git a/smtk/task/Registrar.cxx b/smtk/task/Registrar.cxx index 5dc54b0ae1..a84e9d5692 100644 --- a/smtk/task/Registrar.cxx +++ b/smtk/task/Registrar.cxx @@ -10,9 +10,12 @@ // //============================================================================= #include "smtk/task/Registrar.h" + +#include "smtk/task/FillOutAttributes.h" #include "smtk/task/Task.h" #include "smtk/task/TaskNeedsResources.h" #include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonFillOutAttributes.h" #include "smtk/task/json/jsonTask.h" #include "smtk/task/json/jsonTaskNeedsResources.h" @@ -25,8 +28,9 @@ namespace smtk namespace task { -using TaskList = std::tuple; -using JSONList = std::tuple; +using TaskList = std::tuple; +using JSONList = + std::tuple; void Registrar::registerTo(const smtk::common::Managers::Ptr& managers) { diff --git a/smtk/task/json/Helper.cxx b/smtk/task/json/Helper.cxx index 438d09dd2d..d7752ab4f0 100644 --- a/smtk/task/json/Helper.cxx +++ b/smtk/task/json/Helper.cxx @@ -33,7 +33,6 @@ Helper::~Helper() = default; bool Helper::registerType(const std::string& typeName, ConfigurationHelper helper) { - std::cout << "Register " << typeName << "\n"; std::lock_guard lock(g_types); return s_types.insert({ typeName, helper }).second; } diff --git a/smtk/task/json/jsonFillOutAttributes.cxx b/smtk/task/json/jsonFillOutAttributes.cxx new file mode 100644 index 0000000000..b1dd6700ba --- /dev/null +++ b/smtk/task/json/jsonFillOutAttributes.cxx @@ -0,0 +1,89 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/json/jsonFillOutAttributes.h" +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonTask.h" + +#include "smtk/task/FillOutAttributes.h" + +namespace smtk +{ +namespace task +{ + +void from_json(const nlohmann::json& j, FillOutAttributes::AttributeSet& attributeSet) +{ + if (j.contains("role")) + { + j.at("role").get_to(attributeSet.m_role); + } + if (j.contains("definitions")) + { + j.at("definitions").get_to(attributeSet.m_definitions); + } + if (j.contains("resource-attributes")) + { + j.at("resource-attributes").get_to(attributeSet.m_resources); + } +} + +void to_json(nlohmann::json& j, const FillOutAttributes::AttributeSet& attributeSet) +{ + j = nlohmann::json{ { "role", attributeSet.m_role }, + { "definitions", attributeSet.m_definitions }, + { "resource-attributes", attributeSet.m_resources } }; +} + +void from_json(const nlohmann::json& j, FillOutAttributes::ResourceAttributes& resourceAttributes) +{ + if (j.contains("valid")) + { + j.at("valid").get_to(resourceAttributes); + } + if (j.contains("invalid")) + { + j.at("invalid").get_to(resourceAttributes); + } +} + +void to_json(nlohmann::json& j, const FillOutAttributes::ResourceAttributes& resourceAttributes) +{ + j = nlohmann::json{ { "valid", resourceAttributes.m_valid }, + { "invalid", resourceAttributes.m_invalid } }; +} + +namespace json +{ + +Task::Configuration jsonFillOutAttributes::operator()(const Task* task, Helper& helper) const +{ + Task::Configuration config; + auto* nctask = const_cast(task); + auto* fillOutAttributes = dynamic_cast(nctask); + if (fillOutAttributes) + { + jsonTask superclass; + config = superclass(fillOutAttributes, helper); + nlohmann::json::array_t attributeSets; + fillOutAttributes->visitAttributeSets( + [&attributeSets, fillOutAttributes]( + const FillOutAttributes::AttributeSet& attributeSet) -> smtk::common::Visit { + nlohmann::json jsonAttributeSet = attributeSet; + attributeSets.push_back(jsonAttributeSet); + return smtk::common::Visit::Continue; + }); + config["attribute-sets"] = attributeSets; + } + return config; +} + +} // namespace json +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/jsonFillOutAttributes.h b/smtk/task/json/jsonFillOutAttributes.h new file mode 100644 index 0000000000..5d40ad1125 --- /dev/null +++ b/smtk/task/json/jsonFillOutAttributes.h @@ -0,0 +1,48 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_FillOutAttributes_h +#define smtk_task_json_FillOutAttributes_h + +#include "smtk/task/FillOutAttributes.h" + +#include "nlohmann/json.hpp" + +#include +#include + +namespace smtk +{ +namespace task +{ + +void SMTKCORE_EXPORT +from_json(const nlohmann::json& j, FillOutAttributes::AttributeSet& attributeSet); +void SMTKCORE_EXPORT +to_json(nlohmann::json& j, const FillOutAttributes::AttributeSet& attributeSet); +void SMTKCORE_EXPORT +from_json(const nlohmann::json& j, FillOutAttributes::ResourceAttributes& resourceAttributes); +void SMTKCORE_EXPORT +to_json(nlohmann::json& j, const FillOutAttributes::ResourceAttributes& resourceAttributes); + +namespace json +{ + +class Helper; + +struct SMTKCORE_EXPORT jsonFillOutAttributes +{ + Task::Configuration operator()(const Task* task, Helper& helper) const; +}; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_FillOutAttributes_h diff --git a/smtk/task/json/jsonManager.cxx b/smtk/task/json/jsonManager.cxx index 304ac136f9..b3b8ddaeb6 100644 --- a/smtk/task/json/jsonManager.cxx +++ b/smtk/task/json/jsonManager.cxx @@ -90,9 +90,14 @@ bool jsonManager::deserialize( } } // Now configure dependent tasks with adaptors if specified. - for (const auto& jsonAdaptor : json.at("task-adaptors")) + if (json.contains("task-adaptors")) { - std::cout << "Adaptor " << jsonAdaptor.at("type") << "\n"; + /* TODO + for (const auto& jsonAdaptor : json.at("task-adaptors")) + { + std::cout << "Adaptor " << jsonAdaptor.at("type") << "\n"; + } + */ } helper.clear(); diff --git a/smtk/task/testing/cxx/TestTaskJSON.cxx b/smtk/task/testing/cxx/TestTaskJSON.cxx index 809268ca31..a7840c5d2d 100644 --- a/smtk/task/testing/cxx/TestTaskJSON.cxx +++ b/smtk/task/testing/cxx/TestTaskJSON.cxx @@ -89,10 +89,19 @@ int TestTaskJSON(int, char*[]) }, { "id": 2, - "type": "smtk::task::Task", - "title": "Do something", + "type": "smtk::task::FillOutAttributes", + "title": "Mark up model", "state": "incomplete", - "dependencies": [ 1 ] + "dependencies": [ 1 ], + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": [ + "Material", + "BoundaryCondition" + ] + } + ] } ], "//": [ -- GitLab From a92689b9298822cab426fe09f25b8358e53f3421 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 12 Aug 2021 01:10:43 -0400 Subject: [PATCH 20/52] Rename `TaskNeedsResources` to `GatherResources`. --- doc/release/notes/task-classes.rst | 7 +++ doc/userguide/task/classes.rst | 37 +++++++------- smtk/task/CMakeLists.txt | 10 ++-- ...NeedsResources.cxx => GatherResources.cxx} | 22 ++++----- ...TaskNeedsResources.h => GatherResources.h} | 20 ++++---- smtk/task/Registrar.cxx | 9 ++-- ...sResources.cxx => jsonGatherResources.cxx} | 18 +++---- ...NeedsResources.h => jsonGatherResources.h} | 8 +-- smtk/task/pybind11/PybindGatherResources.h | 49 +++++++++++++++++++ smtk/task/pybind11/PybindTask.cxx | 5 +- smtk/task/pybind11/PybindTaskNeedsResources.h | 49 ------------------- smtk/task/testing/cxx/TestActiveTask.cxx | 6 +-- smtk/task/testing/cxx/TestTaskBasics.cxx | 15 +++--- smtk/task/testing/cxx/TestTaskJSON.cxx | 4 +- 14 files changed, 133 insertions(+), 126 deletions(-) create mode 100644 doc/release/notes/task-classes.rst rename smtk/task/{TaskNeedsResources.cxx => GatherResources.cxx} (88%) rename smtk/task/{TaskNeedsResources.h => GatherResources.h} (88%) rename smtk/task/json/{jsonTaskNeedsResources.cxx => jsonGatherResources.cxx} (78%) rename smtk/task/json/{jsonTaskNeedsResources.h => jsonGatherResources.h} (80%) create mode 100644 smtk/task/pybind11/PybindGatherResources.h delete mode 100644 smtk/task/pybind11/PybindTaskNeedsResources.h diff --git a/doc/release/notes/task-classes.rst b/doc/release/notes/task-classes.rst new file mode 100644 index 0000000000..a674beff5e --- /dev/null +++ b/doc/release/notes/task-classes.rst @@ -0,0 +1,7 @@ +New task classes +================ + +The task subsystem now provides more task types and additional tests. +See the `task class documentation`_ for details. + +.. _task class documentation: https://smtk.readthedocs.io/en/latest/userguide/task/classes.html diff --git a/doc/userguide/task/classes.rst b/doc/userguide/task/classes.rst index dea4aa9110..3f9c22baac 100644 --- a/doc/userguide/task/classes.rst +++ b/doc/userguide/task/classes.rst @@ -29,10 +29,10 @@ Example: "completed": false } -TaskNeedsResources ------------------- +GatherResources +--------------- -The :smtk:`TaskNeedsResources ` class monitors +The :smtk:`GatherResources ` class monitors a resource manager and is incomplete until its configured list of required resources is acceptable, at which time it transitions to completable. It is Incomplete by default unless unconfigured (in which case it is Completable). @@ -41,23 +41,23 @@ It accepts all the JSON configuration that the base Task class does, plus: * ``resources``: a JSON array of required resources, organized by role. Each array entry must be a JSON object holding: - * ``role``: an optional string holding a resource role name. If omitted, any role is allowed. - * ``type``: an optional string holding a resource typename. If omitted, any resource type is allowed. - * ``min``: an optional integer specifying the number of resources with the given role and type that must be present. - Only non-negative values are accepted. - It defaults to 1, which makes the requirement mandatory. - If set to 0, the requirement is optional. - * ``max``: an optional integer specifying the maximum number of resources with the given role and type allowed. - Negative values indicate that there is no maximum. - It defaults to -1. - It is possible to set this to 0 to indicate that resources of a given role/type are disallowed. + * ``role``: an optional string holding a resource role name. If omitted, any role is allowed. + * ``type``: an optional string holding a resource typename. If omitted, any resource type is allowed. + * ``min``: an optional integer specifying the number of resources with the given role and type that must be present. + Only non-negative values are accepted. + It defaults to 1, which makes the requirement mandatory. + If set to 0, the requirement is optional. + * ``max``: an optional integer specifying the maximum number of resources with the given role and type allowed. + Negative values indicate that there is no maximum. + It defaults to -1. + It is possible to set this to 0 to indicate that resources of a given role/type are disallowed. Example: .. code:: json { - "type": "smtk::task::TaskNeedsResources", + "type": "smtk::task::GatherResources", "title": "Load a geometric model (or models) and a simulation template.", "resources": [ { @@ -87,10 +87,11 @@ This task accepts all the JSON configuration that the base Task class does, plus * ``attribute-sets``: a JSON array of required attributes, organized by role. Each array entry must be a JSON object holding: - * ``role``: an optional string holding an attribute-resource role name. - If omitted, any role is allowed. - * ``definitions``: a set of :smtk:`smtk::attribute::Definition` type-names - specifying which types of attributes to validate before allowing completion. + + * ``role``: an optional string holding an attribute-resource role name. + If omitted, any role is allowed. + * ``definitions``: a set of :smtk:`smtk::attribute::Definition` type-names + specifying which types of attributes to validate before allowing completion. Example: diff --git a/smtk/task/CMakeLists.txt b/smtk/task/CMakeLists.txt index 3ebbd84d8b..acec3676cb 100644 --- a/smtk/task/CMakeLists.txt +++ b/smtk/task/CMakeLists.txt @@ -4,12 +4,12 @@ set(taskSrcs Manager.cxx Registrar.cxx Task.cxx - TaskNeedsResources.cxx + GatherResources.cxx json/Helper.cxx json/jsonFillOutAttributes.cxx + json/jsonGatherResources.cxx json/jsonManager.cxx json/jsonTask.cxx - json/jsonTaskNeedsResources.cxx ) set(taskHeaders @@ -19,12 +19,12 @@ set(taskHeaders Manager.h Registrar.h Task.h - TaskNeedsResources.h - json/jsonFillOutAttributes.h + GatherResources.h json/Helper.h + json/jsonFillOutAttributes.h + json/jsonGatherResources.h json/jsonManager.h json/jsonTask.h - json/jsonTaskNeedsResources.h ) if (SMTK_ENABLE_PYTHON_WRAPPING) diff --git a/smtk/task/TaskNeedsResources.cxx b/smtk/task/GatherResources.cxx similarity index 88% rename from smtk/task/TaskNeedsResources.cxx rename to smtk/task/GatherResources.cxx index 3104fc386e..2658f7b847 100644 --- a/smtk/task/TaskNeedsResources.cxx +++ b/smtk/task/GatherResources.cxx @@ -8,7 +8,7 @@ // PURPOSE. See the above copyright notice for more information. //========================================================================= -#include "smtk/task/TaskNeedsResources.h" +#include "smtk/task/GatherResources.h" #include "smtk/operation/Manager.h" #include "smtk/operation/SpecificationOps.h" @@ -21,7 +21,7 @@ namespace smtk namespace task { -void to_json(json& j, const TaskNeedsResources::Predicate& p) +void to_json(json& j, const GatherResources::Predicate& p) { j = json{ { "role", p.m_role }, { "type", p.m_type } }; if (p.m_minimumCount == 0 && p.m_maximumCount < 0) @@ -39,7 +39,7 @@ void to_json(json& j, const TaskNeedsResources::Predicate& p) } } -void from_json(const json& j, TaskNeedsResources::Predicate& p) +void from_json(const json& j, GatherResources::Predicate& p) { if (j.contains("role")) { @@ -80,9 +80,9 @@ void from_json(const json& j, TaskNeedsResources::Predicate& p) } } -TaskNeedsResources::TaskNeedsResources() = default; +GatherResources::GatherResources() = default; -TaskNeedsResources::TaskNeedsResources( +GatherResources::GatherResources( const Configuration& config, const smtk::common::Managers::Ptr& managers) : Task(config, managers) @@ -91,7 +91,7 @@ TaskNeedsResources::TaskNeedsResources( this->configure(config); } -TaskNeedsResources::TaskNeedsResources( +GatherResources::GatherResources( const Configuration& config, const PassedDependencies& dependencies, const smtk::common::Managers::Ptr& managers) @@ -101,7 +101,7 @@ TaskNeedsResources::TaskNeedsResources( this->configure(config); } -void TaskNeedsResources::configure(const Configuration& config) +void GatherResources::configure(const Configuration& config) { if (config.contains("resources")) { @@ -131,7 +131,7 @@ void TaskNeedsResources::configure(const Configuration& config) }, /* priority */ 0, /* initialize */ true, - "TaskNeedsResources monitors results for resources and their roles."); + "GatherResources monitors results for resources and their roles."); } } if (!m_resourcesByRole.empty()) @@ -140,7 +140,7 @@ void TaskNeedsResources::configure(const Configuration& config) } } -smtk::common::Visit TaskNeedsResources::visitPredicates(PredicateVisitor visitor) +smtk::common::Visit GatherResources::visitPredicates(PredicateVisitor visitor) { if (!visitor) { @@ -156,7 +156,7 @@ smtk::common::Visit TaskNeedsResources::visitPredicates(PredicateVisitor visitor return smtk::common::Visit::Continue; } -void TaskNeedsResources::updateResources( +void GatherResources::updateResources( smtk::resource::Resource& resource, smtk::resource::EventType event) { @@ -200,7 +200,7 @@ void TaskNeedsResources::updateResources( } } -State TaskNeedsResources::computeInternalState() const +State GatherResources::computeInternalState() const { State s = State::Completable; for (const auto& entry : m_resourcesByRole) diff --git a/smtk/task/TaskNeedsResources.h b/smtk/task/GatherResources.h similarity index 88% rename from smtk/task/TaskNeedsResources.h rename to smtk/task/GatherResources.h index 6aa57b11ad..6a92d9ff26 100644 --- a/smtk/task/TaskNeedsResources.h +++ b/smtk/task/GatherResources.h @@ -7,8 +7,8 @@ // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //========================================================================= -#ifndef smtk_task_TaskNeedsResources_h -#define smtk_task_TaskNeedsResources_h +#ifndef smtk_task_GatherResources_h +#define smtk_task_GatherResources_h #include "smtk/resource/Manager.h" #include "smtk/resource/Observer.h" @@ -22,16 +22,16 @@ namespace smtk namespace task { -/**\brief TaskNeedsResources is a task that requires resources from a resource manager. +/**\brief GatherResources is a task that requires resources from a resource manager. * * Tasks of this type observe a resource manager for resources to be added and marked * with a Role as specified (at construction time). When all the required resources * are present with the required roles, then the task becomes completable. */ -class SMTKCORE_EXPORT TaskNeedsResources : public Task +class SMTKCORE_EXPORT GatherResources : public Task { public: - smtkTypeMacro(smtk::task::TaskNeedsResources); + smtkTypeMacro(smtk::task::GatherResources); smtkSuperclassMacro(smtk::task::Task); smtkCreateMacro(smtk::task::Task); @@ -61,16 +61,16 @@ public: /// Signature of functors that visit resources-by-role predicates. using PredicateVisitor = std::function; - TaskNeedsResources(); - TaskNeedsResources( + GatherResources(); + GatherResources( const Configuration& config, const smtk::common::Managers::Ptr& managers = nullptr); - TaskNeedsResources( + GatherResources( const Configuration& config, const PassedDependencies& dependencies, const smtk::common::Managers::Ptr& managers = nullptr); - ~TaskNeedsResources() override = default; + ~GatherResources() override = default; void configure(const Configuration& config); @@ -90,4 +90,4 @@ protected: } // namespace task } // namespace smtk -#endif // smtk_task_TaskNeedsResources_h +#endif // smtk_task_GatherResources_h diff --git a/smtk/task/Registrar.cxx b/smtk/task/Registrar.cxx index a84e9d5692..f7d183dfec 100644 --- a/smtk/task/Registrar.cxx +++ b/smtk/task/Registrar.cxx @@ -12,12 +12,12 @@ #include "smtk/task/Registrar.h" #include "smtk/task/FillOutAttributes.h" +#include "smtk/task/GatherResources.h" #include "smtk/task/Task.h" -#include "smtk/task/TaskNeedsResources.h" #include "smtk/task/json/Helper.h" #include "smtk/task/json/jsonFillOutAttributes.h" +#include "smtk/task/json/jsonGatherResources.h" #include "smtk/task/json/jsonTask.h" -#include "smtk/task/json/jsonTaskNeedsResources.h" #include "smtk/plugin/Manager.h" @@ -28,9 +28,8 @@ namespace smtk namespace task { -using TaskList = std::tuple; -using JSONList = - std::tuple; +using TaskList = std::tuple; +using JSONList = std::tuple; void Registrar::registerTo(const smtk::common::Managers::Ptr& managers) { diff --git a/smtk/task/json/jsonTaskNeedsResources.cxx b/smtk/task/json/jsonGatherResources.cxx similarity index 78% rename from smtk/task/json/jsonTaskNeedsResources.cxx rename to smtk/task/json/jsonGatherResources.cxx index 4720319e4b..0e05eda750 100644 --- a/smtk/task/json/jsonTaskNeedsResources.cxx +++ b/smtk/task/json/jsonGatherResources.cxx @@ -7,11 +7,11 @@ // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //========================================================================= -#include "smtk/task/json/jsonTaskNeedsResources.h" +#include "smtk/task/json/jsonGatherResources.h" #include "smtk/task/json/Helper.h" #include "smtk/task/json/jsonTask.h" -#include "smtk/task/TaskNeedsResources.h" +#include "smtk/task/GatherResources.h" namespace smtk { @@ -20,20 +20,20 @@ namespace task namespace json { -Task::Configuration jsonTaskNeedsResources::operator()(const Task* task, Helper& helper) const +Task::Configuration jsonGatherResources::operator()(const Task* task, Helper& helper) const { Task::Configuration config; auto* nctask = const_cast(task); - auto* taskNeedsResources = dynamic_cast(nctask); - if (taskNeedsResources) + auto* gatherResources = dynamic_cast(nctask); + if (gatherResources) { jsonTask superclass; - config = superclass(taskNeedsResources, helper); + config = superclass(gatherResources, helper); nlohmann::json::array_t predicates; nlohmann::json::array_t outputs; - taskNeedsResources->visitPredicates( - [&predicates, &outputs, taskNeedsResources]( - const TaskNeedsResources::Predicate& predicate) -> smtk::common::Visit { + gatherResources->visitPredicates( + [&predicates, &outputs, gatherResources]( + const GatherResources::Predicate& predicate) -> smtk::common::Visit { nlohmann::json jsonPredicate = { { "role", predicate.m_role }, { "type", predicate.m_type }, diff --git a/smtk/task/json/jsonTaskNeedsResources.h b/smtk/task/json/jsonGatherResources.h similarity index 80% rename from smtk/task/json/jsonTaskNeedsResources.h rename to smtk/task/json/jsonGatherResources.h index a924d2794c..f0b67f13fa 100644 --- a/smtk/task/json/jsonTaskNeedsResources.h +++ b/smtk/task/json/jsonGatherResources.h @@ -7,8 +7,8 @@ // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //========================================================================= -#ifndef smtk_task_json_TaskNeedsResources_h -#define smtk_task_json_TaskNeedsResources_h +#ifndef smtk_task_json_GatherResources_h +#define smtk_task_json_GatherResources_h #include "smtk/task/Task.h" @@ -24,7 +24,7 @@ namespace json class Helper; -struct SMTKCORE_EXPORT jsonTaskNeedsResources +struct SMTKCORE_EXPORT jsonGatherResources { Task::Configuration operator()(const Task* task, Helper& helper) const; }; @@ -33,4 +33,4 @@ struct SMTKCORE_EXPORT jsonTaskNeedsResources } // namespace task } // namespace smtk -#endif // smtk_task_json_TaskNeedsResources_h +#endif // smtk_task_json_GatherResources_h diff --git a/smtk/task/pybind11/PybindGatherResources.h b/smtk/task/pybind11/PybindGatherResources.h new file mode 100644 index 0000000000..86c3289ba8 --- /dev/null +++ b/smtk/task/pybind11/PybindGatherResources.h @@ -0,0 +1,49 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= + +#ifndef pybind_smtk_task_GatherResources_h +#define pybind_smtk_task_GatherResources_h + +#include + +#include "smtk/task/GatherResources.h" + +#include "smtk/task/Task.h" + +namespace py = pybind11; + +inline PySharedPtrClass< smtk::task::GatherResources, smtk::task::Task > pybind11_init_smtk_task_GatherResources(py::module &m) +{ + PySharedPtrClass< smtk::task::GatherResources, smtk::task::Task > instance(m, "GatherResources"); + instance + .def(py::init<>()) + .def(py::init<::smtk::task::Task::Configuration const &, ::smtk::common::Managers::Ptr const &>()) + .def(py::init<::smtk::task::Task::Configuration const &, ::smtk::task::Task::PassedDependencies const &, ::smtk::common::Managers::Ptr const &>()) + .def("configure", &smtk::task::GatherResources::configure, py::arg("config")) + .def_static("create", (std::shared_ptr (*)()) &smtk::task::GatherResources::create) + .def_static("create", (std::shared_ptr (*)(::std::shared_ptr &)) &smtk::task::GatherResources::create, py::arg("ref")) + .def("typeName", &smtk::task::GatherResources::typeName) + .def_readonly_static("type_name", &smtk::task::GatherResources::type_name) + ; + py::class_< smtk::task::GatherResources::Predicate >(instance, "Predicate") + .def(py::init<::smtk::task::GatherResources::Predicate const &>()) + .def(py::init<>()) + .def("deepcopy", (smtk::task::GatherResources::Predicate & (smtk::task::GatherResources::Predicate::*)(::smtk::task::GatherResources::Predicate const &)) &smtk::task::GatherResources::Predicate::operator=) + .def_readwrite("m_role", &smtk::task::GatherResources::Predicate::m_role) + .def_readwrite("m_minimumCount", &smtk::task::GatherResources::Predicate::m_minimumCount) + .def_readwrite("m_maximumCount", &smtk::task::GatherResources::Predicate::m_maximumCount) + .def_readwrite("m_type", &smtk::task::GatherResources::Predicate::m_type) + .def_readwrite("m_validator", &smtk::task::GatherResources::Predicate::m_validator) + .def_readwrite("m_resources", &smtk::task::GatherResources::Predicate::m_resources) + ; + return instance; +} + +#endif diff --git a/smtk/task/pybind11/PybindTask.cxx b/smtk/task/pybind11/PybindTask.cxx index 660f1096b8..b0e4167530 100644 --- a/smtk/task/pybind11/PybindTask.cxx +++ b/smtk/task/pybind11/PybindTask.cxx @@ -20,7 +20,7 @@ using PySharedPtrClass = py::class_, Args...>; using namespace nlohmann; -#include "PybindTaskNeedsResources.h" +#include "PybindGatherResources.h" #include "PybindTask.h" #include "PybindManager.h" #include "PybindRegistrar.h" @@ -42,5 +42,6 @@ PYBIND11_MODULE(task, m) pybind11_init_smtk_task_State(task); pybind11_init_smtk_task_stateEnum(task); pybind11_init_smtk_task_stateName(task); - auto smtk_task_TaskNeedsResources = pybind11_init_smtk_task_TaskNeedsResources(task); + // auto smtk_task_FillOutAttributes = pybind11_init_smtk_task_FillOutAttributes(task); + auto smtk_task_GatherResources = pybind11_init_smtk_task_GatherResources(task); } diff --git a/smtk/task/pybind11/PybindTaskNeedsResources.h b/smtk/task/pybind11/PybindTaskNeedsResources.h deleted file mode 100644 index 2a76509d5e..0000000000 --- a/smtk/task/pybind11/PybindTaskNeedsResources.h +++ /dev/null @@ -1,49 +0,0 @@ -//========================================================================= -// Copyright (c) Kitware, Inc. -// All rights reserved. -// See LICENSE.txt for details. -// -// This software is distributed WITHOUT ANY WARRANTY; without even -// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -// PURPOSE. See the above copyright notice for more information. -//========================================================================= - -#ifndef pybind_smtk_task_TaskNeedsResources_h -#define pybind_smtk_task_TaskNeedsResources_h - -#include - -#include "smtk/task/TaskNeedsResources.h" - -#include "smtk/task/Task.h" - -namespace py = pybind11; - -inline PySharedPtrClass< smtk::task::TaskNeedsResources, smtk::task::Task > pybind11_init_smtk_task_TaskNeedsResources(py::module &m) -{ - PySharedPtrClass< smtk::task::TaskNeedsResources, smtk::task::Task > instance(m, "TaskNeedsResources"); - instance - .def(py::init<>()) - .def(py::init<::smtk::task::Task::Configuration const &, ::smtk::common::Managers::Ptr const &>()) - .def(py::init<::smtk::task::Task::Configuration const &, ::smtk::task::Task::PassedDependencies const &, ::smtk::common::Managers::Ptr const &>()) - .def("configure", &smtk::task::TaskNeedsResources::configure, py::arg("config")) - .def_static("create", (std::shared_ptr (*)()) &smtk::task::TaskNeedsResources::create) - .def_static("create", (std::shared_ptr (*)(::std::shared_ptr &)) &smtk::task::TaskNeedsResources::create, py::arg("ref")) - .def("typeName", &smtk::task::TaskNeedsResources::typeName) - .def_readonly_static("type_name", &smtk::task::TaskNeedsResources::type_name) - ; - py::class_< smtk::task::TaskNeedsResources::Predicate >(instance, "Predicate") - .def(py::init<::smtk::task::TaskNeedsResources::Predicate const &>()) - .def(py::init<>()) - .def("deepcopy", (smtk::task::TaskNeedsResources::Predicate & (smtk::task::TaskNeedsResources::Predicate::*)(::smtk::task::TaskNeedsResources::Predicate const &)) &smtk::task::TaskNeedsResources::Predicate::operator=) - .def_readwrite("m_role", &smtk::task::TaskNeedsResources::Predicate::m_role) - .def_readwrite("m_minimumCount", &smtk::task::TaskNeedsResources::Predicate::m_minimumCount) - .def_readwrite("m_maximumCount", &smtk::task::TaskNeedsResources::Predicate::m_maximumCount) - .def_readwrite("m_type", &smtk::task::TaskNeedsResources::Predicate::m_type) - .def_readwrite("m_validator", &smtk::task::TaskNeedsResources::Predicate::m_validator) - .def_readwrite("m_resources", &smtk::task::TaskNeedsResources::Predicate::m_resources) - ; - return instance; -} - -#endif diff --git a/smtk/task/testing/cxx/TestActiveTask.cxx b/smtk/task/testing/cxx/TestActiveTask.cxx index cbe518620e..cf58f9bd24 100644 --- a/smtk/task/testing/cxx/TestActiveTask.cxx +++ b/smtk/task/testing/cxx/TestActiveTask.cxx @@ -17,10 +17,10 @@ #include "smtk/plugin/Registry.h" #include "smtk/resource/Manager.h" #include "smtk/resource/Registrar.h" +#include "smtk/task/GatherResources.h" #include "smtk/task/Manager.h" #include "smtk/task/Registrar.h" #include "smtk/task/Task.h" -#include "smtk/task/TaskNeedsResources.h" #include "smtk/common/testing/cxx/helpers.h" @@ -98,7 +98,7 @@ int TestActiveTask(int, char*[]) std::cout << "Switching to task 2:\n"; taskManager->active().switchTo(t2.get()); - // Test TaskNeedsResources + // Test GatherResources // I. Construction w/ configuration. // The task is incomplete unless 1 or 2 model geometry resources // and 1 or more simulation attribute resources are held by the @@ -109,7 +109,7 @@ int TestActiveTask(int, char*[]) { { { "role", "model geometry" }, { "type", "smtk::model::Resource" }, { "max", 2 } }, { { "role", "simulation attribute" }, { "type", "smtk::attribute::Resource" } } } } }; - auto t4 = taskManager->instances().create(c4, managers); + auto t4 = taskManager->instances().create(c4, managers); t1->addDependency(t4); std::cout << "Ensuring switches to unavailable tasks fail.\n"; bool didSwitch = taskManager->active().switchTo(t1.get()); diff --git a/smtk/task/testing/cxx/TestTaskBasics.cxx b/smtk/task/testing/cxx/TestTaskBasics.cxx index 484bff6e4b..887e278384 100644 --- a/smtk/task/testing/cxx/TestTaskBasics.cxx +++ b/smtk/task/testing/cxx/TestTaskBasics.cxx @@ -17,10 +17,10 @@ #include "smtk/plugin/Registry.h" #include "smtk/resource/Manager.h" #include "smtk/resource/Registrar.h" +#include "smtk/task/GatherResources.h" #include "smtk/task/Manager.h" #include "smtk/task/Registrar.h" #include "smtk/task/Task.h" -#include "smtk/task/TaskNeedsResources.h" #include "smtk/common/testing/cxx/helpers.h" @@ -173,7 +173,7 @@ int TestTaskBasics(int, char*[]) success = t1->removeDependency(t2); test(!success, "Expected removal of non-existent dependency to fail."); - // Test TaskNeedsResources + // Test GatherResources // I. Construction w/ configuration. // The task is incomplete unless 1 or 2 model geometry resources // and 1 or more simulation attribute resources are held by the @@ -184,8 +184,8 @@ int TestTaskBasics(int, char*[]) { { { "role", "model geometry" }, { "type", "smtk::model::Resource" }, { "max", 2 } }, { { "role", "simulation attribute" }, { "type", "smtk::attribute::Resource" } } } } }; - auto t4 = taskManager->instances().create(c4, managers); - test(!!t4, "Could not create TaskNeedsResources."); + auto t4 = taskManager->instances().create(c4, managers); + test(!!t4, "Could not create GatherResources."); test(t4->state() == State::Incomplete, "Task with no resources should be incomplete."); auto hokey = t4->observers().insert(callback); @@ -241,15 +241,14 @@ int TestTaskBasics(int, char*[]) "Expected state transition incomplete⟶completable."); // III. Verify that an empty role is allowed, as are empty resource types. - // This should also test initialization of TaskNeedsResources when + // This should also test initialization of GatherResources when // resource manager is not empty. Task::Configuration c5{ { "title", "Task 5" }, { "resources", { { { "type", "smtk::model::Resource" } }, { { "role", "simulation attribute" } } } } }; - auto t5 = - taskManager->instances().createFromName("smtk::task::TaskNeedsResources", c5, managers); - test(!!t5, "Could not create TaskNeedsResources."); + auto t5 = taskManager->instances().createFromName("smtk::task::GatherResources", c5, managers); + test(!!t5, "Could not create GatherResources."); auto pokey = t5->observers().insert(callback); test(t5->state() == State::Completable, "Task 5 should be completable initially."); called = 0; diff --git a/smtk/task/testing/cxx/TestTaskJSON.cxx b/smtk/task/testing/cxx/TestTaskJSON.cxx index a7840c5d2d..7ded5f0a51 100644 --- a/smtk/task/testing/cxx/TestTaskJSON.cxx +++ b/smtk/task/testing/cxx/TestTaskJSON.cxx @@ -20,7 +20,7 @@ #include "smtk/task/Manager.h" #include "smtk/task/Registrar.h" #include "smtk/task/Task.h" -#include "smtk/task/TaskNeedsResources.h" +// #include "smtk/task/GatherResources.h" #include "smtk/task/json/jsonManager.h" #include "smtk/task/json/jsonTask.h" @@ -68,7 +68,7 @@ int TestTaskJSON(int, char*[]) "tasks": [ { "id": 1, - "type": "smtk::task::TaskNeedsResources", + "type": "smtk::task::GatherResources", "title": "Load a model and attribute", "state": "completed", "resources": [ -- GitLab From dec876bc5edbdb13fe25578feb0d3979cc311032 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 12 Aug 2021 01:29:35 -0400 Subject: [PATCH 21/52] Update python bindings for task classes. --- smtk/task/pybind11/PybindActive.h | 38 ++++++++++++++ smtk/task/pybind11/PybindFillOutAttributes.h | 55 ++++++++++++++++++++ smtk/task/pybind11/PybindManager.h | 8 ++- smtk/task/pybind11/PybindTask.cxx | 7 ++- 4 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 smtk/task/pybind11/PybindActive.h create mode 100644 smtk/task/pybind11/PybindFillOutAttributes.h diff --git a/smtk/task/pybind11/PybindActive.h b/smtk/task/pybind11/PybindActive.h new file mode 100644 index 0000000000..d7068724dd --- /dev/null +++ b/smtk/task/pybind11/PybindActive.h @@ -0,0 +1,38 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= + +#ifndef pybind_smtk_task_Active_h +#define pybind_smtk_task_Active_h + +#include + +#include "smtk/task/Active.h" + +#include "smtk/task/Instances.h" +#include "smtk/task/Task.h" + +namespace py = pybind11; + +inline py::class_< smtk::task::Active > pybind11_init_smtk_task_Active(py::module &m) +{ + py::class_< smtk::task::Active > instance(m, "Active"); + instance + .def(py::init<::smtk::task::Instances *>()) + .def("observers", (smtk::task::Active::Observers & (smtk::task::Active::*)()) &smtk::task::Active::observers) + .def("observers", (smtk::task::Active::Observers const & (smtk::task::Active::*)() const) &smtk::task::Active::observers) + .def("switchTo", &smtk::task::Active::switchTo, py::arg("arg0")) + .def("task", &smtk::task::Active::task) + .def("typeName", &smtk::task::Active::typeName) + .def_readonly_static("type_name", &smtk::task::Active::type_name) + ; + return instance; +} + +#endif diff --git a/smtk/task/pybind11/PybindFillOutAttributes.h b/smtk/task/pybind11/PybindFillOutAttributes.h new file mode 100644 index 0000000000..3b5c121691 --- /dev/null +++ b/smtk/task/pybind11/PybindFillOutAttributes.h @@ -0,0 +1,55 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= + +#ifndef pybind_smtk_task_FillOutAttributes_h +#define pybind_smtk_task_FillOutAttributes_h + +#include + +#include "smtk/task/FillOutAttributes.h" + +#include "smtk/common/Visit.h" +#include "smtk/task/Task.h" + +namespace py = pybind11; + +inline PySharedPtrClass< smtk::task::FillOutAttributes, smtk::task::Task > pybind11_init_smtk_task_FillOutAttributes(py::module &m) +{ + PySharedPtrClass< smtk::task::FillOutAttributes, smtk::task::Task > instance(m, "FillOutAttributes"); + instance + .def(py::init<>()) + .def(py::init<::smtk::task::Task::Configuration const &, ::smtk::common::Managers::Ptr const &>()) + .def(py::init<::smtk::task::Task::Configuration const &, ::smtk::task::Task::PassedDependencies const &, ::smtk::common::Managers::Ptr const &>()) + .def("configure", &smtk::task::FillOutAttributes::configure, py::arg("config")) + .def_static("create", (std::shared_ptr (*)()) &smtk::task::FillOutAttributes::create) + .def_static("create", (std::shared_ptr (*)(::std::shared_ptr &)) &smtk::task::FillOutAttributes::create, py::arg("ref")) + .def("typeName", &smtk::task::FillOutAttributes::typeName) + .def("visitAttributeSets", &smtk::task::FillOutAttributes::visitAttributeSets, py::arg("visitor")) + .def_readonly_static("type_name", &smtk::task::FillOutAttributes::type_name) + ; + py::class_< smtk::task::FillOutAttributes::AttributeSet >(instance, "AttributeSet") + .def(py::init<>()) + .def(py::init<::smtk::task::FillOutAttributes::AttributeSet const &>()) + .def("deepcopy", (smtk::task::FillOutAttributes::AttributeSet & (smtk::task::FillOutAttributes::AttributeSet::*)(::smtk::task::FillOutAttributes::AttributeSet const &)) &smtk::task::FillOutAttributes::AttributeSet::operator=) + .def_readwrite("m_definitions", &smtk::task::FillOutAttributes::AttributeSet::m_definitions) + .def_readwrite("m_resources", &smtk::task::FillOutAttributes::AttributeSet::m_resources) + .def_readwrite("m_role", &smtk::task::FillOutAttributes::AttributeSet::m_role) + ; + py::class_< smtk::task::FillOutAttributes::ResourceAttributes >(instance, "ResourceAttributes") + .def(py::init<>()) + .def(py::init<::smtk::task::FillOutAttributes::ResourceAttributes const &>()) + .def("deepcopy", (smtk::task::FillOutAttributes::ResourceAttributes & (smtk::task::FillOutAttributes::ResourceAttributes::*)(::smtk::task::FillOutAttributes::ResourceAttributes const &)) &smtk::task::FillOutAttributes::ResourceAttributes::operator=) + .def_readwrite("m_invalid", &smtk::task::FillOutAttributes::ResourceAttributes::m_invalid) + .def_readwrite("m_valid", &smtk::task::FillOutAttributes::ResourceAttributes::m_valid) + ; + return instance; +} + +#endif diff --git a/smtk/task/pybind11/PybindManager.h b/smtk/task/pybind11/PybindManager.h index b276ddb0f7..1174fb4081 100644 --- a/smtk/task/pybind11/PybindManager.h +++ b/smtk/task/pybind11/PybindManager.h @@ -15,18 +15,22 @@ #include "smtk/task/Manager.h" +#include "smtk/task/Active.h" + namespace py = pybind11; inline PySharedPtrClass< smtk::task::Manager > pybind11_init_smtk_task_Manager(py::module &m) { PySharedPtrClass< smtk::task::Manager > instance(m, "Manager"); instance + .def("active", (smtk::task::Active & (smtk::task::Manager::*)()) &smtk::task::Manager::active) + .def("active", (smtk::task::Active const & (smtk::task::Manager::*)() const) &smtk::task::Manager::active) .def_static("create", (std::shared_ptr (*)()) &smtk::task::Manager::create) .def_static("create", (std::shared_ptr (*)(::std::shared_ptr &)) &smtk::task::Manager::create, py::arg("ref")) - .def("managers", &smtk::task::Manager::managers) - .def("setManagers", &smtk::task::Manager::setManagers, py::arg("managers")) .def("instances", (smtk::task::Manager::Instances & (smtk::task::Manager::*)()) &smtk::task::Manager::instances) .def("instances", (smtk::task::Manager::Instances const & (smtk::task::Manager::*)() const) &smtk::task::Manager::instances) + .def("managers", &smtk::task::Manager::managers) + .def("setManagers", &smtk::task::Manager::setManagers, py::arg("managers")) .def("typeName", &smtk::task::Manager::typeName) .def_readonly_static("type_name", &smtk::task::Manager::type_name) ; diff --git a/smtk/task/pybind11/PybindTask.cxx b/smtk/task/pybind11/PybindTask.cxx index b0e4167530..7fbe0c568d 100644 --- a/smtk/task/pybind11/PybindTask.cxx +++ b/smtk/task/pybind11/PybindTask.cxx @@ -20,11 +20,13 @@ using PySharedPtrClass = py::class_, Args...>; using namespace nlohmann; +#include "PybindActive.h" +#include "PybindFillOutAttributes.h" #include "PybindGatherResources.h" -#include "PybindTask.h" #include "PybindManager.h" #include "PybindRegistrar.h" #include "PybindState.h" +#include "PybindTask.h" PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); @@ -36,12 +38,13 @@ PYBIND11_MODULE(task, m) // The order of these function calls is important! It was determined by // comparing the dependencies of each of the wrapped objects. + auto smtk_task_Active = pybind11_init_smtk_task_Active(task); auto smtk_task_Manager = pybind11_init_smtk_task_Manager(task); auto smtk_task_Registrar = pybind11_init_smtk_task_Registrar(task); auto smtk_task_Task = pybind11_init_smtk_task_Task(task); pybind11_init_smtk_task_State(task); pybind11_init_smtk_task_stateEnum(task); pybind11_init_smtk_task_stateName(task); - // auto smtk_task_FillOutAttributes = pybind11_init_smtk_task_FillOutAttributes(task); + auto smtk_task_FillOutAttributes = pybind11_init_smtk_task_FillOutAttributes(task); auto smtk_task_GatherResources = pybind11_init_smtk_task_GatherResources(task); } -- GitLab From c4f988fbe1055e3a028a4617339b0120e0e79792 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 12 Aug 2021 15:06:16 -0400 Subject: [PATCH 22/52] Add a task group class. --- doc/userguide/task/classes.rst | 86 ++++++- smtk/task/CMakeLists.txt | 2 + smtk/task/Group.cxx | 283 ++++++++++++++++++++++++ smtk/task/Group.h | 105 +++++++++ smtk/task/testing/cxx/TestTaskGroup.cxx | 169 ++++++++++++++ 5 files changed, 642 insertions(+), 3 deletions(-) create mode 100644 smtk/task/Group.cxx create mode 100644 smtk/task/Group.h create mode 100644 smtk/task/testing/cxx/TestTaskGroup.cxx diff --git a/doc/userguide/task/classes.rst b/doc/userguide/task/classes.rst index 3f9c22baac..f7709417b4 100644 --- a/doc/userguide/task/classes.rst +++ b/doc/userguide/task/classes.rst @@ -19,7 +19,8 @@ The following JSON can be used to configure it: * ``completed``: an optional boolean value indicating whether the user has marked the task complete or not. -Example: +Example +""""""" .. code:: json @@ -29,6 +30,83 @@ Example: "completed": false } +Group +----- + +A task :smtk:`Group ` exists to organize a set of tasks. + +The Group instance is responsible for configuring its children, including +creating dependencies among them. The Group's state and output are +dependent on its children. + +The Group has a "mode," which describes how children are related to +one another: when the mode is parallel, children have no dependency on +one another and the group itself is dependent on all of its children. +When the mode is serial, children must be completed in the +order specified (i.e., each successive task is dependent on its +predecessor) and the group itself is dependent on the final child task. + +Task groups are completable by default (i.e., when no children are configured). +If children exist, the group takes its state as a combination of its dependencies. +In serial mode, the group has a single internal dependency and its state is identical to +the dependency's state. If the group also has external dependencies on sibling tasks +(that are not children), its state also depends on them. +In parallel mode, the group has at least as many dependencies as children. + +The task Group class accepts all the JSON configuration that the base Task class does, plus: + +* ``mode``: either ``serial`` or ``parallel``. +* ``children``: an ordered JSON array of child task specifications. + Each child task may have an integer ``id`` whose value may be referenced + by ``adaptors`` below. +* ``adaptors``: an array of task-adaptor specifications that inform + the group task how to configure children. The reserved ``id`` of 0 + refers to the Group itself. + +Example +""""""" + +.. code:: json + + { + "type": "smtk::task::Group", + "title": "Perform the child tasks in order.", + "mode": "serial", + "children": [ + { + "id": 1, + "type": "smtk::task::Task", + "title": "Step 1." + }, + { + "id": 2, + "type": "smtk::task::Task", + "title": "Step 2." + } + ], + "adaptors": [ + { + "//": "How the parent configures its child." + "type": "smtk::task::adaptor::PassResources", + "from": 0, + "to": 1 + }, + { + "//": "How the parent configures its child." + "type": "smtk::task::adaptor::PassResources", + "from": 0, + "to": 2 + }, + { + "//": "How the serial task configures its successor." + "type": "smtk::task::adaptor::PassComponents", + "from": 1, + "to": 2 + } + ] + } + + GatherResources --------------- @@ -52,7 +130,8 @@ It accepts all the JSON configuration that the base Task class does, plus: It defaults to -1. It is possible to set this to 0 to indicate that resources of a given role/type are disallowed. -Example: +Example +""""""" .. code:: json @@ -93,7 +172,8 @@ This task accepts all the JSON configuration that the base Task class does, plus * ``definitions``: a set of :smtk:`smtk::attribute::Definition` type-names specifying which types of attributes to validate before allowing completion. -Example: +Example +""""""" .. code:: json diff --git a/smtk/task/CMakeLists.txt b/smtk/task/CMakeLists.txt index acec3676cb..5f4de12b50 100644 --- a/smtk/task/CMakeLists.txt +++ b/smtk/task/CMakeLists.txt @@ -5,6 +5,7 @@ set(taskSrcs Registrar.cxx Task.cxx GatherResources.cxx + Group.cxx json/Helper.cxx json/jsonFillOutAttributes.cxx json/jsonGatherResources.cxx @@ -20,6 +21,7 @@ set(taskHeaders Registrar.h Task.h GatherResources.h + Group.h json/Helper.h json/jsonFillOutAttributes.h json/jsonGatherResources.h diff --git a/smtk/task/Group.cxx b/smtk/task/Group.cxx new file mode 100644 index 0000000000..c0b393c1ae --- /dev/null +++ b/smtk/task/Group.cxx @@ -0,0 +1,283 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/FillOutAttributes.h" + +#include "smtk/project/ResourceContainer.h" + +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonFillOutAttributes.h" + +#include "smtk/operation/Manager.h" +#include "smtk/operation/SpecificationOps.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/Resource.h" + +#include "smtk/resource/Manager.h" + +#include + +namespace smtk +{ +namespace task +{ + +FillOutAttributes::FillOutAttributes() = default; + +FillOutAttributes::FillOutAttributes( + const Configuration& config, + const smtk::common::Managers::Ptr& managers) + : Task(config, managers) + , m_managers(managers) +{ + this->configure(config); +} + +FillOutAttributes::FillOutAttributes( + const Configuration& config, + const PassedDependencies& dependencies, + const smtk::common::Managers::Ptr& managers) + : Task(config, dependencies, managers) + , m_managers(managers) +{ + this->configure(config); +} + +void FillOutAttributes::configure(const Configuration& config) +{ + // The predicate from_json method needs the resource manager: + auto& helper = json::Helper::instance(); + helper.setManagers(m_managers); + + if (config.contains("attribute-sets")) + { + config.at("attribute-sets").get_to(m_attributeSets); + } + if (m_managers) + { + if (auto operationManager = m_managers->get()) + { + m_observer = operationManager->observers().insert( + [this]( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result) { return this->update(op, event, result); }, + /* priority */ 0, + /* initialize */ true, + "FillOutAttributes monitors operations for updates."); + } + } + if (!m_attributeSets.empty()) + { + this->initializeResources(); + this->internalStateChanged(this->computeInternalState()); + } +} + +smtk::common::Visit FillOutAttributes::visitAttributeSets(AttributeSetVisitor visitor) +{ + if (!visitor) + { + return smtk::common::Visit::Halt; + } + for (const auto& entry : m_attributeSets) + { + if (visitor(entry) == smtk::common::Visit::Halt) + { + return smtk::common::Visit::Halt; + } + } + return smtk::common::Visit::Continue; +} + +bool FillOutAttributes::initializeResources() +{ + bool foundResource = false; + if (m_attributeSets.empty()) + { + return foundResource; + } + if (auto resourceManager = m_managers->get()) + { + auto resources = resourceManager->find(); + for (auto resource : resources) + { + const std::string& role = smtk::project::detail::role(resource); + for (auto& attributeSet : m_attributeSets) + { + if ( + attributeSet.m_role.empty() || attributeSet.m_role == "*" || attributeSet.m_role == role) + { + foundResource = true; + auto it = attributeSet.m_resources.insert({ resource->id(), { {}, {} } }).first; + this->updateResourceEntry(*resource, attributeSet, it->second); + } + } + } + } + return foundResource; +} + +bool FillOutAttributes::updateResourceEntry( + smtk::attribute::Resource& resource, + const AttributeSet& predicate, + ResourceAttributes& entry) +{ + bool changesMade = false; + // I. Remove invalid entries for attributes that are valid or deleted. + std::set expunged; + std::set validated; + std::set invalidated; + for (const auto& invalidId : entry.m_invalid) + { + auto att = resource.findAttribute(invalidId); + if (att) + { + if (att->isValid()) // TODO: accept predicate override for categories? + { + validated.insert(invalidId); + } + } + else + { + expunged.insert(invalidId); + } + } + // II. Check valid attributes to see if they have been invalidated or expunged. + for (const auto& validId : entry.m_valid) + { + auto att = resource.findAttribute(validId); + if (att) + { + if (!att->isValid()) // TODO: accept predicate override for categories? + { + invalidated.insert(validId); + } + } + else + { + expunged.insert(validId); + } + } + // If the set of invalid attributes was changed, we need to re-run computeInternalState(). + changesMade |= !expunged.empty() || !validated.empty() || !invalidated.empty(); + for (const auto& id : validated) + { + entry.m_invalid.erase(id); + entry.m_valid.insert(id); + } + for (const auto& id : expunged) + { + entry.m_invalid.erase(id); + } + for (const auto& id : invalidated) + { + entry.m_invalid.insert(id); + entry.m_valid.erase(id); + } + // II. Check for newly-created attributes + std::vector attributes; + for (const auto& definition : predicate.m_definitions) + { + resource.findAttributes(definition, attributes); + for (const auto& attribute : attributes) + { + auto uid = attribute->id(); + if ( + (entry.m_invalid.find(uid) == entry.m_invalid.end()) && + (entry.m_valid.find(uid) == entry.m_valid.end())) + { + // We've found a new attribute. Classify it. + changesMade = true; + if (attribute->isValid()) // TODO: accept predicate override for categories? + { + entry.m_valid.insert(uid); + } + else + { + entry.m_invalid.insert(uid); + } + } + } + } + return changesMade; +} + +int FillOutAttributes::update( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result) +{ + bool predicatesUpdated = false; + switch (event) + { + case smtk::operation::EventType::DID_OPERATE: + { + auto mentionedResources = smtk::operation::extractResources(result); + + for (auto& weakResource : mentionedResources) + { + auto resource = std::dynamic_pointer_cast(weakResource.lock()); + if (resource) + { + const std::string& role = smtk::project::detail::role(resource); + // Do we care about this resource? + for (auto& predicate : m_attributeSets) + { + auto it = predicate.m_resources.find(resource->id()); + bool doUpdate = false; + if (it != predicate.m_resources.end()) + { + doUpdate = true; + } + else if ( + predicate.m_role == role || predicate.m_role == "*" || predicate.m_role.empty()) + { + it = predicate.m_resources.insert({ resource->id(), { {}, {} } }).first; + doUpdate = true; + } + if (doUpdate) + { + predicatesUpdated |= this->updateResourceEntry(*resource, predicate, it->second); + } + } + } + } + } + break; + case smtk::operation::EventType::WILL_OPERATE: + break; + } + if (predicatesUpdated) + { + this->internalStateChanged(this->computeInternalState()); + } + return 0; +} + +State FillOutAttributes::computeInternalState() const +{ + State s = State::Completable; + for (const auto& predicate : m_attributeSets) + { + for (const auto& resourceEntry : predicate.m_resources) + { + if (!resourceEntry.second.m_invalid.empty()) + { + s = State::Incomplete; + return s; + } + } + } + return s; +} + +} // namespace task +} // namespace smtk diff --git a/smtk/task/Group.h b/smtk/task/Group.h new file mode 100644 index 0000000000..72a3712fc6 --- /dev/null +++ b/smtk/task/Group.h @@ -0,0 +1,105 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_FillOutAttributes_h +#define smtk_task_FillOutAttributes_h + +#include "smtk/operation/Manager.h" +#include "smtk/operation/Observer.h" +#include "smtk/operation/Operation.h" +#include "smtk/resource/Resource.h" +#include "smtk/task/Task.h" + +#include "smtk/common/Visit.h" + +namespace smtk +{ +namespace task +{ + +/**\brief FillOutAttributes is a task that is incomplete until specified + * attributes are valid. + * + * This task accepts an input attribute resource (configured by a predecessor + * task or specified via a role) and observe an operation manager for operations. + * After each operation, attributes with a definition are validated. + * If all attributes identify are valid, the task becomes completable. + * Otherwise, the task will remain (or become) incomplete. + */ +class SMTKCORE_EXPORT FillOutAttributes : public Task +{ +public: + smtkTypeMacro(smtk::task::FillOutAttributes); + smtkSuperclassMacro(smtk::task::Task); + smtkCreateMacro(smtk::task::Task); + + /// Per-resource sets of validated attributes + /// + /// We need to track attributes so incremental updates + /// can decide whether to change state. + struct ResourceAttributes + { + /// Attributes matching a definition that are validated. + std::set m_valid; + /// Attributes matching a definition that need attention. + std::set m_invalid; + }; + /// A predicate used to collect resources that fit a given role. + struct AttributeSet + { + /// The required role. If empty, any role is allowed. + std::string m_role; + /// The definitions in matching resources whose attributes should be valid. + std::set m_definitions; + /// The set of resources being managed that are selected by the validator. + std::map m_resources; + }; + /// Signature of functors that visit resources-by-role predicates. + using AttributeSetVisitor = std::function; + + FillOutAttributes(); + FillOutAttributes( + const Configuration& config, + const smtk::common::Managers::Ptr& managers = nullptr); + FillOutAttributes( + const Configuration& config, + const PassedDependencies& dependencies, + const smtk::common::Managers::Ptr& managers = nullptr); + + ~FillOutAttributes() override = default; + + void configure(const Configuration& config); + + smtk::common::Visit visitAttributeSets(AttributeSetVisitor visitor); + +protected: + /// Initialize with a list of resources from manager in m_managers. + bool initializeResources(); + /// Update a single resource in a predicate + bool updateResourceEntry( + smtk::attribute::Resource& resource, + const AttributeSet& predicate, + ResourceAttributes& entry); + /// Respond to operations that may change task state. + int update( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result); + + /// Check m_resourcesByRole to see if all requirements are met. + State computeInternalState() const; + + smtk::common::Managers::Ptr m_managers; + smtk::operation::Observers::Key m_observer; + std::vector m_attributeSets; +}; +} // namespace task +} // namespace smtk + +#endif // smtk_task_FillOutAttributes_h diff --git a/smtk/task/testing/cxx/TestTaskGroup.cxx b/smtk/task/testing/cxx/TestTaskGroup.cxx new file mode 100644 index 0000000000..361a600c6e --- /dev/null +++ b/smtk/task/testing/cxx/TestTaskGroup.cxx @@ -0,0 +1,169 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/attribute/Registrar.h" +#include "smtk/attribute/Resource.h" +#include "smtk/common/Managers.h" +#include "smtk/model/Registrar.h" +#include "smtk/model/Resource.h" +#include "smtk/operation/Manager.h" +#include "smtk/operation/Registrar.h" +#include "smtk/plugin/Registry.h" +#include "smtk/resource/Manager.h" +#include "smtk/resource/Registrar.h" +#include "smtk/task/Manager.h" +#include "smtk/task/Registrar.h" +#include "smtk/task/Task.h" +// #include "smtk/task/GatherResources.h" + +#include "smtk/task/json/jsonManager.h" +#include "smtk/task/json/jsonTask.h" + +#include "smtk/common/testing/cxx/helpers.h" + +namespace test_task +{ + +using namespace smtk::task; + +} // namespace test_task + +int TestTaskGroup(int, char*[]) +{ + using smtk::task::State; + using smtk::task::Task; + + // Create managers + auto managers = smtk::common::Managers::create(); + auto attributeRegistry = smtk::plugin::addToManagers(managers); + auto resourceRegistry = smtk::plugin::addToManagers(managers); + auto operationRegistry = smtk::plugin::addToManagers(managers); + auto taskRegistry = smtk::plugin::addToManagers(managers); + + auto resourceManager = managers->get(); + auto operationManager = managers->get(); + auto taskManager = managers->get(); + + auto attributeResourceRegistry = + smtk::plugin::addToManagers(resourceManager); + auto modelRegistry = smtk::plugin::addToManagers(resourceManager); + auto taskTaskRegistry = smtk::plugin::addToManagers(taskManager); + + auto attrib = resourceManager->create(); + auto model = resourceManager->create(); + attrib->setName("simulation"); + model->setName("geometry"); + attrib->properties().get()["project_role"] = "simulation attribute"; + model->properties().get()["project_role"] = "model geometry"; + auto attribUUIDStr = attrib->id().toString(); + auto modelUUIDStr = model->id().toString(); + + std::string configString = R"({ + "tasks": [ + { + "id": 1, + "type": "smtk::task::GatherResources", + "title": "Load a model and attribute", + "state": "completed", + "resources": [ + { + "role": "model geometry", + "type": "smtk::model::Resource", + "max": 2 + }, + { + "role": "simulation attribute", + "type": "smtk::attribute::Resource" + } + ], + "output": [ + { "role": "model geometry", "resources": [ "feedface-0000-0000-0000-000000000000" ] }, + { "role": "simulation attribute", "resources": [ "deadbeef-0000-0000-0000-000000000000" ] } + ] + }, + { + "id": 2, + "type": "smtk::task::FillOutAttributes", + "title": "Mark up model", + "state": "incomplete", + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": [ + "Material", + "BoundaryCondition" + ] + } + ] + }, + { + "id": 3, + "type": "smtk::task::FillOutAttributes", + "title": "Specify simulation parameters", + "state": "incomplete", + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": [ + "Globals" + ] + } + ] + } + { + "id": 4, + "type": "smtk::task::Group", + "title": "Prepare simulation", + "state": "incomplete", + "dependencies": [ 1 ], + "grouping": { + "children": [2, 3], + "mode": "parallel" + } + } + ], + "//": [ + "For each dependency, we can store configuration information", + "for the object used to adapt one task's output into", + "configuration information for its dependent task. If no", + "configuration is present for a dependency, we use the", + "'Ignore' adaptor as a default – which never modifies the", + "depedent task's configuration." + ], + "task-adaptors": [ + { + "type": "smtk::task::adaptor::Ignore", + "from": 1, + "to": 2 + } + ] +} + )"; + auto cursor = configString.find("deadbeef", 0); + configString = configString.replace(cursor, attribUUIDStr.length(), attribUUIDStr); + cursor = configString.find("feedface", 0); + configString = configString.replace(cursor, modelUUIDStr.length(), modelUUIDStr); + + auto config = nlohmann::json::parse(configString); + std::cout << config.dump(2) << "\n"; + bool ok = smtk::task::json::jsonManager::deserialize(managers, config); + test(ok, "Failed to parse configuration."); + test(taskManager->instances().size() == 2, "Expected to deserialize 2 tasks."); + + // Round trip it. + nlohmann::json config2; + ok = smtk::task::json::jsonManager::serialize(managers, config2); + test(ok, "Failed to serialize task manager."); + std::cout << config2.dump(2) << "\n"; + + // TODO: Round trip a second time so we should be guaranteed to have + // two machine-(not-hand-)generated strings to compare. + + return 0; +} -- GitLab From aa46a219803d267a8750d00dc3636c366daef32e Mon Sep 17 00:00:00 2001 From: David Thompson Date: Fri, 13 Aug 2021 17:14:23 -0400 Subject: [PATCH 23/52] Implement task adaptors. + Add base adaptor class and `smtk::task::adaptor::ResourceAndRole` subclass. + Add new "irrelevant" state; make FillOutAttributes use it. + Progress on Group task implementation. + Rename `GatherResources::Predicate` to `GatherResources::ResourceSet`. + Improved documentation for the task subsystem. + Verify FillOutAttributes works in TestTaskJSON. This also starts work on round-tripping the JSON. + Refactor code from the task::Manager into a Configurator class so we can use the same code to register adaptors as tasks. --- doc/release/notes/task-classes.rst | 3 +- doc/smtk.doxyfile.in | 4 + doc/userguide/task/adaptors.rst | 62 ++++++ doc/userguide/task/classes.rst | 34 ++- doc/userguide/task/concepts.rst | 18 +- doc/userguide/task/index.rst | 1 + smtk/doc.h | 20 ++ smtk/task/Adaptor.cxx | 44 ++++ smtk/task/Adaptor.h | 57 +++++ smtk/task/CMakeLists.txt | 46 ++-- smtk/task/FillOutAttributes.cxx | 24 ++- smtk/task/FillOutAttributes.h | 18 +- smtk/task/GatherResources.cxx | 30 +-- smtk/task/GatherResources.h | 12 +- smtk/task/Group.cxx | 237 ++------------------- smtk/task/Group.h | 83 +++----- smtk/task/Manager.cxx | 2 +- smtk/task/Manager.h | 20 +- smtk/task/Registrar.cxx | 36 +++- smtk/task/State.h | 11 +- smtk/task/Task.cxx | 2 + smtk/task/adaptor/Instances.h | 45 ++++ smtk/task/adaptor/ResourceAndRole.cxx | 127 +++++++++++ smtk/task/adaptor/ResourceAndRole.h | 52 +++++ smtk/task/json/Configurator.cxx | 36 ++++ smtk/task/json/Configurator.h | 134 ++++++++++++ smtk/task/json/Configurator.txx | 186 ++++++++++++++++ smtk/task/json/Helper.cxx | 111 +++++----- smtk/task/json/Helper.h | 100 ++------- smtk/task/json/jsonAdaptor.cxx | 69 ++++++ smtk/task/json/jsonAdaptor.h | 43 ++++ smtk/task/json/jsonFillOutAttributes.cxx | 2 + smtk/task/json/jsonGatherResources.cxx | 33 ++- smtk/task/json/jsonGroup.cxx | 45 ++++ smtk/task/json/jsonGroup.h | 36 ++++ smtk/task/json/jsonManager.cxx | 39 +++- smtk/task/json/jsonManager.h | 2 +- smtk/task/json/jsonResourceAndRole.cxx | 41 ++++ smtk/task/json/jsonResourceAndRole.h | 38 ++++ smtk/task/json/jsonTask.cxx | 8 +- smtk/task/pybind11/PybindGatherResources.h | 18 +- smtk/task/pybind11/PybindManager.h | 6 +- smtk/task/testing/cxx/TestActiveTask.cxx | 12 +- smtk/task/testing/cxx/TestTaskBasics.cxx | 35 +-- smtk/task/testing/cxx/TestTaskGroup.cxx | 81 ++++--- smtk/task/testing/cxx/TestTaskJSON.cxx | 114 +++++++++- 46 files changed, 1586 insertions(+), 591 deletions(-) create mode 100644 doc/userguide/task/adaptors.rst create mode 100644 smtk/task/Adaptor.cxx create mode 100644 smtk/task/Adaptor.h create mode 100644 smtk/task/adaptor/Instances.h create mode 100644 smtk/task/adaptor/ResourceAndRole.cxx create mode 100644 smtk/task/adaptor/ResourceAndRole.h create mode 100644 smtk/task/json/Configurator.cxx create mode 100644 smtk/task/json/Configurator.h create mode 100644 smtk/task/json/Configurator.txx create mode 100644 smtk/task/json/jsonAdaptor.cxx create mode 100644 smtk/task/json/jsonAdaptor.h create mode 100644 smtk/task/json/jsonGroup.cxx create mode 100644 smtk/task/json/jsonGroup.h create mode 100644 smtk/task/json/jsonResourceAndRole.cxx create mode 100644 smtk/task/json/jsonResourceAndRole.h diff --git a/doc/release/notes/task-classes.rst b/doc/release/notes/task-classes.rst index a674beff5e..c071c7da6d 100644 --- a/doc/release/notes/task-classes.rst +++ b/doc/release/notes/task-classes.rst @@ -1,7 +1,8 @@ New task classes ================ -The task subsystem now provides more task types and additional tests. +The task subsystem now provides more task types, task-adaptor classes +for configuring tasks as they change state, and additional tests. See the `task class documentation`_ for details. .. _task class documentation: https://smtk.readthedocs.io/en/latest/userguide/task/classes.html diff --git a/doc/smtk.doxyfile.in b/doc/smtk.doxyfile.in index 8f8467ace6..b84db93803 100644 --- a/doc/smtk.doxyfile.in +++ b/doc/smtk.doxyfile.in @@ -735,6 +735,8 @@ INPUT = \ "@smtk_SOURCE_DIR@/smtk/project" \ "@smtk_SOURCE_DIR@/smtk/common" \ "@smtk_SOURCE_DIR@/smtk/task" \ + "@smtk_SOURCE_DIR@/smtk/task/adaptor" \ + "@smtk_SOURCE_DIR@/smtk/task/json" \ "@smtk_SOURCE_DIR@/smtk/workflow" \ "@smtk_SOURCE_DIR@/smtk/model" \ "@smtk_SOURCE_DIR@/smtk/model/operators" \ @@ -831,6 +833,8 @@ INPUT = \ "@smtk_BINARY_DIR@/smtk/project" \ "@smtk_BINARY_DIR@/smtk/common" \ "@smtk_BINARY_DIR@/smtk/task" \ + "@smtk_BINARY_DIR@/smtk/task/adaptor" \ + "@smtk_BINARY_DIR@/smtk/task/json" \ "@smtk_BINARY_DIR@/smtk/workflow" \ "@smtk_BINARY_DIR@/smtk/model" \ "@smtk_BINARY_DIR@/smtk/model/operators" \ diff --git a/doc/userguide/task/adaptors.rst b/doc/userguide/task/adaptors.rst new file mode 100644 index 0000000000..7da005b452 --- /dev/null +++ b/doc/userguide/task/adaptors.rst @@ -0,0 +1,62 @@ +.. _smtk-adaptor-classes: + +Guide to adaptor classes +======================== + +The following sections describe the different adaptor subclasses that +exist for use in your workflows, the use cases that motivate them, +and how to configure them. + +Adaptor +------- + +The :smtk:`base Adaptor class` is abstract and +cannot be instantiated on its own but does have configuration parameters +that are common to all adaptors and must be provided: + +* ``id``: this is an integer identifying the adaptor. + Although currently unused, all adaptor JSON should provide a unique, + non-zero value as it may be used in the future to reference adaptors + during configuration. +* ``type``: this is the fully-qualified class name of the adaptor + class that should be created. +* ``from``: an integer used to identify the task which should be + observed and used to configure another. +* ``to``: an integer used to identify the task which should be + configured when the "from" task has changed into a completable + or completed state. + +Example +""""""" + +.. code:: json + + { + "id": 1, + "type": "smtk::task::Adaptor", + "from": 1, + "to": 2 + } + +ResourceAndRole +--------------- + +The :smtk:`ResourceAndRole ` adaptor +exists to pass information from GatherResources to FillOutAttributes. +When configured, only attribute resources chosen by the user will be examined +for validity. + +The ResourceAndRole adaptor accepts no additional JSON configuration beyond the base +Adaptor class. + +Example +""""""" + +.. code:: json + + { + "id": 1, + "type": "smtk::task::adaptor::ResourceAndRole", + "from": 1, /* must be a GatherResources task */ + "to": 2 /* must be a FillOutAttributes task */ + } diff --git a/doc/userguide/task/classes.rst b/doc/userguide/task/classes.rst index f7709417b4..9e3737491f 100644 --- a/doc/userguide/task/classes.rst +++ b/doc/userguide/task/classes.rst @@ -41,17 +41,24 @@ dependent on its children. The Group has a "mode," which describes how children are related to one another: when the mode is parallel, children have no dependency on -one another and the group itself is dependent on all of its children. +one another; the parent group configures them independently. When the mode is serial, children must be completed in the order specified (i.e., each successive task is dependent on its -predecessor) and the group itself is dependent on the final child task. +predecessor) and each child task may configure its successor as +it becomes completable. Task groups are completable by default (i.e., when no children are configured). -If children exist, the group takes its state as a combination of its dependencies. -In serial mode, the group has a single internal dependency and its state is identical to -the dependency's state. If the group also has external dependencies on sibling tasks -(that are not children), its state also depends on them. -In parallel mode, the group has at least as many dependencies as children. +If children exist, the group takes its internal state as a combination of its children's +states: + +* irrelevant if all of its children are irrelevant; +* unavailable if all of its children are unavailable; +* incomplete if any of its children are incomplete; +* completable if all of its relevant children are completable; and +* completed when the user marks either it or all of its children completed. + +As with other task classes, the group's overall state also includes the state of +its external dependencies. The task Group class accepts all the JSON configuration that the base Task class does, plus: @@ -102,6 +109,13 @@ Example "type": "smtk::task::adaptor::PassComponents", "from": 1, "to": 2 + }, + { + "//": "How a child task configures its parent's" + "//": "output. Be careful to avoid loops." + "type": "smtk::task::adaptor::PassComponents", + "from": 2, + "to": 0 } ] } @@ -171,6 +185,12 @@ This task accepts all the JSON configuration that the base Task class does, plus If omitted, any role is allowed. * ``definitions``: a set of :smtk:`smtk::attribute::Definition` type-names specifying which types of attributes to validate before allowing completion. + * ``auto-configure``: either true or false (the default), depending on + whether resources with matching roles should automatically be added. + The default is false since a task-adaptor, such as + :smtk:`ResourceAndRole `, will + normally configure only those resources identified by a user as + relevant in a dependent task. Example """"""" diff --git a/doc/userguide/task/concepts.rst b/doc/userguide/task/concepts.rst index bb933e14b2..786b9ede74 100644 --- a/doc/userguide/task/concepts.rst +++ b/doc/userguide/task/concepts.rst @@ -27,9 +27,9 @@ that connect completed tasks to incomplete tasks. is unavailable until its dependencies are met, at which time it will transition straight to completable. -:smtk:`Instances ` +:smtk:`Task instance-tracker and factory ` is used to create instances of registered task classes. - Any plugins that provide new task subclasses should + Any plugins that provide new Task subclasses should register those classes with the factory in their registrar (see :ref:`smtk-plugin-sys`). @@ -42,6 +42,20 @@ that connect completed tasks to incomplete tasks. thus should not be active. This object can be observed for changes to the active task. +:smtk:`Adaptor ` + instances configure a dependent task when the dependency + changes state. This way, information provided by the user + can have an effect on the state and user-interface of + subsequent tasks. + You should subclass this to implement logic that determines what + information should be transmitted from one task to another. + +:smtk:`Adaptor instance-tracker and factory ` + is used to create instances of registered adaptor classes. + Any plugins that provide new Adaptor subclasses should + register those classes with the factory in their registrar + (see :ref:`smtk-plugin-sys`). + :smtk:`Manager ` is an object applications can create to hold a task factory and the set of task instances the factory has created. diff --git a/doc/userguide/task/index.rst b/doc/userguide/task/index.rst index c4b9aaa2ae..1667d2a5be 100644 --- a/doc/userguide/task/index.rst +++ b/doc/userguide/task/index.rst @@ -13,3 +13,4 @@ These tasks form a graph with dependencies as arcs. concepts.rst classes.rst + adaptors.rst diff --git a/smtk/doc.h b/smtk/doc.h index 53eeb9428b..56dd6eed08 100644 --- a/smtk/doc.h +++ b/smtk/doc.h @@ -149,6 +149,26 @@ namespace io { } +/**\brief User-interface tasks. + * + */ +namespace task +{ +/**\brief Adaptors that configure downstream tasks. + * + */ +namespace adaptor +{ +} + +/**\brief JSON serialization and deserialization of tasks. + * + */ +namespace json +{ +} +} // namespace task + /**\brief workflow managment. * */ diff --git a/smtk/task/Adaptor.cxx b/smtk/task/Adaptor.cxx new file mode 100644 index 0000000000..19c73b6237 --- /dev/null +++ b/smtk/task/Adaptor.cxx @@ -0,0 +1,44 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/Adaptor.h" + +namespace smtk +{ +namespace task +{ + +Adaptor::Adaptor() = default; + +Adaptor::Adaptor(const Configuration& config) +{ + (void)config; // subclasses may use this +} + +Adaptor::Adaptor( + const Configuration& config, + std::shared_ptr& from, + std::shared_ptr& to) + : m_from(from) + , m_to(to) +{ + (void)config; // subclasses may use this + if (from) + { + m_observer = from->observers().insert([this](Task&, State prev, State next) { + if (prev < next && next >= State::Completable) + { + this->reconfigureTask(); + } + }); + } +} + +} // namespace task +} // namespace smtk diff --git a/smtk/task/Adaptor.h b/smtk/task/Adaptor.h new file mode 100644 index 0000000000..e894ee0424 --- /dev/null +++ b/smtk/task/Adaptor.h @@ -0,0 +1,57 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= + +#ifndef smtk_task_Adaptor_h +#define smtk_task_Adaptor_h + +#include "smtk/task/Task.h" + +namespace smtk +{ +namespace task +{ + +/// This object provides applications a way to configure a task using +/// information adapted from its dependencies. +class SMTKCORE_EXPORT Adaptor : smtkEnableSharedPtr(Adaptor) +{ +public: + smtkTypeMacroBase(smtk::task::Adaptor); + + /// Task adaptors are configured using JSON. + using Configuration = nlohmann::json; + + /// Construct an unconfigured adaptor. + Adaptor(); + Adaptor(const Configuration& config); + Adaptor(const Configuration& config, std::shared_ptr& from, std::shared_ptr& to); + + /// Destructor must be virtual. + virtual ~Adaptor() = default; + + /// Subclasses must implement this to reconfigure the "to()" task. + /// This method is called when the "from()" task changes into a + /// completable state. + virtual bool reconfigureTask() = 0; + + /// The task this adaptor uses to fetch configuration parameters. + std::shared_ptr from() const { return m_from.lock(); } + /// The task to which this adaptor applies configuration parameters. + std::shared_ptr to() const { return m_to.lock(); } + +protected: + std::weak_ptr m_from; + std::weak_ptr m_to; + smtk::task::Task::Observers::Key m_observer; +}; +} // namespace task +} // namespace smtk + +#endif // smtk_task_Adaptor_h diff --git a/smtk/task/CMakeLists.txt b/smtk/task/CMakeLists.txt index 5f4de12b50..6133288866 100644 --- a/smtk/task/CMakeLists.txt +++ b/smtk/task/CMakeLists.txt @@ -1,34 +1,52 @@ +# Tasks are classes (with header and source files) that also have +# JSON classes (with header and source files). +set(tasks + Task + FillOutAttributes + GatherResources + Group +) + +set(adaptors + ResourceAndRole +) + set(taskSrcs Active.cxx - FillOutAttributes.cxx + Adaptor.cxx Manager.cxx Registrar.cxx - Task.cxx - GatherResources.cxx - Group.cxx + adaptor/ResourceAndRole.cxx + json/Configurator.cxx json/Helper.cxx - json/jsonFillOutAttributes.cxx - json/jsonGatherResources.cxx json/jsonManager.cxx - json/jsonTask.cxx + json/jsonAdaptor.cxx ) set(taskHeaders Active.h - FillOutAttributes.h + Adaptor.h Instances.h Manager.h Registrar.h - Task.h - GatherResources.h - Group.h + adaptor/ResourceAndRole.h + json/Adaptor.h + json/Configurator.h + json/Configurator.txx json/Helper.h - json/jsonFillOutAttributes.h - json/jsonGatherResources.h json/jsonManager.h - json/jsonTask.h ) +foreach(task ${tasks}) + list(APPEND taskSrcs ${task}.cxx json/json${task}.cxx) + list(APPEND taskHeaders ${task}.h json/json${task}.h) +endforeach() + +foreach(adaptor ${adaptors}) + list(APPEND taskSrcs adaptor/${adaptor}.cxx json/json${adaptor}.cxx) + list(APPEND taskHeaders adaptor/${adaptor}.h json/json${adaptor}.h) +endforeach() + if (SMTK_ENABLE_PYTHON_WRAPPING) add_subdirectory(pybind11) endif() diff --git a/smtk/task/FillOutAttributes.cxx b/smtk/task/FillOutAttributes.cxx index f1f8bc164a..b90c188dde 100644 --- a/smtk/task/FillOutAttributes.cxx +++ b/smtk/task/FillOutAttributes.cxx @@ -87,7 +87,7 @@ smtk::common::Visit FillOutAttributes::visitAttributeSets(AttributeSetVisitor vi { return smtk::common::Visit::Halt; } - for (const auto& entry : m_attributeSets) + for (auto& entry : m_attributeSets) { if (visitor(entry) == smtk::common::Visit::Halt) { @@ -115,9 +115,12 @@ bool FillOutAttributes::initializeResources() if ( attributeSet.m_role.empty() || attributeSet.m_role == "*" || attributeSet.m_role == role) { - foundResource = true; - auto it = attributeSet.m_resources.insert({ resource->id(), { {}, {} } }).first; - this->updateResourceEntry(*resource, attributeSet, it->second); + if (attributeSet.m_autoconfigure) + { + foundResource = true; + auto it = attributeSet.m_resources.insert({ resource->id(), { {}, {} } }).first; + this->updateResourceEntry(*resource, attributeSet, it->second); + } } } } @@ -241,8 +244,11 @@ int FillOutAttributes::update( else if ( predicate.m_role == role || predicate.m_role == "*" || predicate.m_role.empty()) { - it = predicate.m_resources.insert({ resource->id(), { {}, {} } }).first; - doUpdate = true; + if (predicate.m_autoconfigure) + { + it = predicate.m_resources.insert({ resource->id(), { {}, {} } }).first; + doUpdate = true; + } } if (doUpdate) { @@ -266,10 +272,12 @@ int FillOutAttributes::update( State FillOutAttributes::computeInternalState() const { State s = State::Completable; + bool empty = true; for (const auto& predicate : m_attributeSets) { for (const auto& resourceEntry : predicate.m_resources) { + empty &= resourceEntry.second.m_valid.empty() && resourceEntry.second.m_invalid.empty(); if (!resourceEntry.second.m_invalid.empty()) { s = State::Incomplete; @@ -277,6 +285,10 @@ State FillOutAttributes::computeInternalState() const } } } + if (empty) + { + s = State::Irrelevant; + } return s; } diff --git a/smtk/task/FillOutAttributes.h b/smtk/task/FillOutAttributes.h index 72a3712fc6..385e7d6e13 100644 --- a/smtk/task/FillOutAttributes.h +++ b/smtk/task/FillOutAttributes.h @@ -22,6 +22,11 @@ namespace smtk { namespace task { +// Forward declaration +namespace adaptor +{ +class ResourceAndRole; +} /**\brief FillOutAttributes is a task that is incomplete until specified * attributes are valid. @@ -57,11 +62,18 @@ public: std::string m_role; /// The definitions in matching resources whose attributes should be valid. std::set m_definitions; + /// Should all resources with a matching role be added? + /// + /// If false (default), then resources must be explicitly configured by UUID + /// or configured by a task adaptor. + /// If true, then all resources with a matching role will have attributes + /// matching m_definitions checked. + bool m_autoconfigure = false; /// The set of resources being managed that are selected by the validator. std::map m_resources; }; - /// Signature of functors that visit resources-by-role predicates. - using AttributeSetVisitor = std::function; + /// Signatures of functors that visit resources-by-role predicates. + using AttributeSetVisitor = std::function; FillOutAttributes(); FillOutAttributes( @@ -79,6 +91,8 @@ public: smtk::common::Visit visitAttributeSets(AttributeSetVisitor visitor); protected: + friend class adaptor::ResourceAndRole; + /// Initialize with a list of resources from manager in m_managers. bool initializeResources(); /// Update a single resource in a predicate diff --git a/smtk/task/GatherResources.cxx b/smtk/task/GatherResources.cxx index 2658f7b847..cab6616125 100644 --- a/smtk/task/GatherResources.cxx +++ b/smtk/task/GatherResources.cxx @@ -21,7 +21,7 @@ namespace smtk namespace task { -void to_json(json& j, const GatherResources::Predicate& p) +void to_json(json& j, const GatherResources::ResourceSet& p) { j = json{ { "role", p.m_role }, { "type", p.m_type } }; if (p.m_minimumCount == 0 && p.m_maximumCount < 0) @@ -39,7 +39,7 @@ void to_json(json& j, const GatherResources::Predicate& p) } } -void from_json(const json& j, GatherResources::Predicate& p) +void from_json(const json& j, GatherResources::ResourceSet& p) { if (j.contains("role")) { @@ -74,7 +74,7 @@ void from_json(const json& j, GatherResources::Predicate& p) // Accept any resource p.m_validator = nullptr; /* - [](const smtk::resource::Resource&, const TaskNeedsResource::Predicate&) + [](const smtk::resource::Resource&, const TaskNeedsResource::ResourceSet&) { return true; }; */ } @@ -115,9 +115,9 @@ void GatherResources::configure(const Configuration& config) continue; } - Predicate predicate; - spec.get_to(predicate); - m_resourcesByRole[predicate.m_role] = spec.get(); + ResourceSet resourceSet; + spec.get_to(resourceSet); + m_resourcesByRole[resourceSet.m_role] = spec.get(); } } } @@ -140,7 +140,7 @@ void GatherResources::configure(const Configuration& config) } } -smtk::common::Visit GatherResources::visitPredicates(PredicateVisitor visitor) +smtk::common::Visit GatherResources::visitResourceSets(ResourceSetVisitor visitor) { if (!visitor) { @@ -160,7 +160,7 @@ void GatherResources::updateResources( smtk::resource::Resource& resource, smtk::resource::EventType event) { - bool predicatesUpdated = false; + bool resourceSetsUpdated = false; auto resourcePtr = resource.shared_from_this(); switch (event) { @@ -174,7 +174,7 @@ void GatherResources::updateResources( if (!it->second.m_validator || it->second.m_validator(resource, it->second)) { it->second.m_resources.insert(resourcePtr); - predicatesUpdated = true; + resourceSetsUpdated = true; } } } @@ -186,7 +186,7 @@ void GatherResources::updateResources( auto it = m_resourcesByRole.find(role); if (it != m_resourcesByRole.end()) { - predicatesUpdated = it->second.m_resources.erase(resourcePtr) > 0; + resourceSetsUpdated = it->second.m_resources.erase(resourcePtr) > 0; } } break; @@ -194,7 +194,7 @@ void GatherResources::updateResources( // TODO break; } - if (predicatesUpdated) + if (resourceSetsUpdated) { this->internalStateChanged(this->computeInternalState()); } @@ -205,14 +205,14 @@ State GatherResources::computeInternalState() const State s = State::Completable; for (const auto& entry : m_resourcesByRole) { - const auto& predicate(entry.second); - if (predicate.m_resources.size() < static_cast(predicate.m_minimumCount)) + const auto& resourceSet(entry.second); + if (resourceSet.m_resources.size() < static_cast(resourceSet.m_minimumCount)) { s = State::Incomplete; } else if ( - predicate.m_maximumCount >= 0 && - predicate.m_resources.size() > static_cast(predicate.m_maximumCount)) + resourceSet.m_maximumCount >= 0 && + resourceSet.m_resources.size() > static_cast(resourceSet.m_maximumCount)) { s = State::Incomplete; } diff --git a/smtk/task/GatherResources.h b/smtk/task/GatherResources.h index 6a92d9ff26..1d8163ec9b 100644 --- a/smtk/task/GatherResources.h +++ b/smtk/task/GatherResources.h @@ -36,7 +36,7 @@ public: smtkCreateMacro(smtk::task::Task); /// A predicate used to collect resources that fit a given role. - struct Predicate + struct ResourceSet { using Entry = std::weak_ptr; /// The required role. If empty, any role is allowed. @@ -47,19 +47,19 @@ public: unsigned int m_minimumCount; /// The maximum number of resources that can be collected while still satisfying the requirement. /// - /// Note that if 0, the predicate is forcing the task to reject all resources + /// Note that if 0, the resourceSet is forcing the task to reject all resources /// that the validator selects (i.e., no resources of the given type are allowed). /// If negative, then there is no maximum number of validated resources. int m_maximumCount; /// The resource typename regex; typically just a resource typename. std::string m_type; /// A lambda used to determine whether the given resource is acceptable. - std::function m_validator; + std::function m_validator; /// The set of resources being managed that are selected by the validator. std::set> m_resources; }; /// Signature of functors that visit resources-by-role predicates. - using PredicateVisitor = std::function; + using ResourceSetVisitor = std::function; GatherResources(); GatherResources( @@ -74,7 +74,7 @@ public: void configure(const Configuration& config); - smtk::common::Visit visitPredicates(PredicateVisitor visitor); + smtk::common::Visit visitResourceSets(ResourceSetVisitor visitor); protected: /// Respond to resource changes that may change task state. @@ -85,7 +85,7 @@ protected: smtk::common::Managers::Ptr m_managers; smtk::resource::Observers::Key m_observer; - std::map m_resourcesByRole; + std::map m_resourcesByRole; }; } // namespace task } // namespace smtk diff --git a/smtk/task/Group.cxx b/smtk/task/Group.cxx index c0b393c1ae..a7eb96f87c 100644 --- a/smtk/task/Group.cxx +++ b/smtk/task/Group.cxx @@ -7,12 +7,12 @@ // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //========================================================================= -#include "smtk/task/FillOutAttributes.h" +#include "smtk/task/Group.h" #include "smtk/project/ResourceContainer.h" #include "smtk/task/json/Helper.h" -#include "smtk/task/json/jsonFillOutAttributes.h" +#include "smtk/task/json/jsonGroup.h" #include "smtk/operation/Manager.h" #include "smtk/operation/SpecificationOps.h" @@ -29,18 +29,16 @@ namespace smtk namespace task { -FillOutAttributes::FillOutAttributes() = default; +Group::Group() = default; -FillOutAttributes::FillOutAttributes( - const Configuration& config, - const smtk::common::Managers::Ptr& managers) +Group::Group(const Configuration& config, const smtk::common::Managers::Ptr& managers) : Task(config, managers) , m_managers(managers) { this->configure(config); } -FillOutAttributes::FillOutAttributes( +Group::Group( const Configuration& config, const PassedDependencies& dependencies, const smtk::common::Managers::Ptr& managers) @@ -50,230 +48,31 @@ FillOutAttributes::FillOutAttributes( this->configure(config); } -void FillOutAttributes::configure(const Configuration& config) -{ - // The predicate from_json method needs the resource manager: - auto& helper = json::Helper::instance(); - helper.setManagers(m_managers); - - if (config.contains("attribute-sets")) - { - config.at("attribute-sets").get_to(m_attributeSets); - } - if (m_managers) - { - if (auto operationManager = m_managers->get()) - { - m_observer = operationManager->observers().insert( - [this]( - const smtk::operation::Operation& op, - smtk::operation::EventType event, - smtk::operation::Operation::Result result) { return this->update(op, event, result); }, - /* priority */ 0, - /* initialize */ true, - "FillOutAttributes monitors operations for updates."); - } - } - if (!m_attributeSets.empty()) - { - this->initializeResources(); - this->internalStateChanged(this->computeInternalState()); - } -} - -smtk::common::Visit FillOutAttributes::visitAttributeSets(AttributeSetVisitor visitor) -{ - if (!visitor) - { - return smtk::common::Visit::Halt; - } - for (const auto& entry : m_attributeSets) - { - if (visitor(entry) == smtk::common::Visit::Halt) - { - return smtk::common::Visit::Halt; - } - } - return smtk::common::Visit::Continue; -} - -bool FillOutAttributes::initializeResources() +void Group::configure(const Configuration& config) { - bool foundResource = false; - if (m_attributeSets.empty()) - { - return foundResource; - } - if (auto resourceManager = m_managers->get()) - { - auto resources = resourceManager->find(); - for (auto resource : resources) - { - const std::string& role = smtk::project::detail::role(resource); - for (auto& attributeSet : m_attributeSets) - { - if ( - attributeSet.m_role.empty() || attributeSet.m_role == "*" || attributeSet.m_role == role) - { - foundResource = true; - auto it = attributeSet.m_resources.insert({ resource->id(), { {}, {} } }).first; - this->updateResourceEntry(*resource, attributeSet, it->second); - } - } - } - } - return foundResource; + // Instantiate children and configure them } -bool FillOutAttributes::updateResourceEntry( - smtk::attribute::Resource& resource, - const AttributeSet& predicate, - ResourceAttributes& entry) +std::vector Group::children() const { - bool changesMade = false; - // I. Remove invalid entries for attributes that are valid or deleted. - std::set expunged; - std::set validated; - std::set invalidated; - for (const auto& invalidId : entry.m_invalid) - { - auto att = resource.findAttribute(invalidId); - if (att) - { - if (att->isValid()) // TODO: accept predicate override for categories? - { - validated.insert(invalidId); - } - } - else - { - expunged.insert(invalidId); - } - } - // II. Check valid attributes to see if they have been invalidated or expunged. - for (const auto& validId : entry.m_valid) - { - auto att = resource.findAttribute(validId); - if (att) - { - if (!att->isValid()) // TODO: accept predicate override for categories? - { - invalidated.insert(validId); - } - } - else - { - expunged.insert(validId); - } - } - // If the set of invalid attributes was changed, we need to re-run computeInternalState(). - changesMade |= !expunged.empty() || !validated.empty() || !invalidated.empty(); - for (const auto& id : validated) - { - entry.m_invalid.erase(id); - entry.m_valid.insert(id); - } - for (const auto& id : expunged) - { - entry.m_invalid.erase(id); - } - for (const auto& id : invalidated) - { - entry.m_invalid.insert(id); - entry.m_valid.erase(id); - } - // II. Check for newly-created attributes - std::vector attributes; - for (const auto& definition : predicate.m_definitions) - { - resource.findAttributes(definition, attributes); - for (const auto& attribute : attributes) - { - auto uid = attribute->id(); - if ( - (entry.m_invalid.find(uid) == entry.m_invalid.end()) && - (entry.m_valid.find(uid) == entry.m_valid.end())) - { - // We've found a new attribute. Classify it. - changesMade = true; - if (attribute->isValid()) // TODO: accept predicate override for categories? - { - entry.m_valid.insert(uid); - } - else - { - entry.m_invalid.insert(uid); - } - } - } - } - return changesMade; -} - -int FillOutAttributes::update( - const smtk::operation::Operation& op, - smtk::operation::EventType event, - smtk::operation::Operation::Result result) -{ - bool predicatesUpdated = false; - switch (event) - { - case smtk::operation::EventType::DID_OPERATE: - { - auto mentionedResources = smtk::operation::extractResources(result); - - for (auto& weakResource : mentionedResources) - { - auto resource = std::dynamic_pointer_cast(weakResource.lock()); - if (resource) - { - const std::string& role = smtk::project::detail::role(resource); - // Do we care about this resource? - for (auto& predicate : m_attributeSets) - { - auto it = predicate.m_resources.find(resource->id()); - bool doUpdate = false; - if (it != predicate.m_resources.end()) - { - doUpdate = true; - } - else if ( - predicate.m_role == role || predicate.m_role == "*" || predicate.m_role.empty()) - { - it = predicate.m_resources.insert({ resource->id(), { {}, {} } }).first; - doUpdate = true; - } - if (doUpdate) - { - predicatesUpdated |= this->updateResourceEntry(*resource, predicate, it->second); - } - } - } - } - } - break; - case smtk::operation::EventType::WILL_OPERATE: - break; - } - if (predicatesUpdated) + std::vector result; + result.reserve(m_children.size()); + for (const auto& entry : m_children) { - this->internalStateChanged(this->computeInternalState()); + result.push_back(entry.first); } - return 0; + return result; } -State FillOutAttributes::computeInternalState() const +State Group::computeInternalState() const { State s = State::Completable; - for (const auto& predicate : m_attributeSets) + for (const auto& entry : m_children) { - for (const auto& resourceEntry : predicate.m_resources) + auto childState = entry.first->state(); + if (childState < s) { - if (!resourceEntry.second.m_invalid.empty()) - { - s = State::Incomplete; - return s; - } + s = childState; } } return s; diff --git a/smtk/task/Group.h b/smtk/task/Group.h index 72a3712fc6..e5d2f1dc5a 100644 --- a/smtk/task/Group.h +++ b/smtk/task/Group.h @@ -7,8 +7,8 @@ // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //========================================================================= -#ifndef smtk_task_FillOutAttributes_h -#define smtk_task_FillOutAttributes_h +#ifndef smtk_task_Group_h +#define smtk_task_Group_h #include "smtk/operation/Manager.h" #include "smtk/operation/Observer.h" @@ -23,83 +23,50 @@ namespace smtk namespace task { -/**\brief FillOutAttributes is a task that is incomplete until specified - * attributes are valid. +/**\brief Group is a task that owns children and draws its state from them. + * + * A task group exists to organize a set of tasks. + * + * The Group instance is responsible for configuring its children, including + * creating dependencies among them. The Group's state and output are + * dependent on its children. + * + * The group has a "mode," which describes how children are related to + * one another: when the mode is parallel, children have no dependency on + * one another and the group itself is dependent on all of its children. + * When the mode is serial, children must be completed in the + * order specified (i.e., each successive task is dependent on its + * predecessor) and the group itself is dependent on the final child task. * - * This task accepts an input attribute resource (configured by a predecessor - * task or specified via a role) and observe an operation manager for operations. - * After each operation, attributes with a definition are validated. - * If all attributes identify are valid, the task becomes completable. - * Otherwise, the task will remain (or become) incomplete. */ -class SMTKCORE_EXPORT FillOutAttributes : public Task +class SMTKCORE_EXPORT Group : public Task { public: - smtkTypeMacro(smtk::task::FillOutAttributes); + smtkTypeMacro(smtk::task::Group); smtkSuperclassMacro(smtk::task::Task); smtkCreateMacro(smtk::task::Task); - /// Per-resource sets of validated attributes - /// - /// We need to track attributes so incremental updates - /// can decide whether to change state. - struct ResourceAttributes - { - /// Attributes matching a definition that are validated. - std::set m_valid; - /// Attributes matching a definition that need attention. - std::set m_invalid; - }; - /// A predicate used to collect resources that fit a given role. - struct AttributeSet - { - /// The required role. If empty, any role is allowed. - std::string m_role; - /// The definitions in matching resources whose attributes should be valid. - std::set m_definitions; - /// The set of resources being managed that are selected by the validator. - std::map m_resources; - }; - /// Signature of functors that visit resources-by-role predicates. - using AttributeSetVisitor = std::function; - - FillOutAttributes(); - FillOutAttributes( - const Configuration& config, - const smtk::common::Managers::Ptr& managers = nullptr); - FillOutAttributes( + Group(); + Group(const Configuration& config, const smtk::common::Managers::Ptr& managers = nullptr); + Group( const Configuration& config, const PassedDependencies& dependencies, const smtk::common::Managers::Ptr& managers = nullptr); - ~FillOutAttributes() override = default; + ~Group() override = default; void configure(const Configuration& config); - smtk::common::Visit visitAttributeSets(AttributeSetVisitor visitor); + std::vector children() const; protected: - /// Initialize with a list of resources from manager in m_managers. - bool initializeResources(); - /// Update a single resource in a predicate - bool updateResourceEntry( - smtk::attribute::Resource& resource, - const AttributeSet& predicate, - ResourceAttributes& entry); - /// Respond to operations that may change task state. - int update( - const smtk::operation::Operation& op, - smtk::operation::EventType event, - smtk::operation::Operation::Result result); - /// Check m_resourcesByRole to see if all requirements are met. State computeInternalState() const; smtk::common::Managers::Ptr m_managers; - smtk::operation::Observers::Key m_observer; - std::vector m_attributeSets; + std::map m_children; }; } // namespace task } // namespace smtk -#endif // smtk_task_FillOutAttributes_h +#endif // smtk_task_Group_h diff --git a/smtk/task/Manager.cxx b/smtk/task/Manager.cxx index f4ed4c6c28..3b7f7bdb4e 100644 --- a/smtk/task/Manager.cxx +++ b/smtk/task/Manager.cxx @@ -16,7 +16,7 @@ namespace task { Manager::Manager() - : m_active(&m_instances) + : m_active(&m_taskInstances) { } diff --git a/smtk/task/Manager.h b/smtk/task/Manager.h index 7e46c7aea7..153b87c205 100644 --- a/smtk/task/Manager.h +++ b/smtk/task/Manager.h @@ -19,8 +19,10 @@ #include "smtk/common/TypeName.h" #include "smtk/task/Active.h" +#include "smtk/task/Adaptor.h" #include "smtk/task/Instances.h" #include "smtk/task/Task.h" +#include "smtk/task/adaptor/Instances.h" #include #include @@ -46,24 +48,34 @@ public: virtual ~Manager(); /// Managed instances of Task objects (and a registry of Task classes). - using Instances = smtk::task::Instances; + using TaskInstances = smtk::task::Instances; /// Return the set of managed task instances. /// /// This class also acts as a registrar for Task subclasses. - Instances& instances() { return m_instances; } - const Instances& instances() const { return m_instances; } + TaskInstances& taskInstances() { return m_taskInstances; } + const TaskInstances& taskInstances() const { return m_taskInstances; } /// Return a tracker for the active task. Active& active() { return m_active; } const Active& active() const { return m_active; } + /// Managed instances of Adaptor objects (and a registry of Adaptor classes). + using AdaptorInstances = smtk::task::adaptor::Instances; + + /// Return the set of managed adaptor instances. + /// + /// This class also acts as a registrar for Adaptor subclasses. + AdaptorInstances& adaptorInstances() { return m_adaptorInstances; } + const AdaptorInstances& adaptorInstances() const { return m_adaptorInstances; } + /// Return the managers instance that contains this manager, if it exists. smtk::common::Managers::Ptr managers() const { return m_managers.lock(); } void setManagers(const smtk::common::Managers::Ptr& managers) { m_managers = managers; } private: - Instances m_instances; + TaskInstances m_taskInstances; + AdaptorInstances m_adaptorInstances; Active m_active; std::weak_ptr m_managers; diff --git a/smtk/task/Registrar.cxx b/smtk/task/Registrar.cxx index f7d183dfec..c5d35b34f2 100644 --- a/smtk/task/Registrar.cxx +++ b/smtk/task/Registrar.cxx @@ -11,12 +11,19 @@ //============================================================================= #include "smtk/task/Registrar.h" +#include "smtk/task/Adaptor.h" #include "smtk/task/FillOutAttributes.h" #include "smtk/task/GatherResources.h" +#include "smtk/task/Group.h" #include "smtk/task/Task.h" -#include "smtk/task/json/Helper.h" +#include "smtk/task/adaptor/ResourceAndRole.h" +#include "smtk/task/json/Configurator.h" +#include "smtk/task/json/Configurator.txx" +#include "smtk/task/json/jsonAdaptor.h" #include "smtk/task/json/jsonFillOutAttributes.h" #include "smtk/task/json/jsonGatherResources.h" +#include "smtk/task/json/jsonGroup.h" +#include "smtk/task/json/jsonResourceAndRole.h" #include "smtk/task/json/jsonTask.h" #include "smtk/plugin/Manager.h" @@ -28,8 +35,11 @@ namespace smtk namespace task { -using TaskList = std::tuple; -using JSONList = std::tuple; +using TaskList = std::tuple; +using TaskJSON = std:: + tuple; +using AdaptorList = std::tuple; +using AdaptorJSON = std::tuple; void Registrar::registerTo(const smtk::common::Managers::Ptr& managers) { @@ -58,16 +68,24 @@ void Registrar::unregisterFrom(const smtk::resource::Manager::Ptr& resourceManag void Registrar::registerTo(const smtk::task::Manager::Ptr& taskManager) { - auto& instances = taskManager->instances(); - instances.registerTypes(); - json::Helper::registerTypes(); + auto& taskInstances = taskManager->taskInstances(); + taskInstances.registerTypes(); + json::Configurator::registerTypes(); + + auto& adaptorInstances = taskManager->adaptorInstances(); + adaptorInstances.registerTypes(); + json::Configurator::registerTypes(); } void Registrar::unregisterFrom(const smtk::task::Manager::Ptr& taskManager) { - auto& instances = taskManager->instances(); - instances.unregisterTypes(); - json::Helper::unregisterTypes(); + auto& taskInstances = taskManager->taskInstances(); + taskInstances.unregisterTypes(); + json::Configurator::unregisterTypes(); + + auto& adaptorInstances = taskManager->adaptorInstances(); + adaptorInstances.unregisterTypes(); + json::Configurator::unregisterTypes(); } } // namespace task diff --git a/smtk/task/State.h b/smtk/task/State.h index 2cd73a0d88..59e13566ad 100644 --- a/smtk/task/State.h +++ b/smtk/task/State.h @@ -28,6 +28,7 @@ namespace task /// The set of states that a task may take on. enum class State { + Irrelevant, //!< The user's work in prior tasks mean this task needs no user input. Unavailable, //!< The task's dependencies are unmet. Incomplete, //!< The task is available but its objective is not accomplished. Completable, //!< The task is available and accomplished but has not been marked complete. @@ -37,8 +38,8 @@ enum class State /// A type-conversion operation to cast enumerants to strings. inline std::string stateName(const State& s) { - static std::array names{ - { "unavailable", "incomplete", "completable", "completed" } + static std::array names{ + { "irrelevant", "unavailable", "incomplete", "completable", "completed" } }; return names[static_cast(s)]; } @@ -54,6 +55,10 @@ inline State stateEnum(const std::string& s) { stateName = stateName.substr(7); } + if (stateName == "unavailable") + { + return State::Unavailable; + } if (stateName == "incomplete") { return State::Incomplete; @@ -66,7 +71,7 @@ inline State stateEnum(const std::string& s) { return State::Completed; } - return State::Unavailable; + return State::Irrelevant; } /// States may be appended to streams. diff --git a/smtk/task/Task.cxx b/smtk/task/Task.cxx index d710b9bade..ad32624606 100644 --- a/smtk/task/Task.cxx +++ b/smtk/task/Task.cxx @@ -75,6 +75,7 @@ State Task::state() const case State::Incomplete: s = State::Unavailable; break; + case State::Irrelevant: case State::Completable: case State::Completed: break; @@ -95,6 +96,7 @@ bool Task::markCompleted(bool completed) { switch (this->state()) { + case State::Irrelevant: // fall through case State::Unavailable: // fall through case State::Incomplete: return false; diff --git a/smtk/task/adaptor/Instances.h b/smtk/task/adaptor/Instances.h new file mode 100644 index 0000000000..802eca0b5d --- /dev/null +++ b/smtk/task/adaptor/Instances.h @@ -0,0 +1,45 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +/*! \file */ +#ifndef smtk_task_adaptor_Instances_h +#define smtk_task_adaptor_Instances_h + +#include "smtk/common/Instances.h" +#include "smtk/common/Managers.h" +#include "smtk/common/TypeName.h" + +#include "smtk/task/Task.h" + +namespace smtk +{ +namespace task +{ +namespace adaptor +{ + +/// Track smtk::task::Adaptor objects with smtk::common::Instances. +using Instances = smtk::common::Instances< + smtk::task::Adaptor, + // Constructor variant: default (no arguments) + void, + // Constructor variant: configuration, no tasks + std::tuple, + // Constructor variant: configuration and tasks. + std::tuple< + smtk::task::Adaptor::Configuration&, // JSON configuration information + smtk::task::Task::Ptr, // Source task (from) + smtk::task::Task::Ptr // Task to be configured (to) + >>; + +} // namespace adaptor +} // namespace task +} // namespace smtk + +#endif // smtk_task_adaptor_Instances_h diff --git a/smtk/task/adaptor/ResourceAndRole.cxx b/smtk/task/adaptor/ResourceAndRole.cxx new file mode 100644 index 0000000000..a53d280a41 --- /dev/null +++ b/smtk/task/adaptor/ResourceAndRole.cxx @@ -0,0 +1,127 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= + +#include "smtk/task/adaptor/ResourceAndRole.h" + +#include "smtk/task/FillOutAttributes.h" +#include "smtk/task/GatherResources.h" + +#include "smtk/attribute/Resource.h" + +#include "smtk/io/Logger.h" + +namespace smtk +{ +namespace task +{ +namespace adaptor +{ + +ResourceAndRole::ResourceAndRole() = default; +ResourceAndRole::ResourceAndRole(const Configuration& config) +{ + this->configureSelf(config); +} + +ResourceAndRole::ResourceAndRole( + const Configuration& config, + std::shared_ptr& from, + std::shared_ptr& to) + : Superclass(config, from, to) +{ + this->configureSelf(config); +} + +bool ResourceAndRole::reconfigureTask() +{ + bool didChange = false; + if (auto source = this->from()) + { + if (auto gather = std::dynamic_pointer_cast(source)) + { + auto dest = this->to(); + std::map> configs; + gather->visitResourceSets( + [&configs](const GatherResources::ResourceSet& resourceSet) -> smtk::common::Visit { + configs[resourceSet.m_role].insert( + resourceSet.m_resources.begin(), resourceSet.m_resources.end()); + return smtk::common::Visit::Continue; + }); + if (auto fill = std::dynamic_pointer_cast(dest)) + { + // Look for matching roles in attribute sets and populate with + // the gathered resources. + auto visitor = + [&configs, &fill, &didChange](FillOutAttributes::AttributeSet& attributeSet) { + auto it = configs.find(attributeSet.m_role); + if (it != configs.end()) + { + std::set unused; + // Fill "unused" with all resource IDs being tracked. + for (const auto& entry : attributeSet.m_resources) + { + unused.insert(entry.first); + } + // Add entries from GatherResource's output and remove from unused. + for (const auto& resource : it->second) + { + auto asit = attributeSet.m_resources.find(resource->id()); + if (asit != attributeSet.m_resources.end()) + { + unused.erase(resource->id()); + } + else + { + didChange = true; + asit = attributeSet.m_resources.insert({ resource->id(), { {}, {} } }).first; + fill->updateResourceEntry( + *(dynamic_cast(resource.get())), + attributeSet, + asit->second); + } + } + // Remove unused (no longer relevant resources) + for (const auto& uid : unused) + { + attributeSet.m_resources.erase(uid); + didChange = true; + } + } + return smtk::common::Visit::Continue; + }; + fill->visitAttributeSets(visitor); + if (didChange) + { + fill->internalStateChanged(fill->computeInternalState()); + } + } + else + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Cannot configure resource and role on a \"" << (dest ? dest->typeName() : "null") + << "\" task."); + } + } + else + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Cannot adapt resource and role from a \"" << source->typeName() << "\"."); + } + } + return didChange; +} + +void ResourceAndRole::configureSelf(const Configuration& config) {} + +} // namespace adaptor +} // namespace task +} // namespace smtk diff --git a/smtk/task/adaptor/ResourceAndRole.h b/smtk/task/adaptor/ResourceAndRole.h new file mode 100644 index 0000000000..892e5c1657 --- /dev/null +++ b/smtk/task/adaptor/ResourceAndRole.h @@ -0,0 +1,52 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= + +#ifndef smtk_task_adaptor_ResourceAndRole_h +#define smtk_task_adaptor_ResourceAndRole_h + +#include "smtk/task/Adaptor.h" + +namespace smtk +{ +namespace task +{ +namespace adaptor +{ + +/// Configure a task with a resource and role given a dependent producer. +class SMTKCORE_EXPORT ResourceAndRole : public Adaptor +{ +public: + smtkTypeMacro(smtk::task::adaptor::ResourceAndRole); + smtkSuperclassMacro(smtk::task::Adaptor); + smtkCreateMacro(smtk::task::Adaptor); + + /// Construct an unconfigured adaptor. + ResourceAndRole(); + ResourceAndRole(const Configuration& config); + ResourceAndRole( + const Configuration& config, + std::shared_ptr& from, + std::shared_ptr& to); + + /// Reconfigure the "to()" task. + /// + /// This method is called when the "from()" task changes into a + /// completable state. + bool reconfigureTask() override; + +protected: + void configureSelf(const Configuration& config); +}; +} // namespace adaptor +} // namespace task +} // namespace smtk + +#endif // smtk_task_adaptor_ResourceAndRole_h diff --git a/smtk/task/json/Configurator.cxx b/smtk/task/json/Configurator.cxx new file mode 100644 index 0000000000..4fc977de3b --- /dev/null +++ b/smtk/task/json/Configurator.cxx @@ -0,0 +1,36 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= + +#include "smtk/task/json/Configurator.h" +#include "smtk/task/json/Configurator.txx" + +#include "smtk/task/Adaptor.h" +#include "smtk/task/Task.h" + +static std::mutex g_types; + +namespace smtk +{ +namespace task +{ +namespace json +{ + +std::mutex& typeMutex() +{ + return g_types; +} + +// template<> Configurator::HelperTypeMap Configurator::s_types; +// template<> Configurator::HelperTypeMap Configurator::s_types; + +} // namespace json +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/Configurator.h b/smtk/task/json/Configurator.h new file mode 100644 index 0000000000..b802370230 --- /dev/null +++ b/smtk/task/json/Configurator.h @@ -0,0 +1,134 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_Configurator_h +#define smtk_task_json_Configurator_h + +#include "smtk/task/Task.h" + +#include "smtk/common/Managers.h" +#include "smtk/common/TypeName.h" + +#include "nlohmann/json.hpp" + +#include +#include +#include +#include + +namespace smtk +{ +namespace task +{ +namespace json +{ + +class Helper; + +SMTKCORE_EXPORT std::mutex& typeMutex(); + +typedef std::mutex& (*TypeMutexFunction)(); + +/// A helper for serializing task configurations. +/// +/// This is needed in order to serialized dependencies among tasks which +/// are stored as pointers that could, in theory, form a cycle. +template +class SMTKCORE_EXPORT Configurator +{ +public: + /// Methods that can produce a configuration for a task have this signature. + using ConfigurationHelper = + std::function; + /// How ConfigurationHelper functions are stored. + /// + /// Keys are task class-names; values are functors that produce a JSON + /// object given a task of that type. + using HelperTypeMap = std::unordered_map; + /// JSON data type + using json = nlohmann::json; + + // Construct a configurator. The helper you pass must not be null. + Configurator(Helper* helper); + ~Configurator() = default; + + ///@{ + /// Methods used in registrars to register/unregister types. + /// + /// These are piggybacked onto the task-manager instance registration (i.e., + /// called within the Registrar's method accepting an smtk::task::Manager), + /// so a Schwarz counter is not required to ensure these are only called + /// when needed. See smtk::task::Registrar for an example of how to use + /// these methods. + /// + /// Also, because serialization and deserialization are inherently a + /// run-time activity, we don't make an attempt at compile-time type-safety. + template + inline static bool registerTypes(); + + template + inline static bool unregisterTypes(); + + template + inline static typename std::enable_if::value, bool>::type + registerTypes(); + + template + inline static typename std::enable_if::value, bool>::type + registerTypes(); + + template + inline static typename std::enable_if::value, bool>::type + unregisterTypes(); + + template + inline static typename std::enable_if::value, bool>::type + unregisterTypes(); + ///@} + + inline static bool registerType(const std::string& typeName, ConfigurationHelper helper); + inline static bool unregisterType(const std::string& typeName); + + /// Return json configuration for the given object using registered helpers. + typename ObjectType::Configuration configuration(const ObjectType* object); + + /// Reset the helper's state. + /// + /// This should be called before beginning serialization or deserialization. + /// Additionally, calling it after each of these tasks is recommended since + /// it will free memory. + void clear(); + + /// Return the ID of an object as computed by the swizzler. + /// This will allocate a new ID if none exists. + std::size_t swizzleId(const ObjectType* object); + + /// Return the pointer to an object given its swizzled ID (or null). + ObjectType* unswizzle(std::size_t objectId) const; + + /// Return a serialization of task-references that is consistent within + /// the scope of serializing a set of tasks. + // json swizzleDependencies(const ObjectType::PassedDependencies& deps); + + /// Return a deserialization of de-swizzled task-references. + // ObjectType::PassedDependencies unswizzleDependencies(const json& ids) const; + +protected: + Helper* m_helper; + std::unordered_map m_swizzleFwd; + std::unordered_map m_swizzleBck; + std::size_t m_nextSwizzle = 1; + static HelperTypeMap s_types; +}; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_Configurator_h diff --git a/smtk/task/json/Configurator.txx b/smtk/task/json/Configurator.txx new file mode 100644 index 0000000000..9b686b5e9b --- /dev/null +++ b/smtk/task/json/Configurator.txx @@ -0,0 +1,186 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_Configurator_txx +#define smtk_task_json_Configurator_txx + +#include "smtk/task/json/Configurator.h" +#include "smtk/task/json/Helper.h" + +namespace smtk +{ +namespace task +{ +namespace json +{ + +template +Configurator::Configurator(Helper* helper) + : m_helper(helper) +{ +} + +template +template +inline bool Configurator::registerTypes() +{ + static_assert( + std::tuple_size::value == std::tuple_size::value, + "Class and helper tuples must be of same length."); + return registerTypes<0, ClassList, HelperList>(); +} + +template +template +inline bool Configurator::unregisterTypes() +{ + return unregisterTypes<0, ClassList>(); +} + +template +template +inline typename std::enable_if::value, bool>::type +Configurator::registerTypes() +{ + auto typeName = smtk::common::typeName::type>(); + using HelperType = typename std::tuple_element::type; + HelperType helper; + bool registered = Configurator::registerType(typeName, helper); + return registered && registerTypes(); +} + +template +template +inline typename std::enable_if::value, bool>::type +Configurator::registerTypes() +{ + return true; +} + +template +template +inline typename std::enable_if::value, bool>::type +Configurator::unregisterTypes() +{ + auto typeName = smtk::common::typeName::type>(); + bool unregistered = Configurator::unregisterType(typeName); + return unregistered && unregisterTypes(); +} + +template +template +inline typename std::enable_if::value, bool>::type +Configurator::unregisterTypes() +{ + return true; +} + +template +inline bool Configurator::registerType( + const std::string& typeName, + ConfigurationHelper helper) +{ + std::lock_guard lock(MF()); + return s_types.insert({ typeName, helper }).second; +} + +template +inline bool Configurator::unregisterType(const std::string& typeName) +{ + std::lock_guard lock(MF()); + return s_types.erase(typeName) > 0; +} + +template +/// Return json configuration for the given object using registered helpers. +typename ObjectType::Configuration Configurator::configuration( + const ObjectType* object) +{ + typename ObjectType::Configuration config; + if (object) + { + auto typeName = object->typeName(); + ConfigurationHelper objectHelper = nullptr; + typename HelperTypeMap::const_iterator it; + { + std::lock_guard lock(MF()); + it = s_types.find(typeName); + if (it == s_types.end()) + { + return config; + } + objectHelper = it->second; + } + this->swizzleId(object); // Assign an object ID as early as possible. + config = objectHelper(object, *m_helper); + } + return config; +} + +/// Reset the helper's state. +/// +/// This should be called before beginning serialization or deserialization. +/// Additionally, calling it after each of these tasks is recommended since +/// it will free memory. +template +void Configurator::clear() +{ + m_swizzleFwd.clear(); + m_swizzleBck.clear(); + m_nextSwizzle = 1; +} + +/// Return the ID of an object as computed by the swizzler. +/// This will allocate a new ID if none exists. +template +std::size_t Configurator::swizzleId(const ObjectType* object) +{ + if (!object) + { + return 0; + } + auto* ncobject = const_cast(object); // Need a non-const ObjectType in some cases. + const auto& it = m_swizzleFwd.find(ncobject); + if (it != m_swizzleFwd.end()) + { + return it->second; + } + std::size_t id = m_nextSwizzle++; + m_swizzleFwd[ncobject] = id; + m_swizzleBck[id] = ncobject; + return id; +} + +/// Return the pointer to an object given its swizzled ID (or null). +template +ObjectType* Configurator::unswizzle(std::size_t objectId) const +{ + auto it = m_swizzleBck.find(objectId); + if (it == m_swizzleBck.end()) + { + return nullptr; + } + return it->second; +} + +/// Return a serialization of task-references that is consistent within +/// the scope of serializing a set of tasks. +// json swizzleDependencies(const ObjectType::PassedDependencies& deps); + +/// Return a deserialization of de-swizzled task-references. +// ObjectType::PassedDependencies unswizzleDependencies(const json& ids) const; + +template +typename Configurator::HelperTypeMap Configurator::s_types; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_Configurator_txx diff --git a/smtk/task/json/Helper.cxx b/smtk/task/json/Helper.cxx index d7752ab4f0..bb9c1c9d9f 100644 --- a/smtk/task/json/Helper.cxx +++ b/smtk/task/json/Helper.cxx @@ -8,6 +8,7 @@ // PURPOSE. See the above copyright notice for more information. //========================================================================= #include "smtk/task/json/Helper.h" +#include "smtk/task/json/Configurator.txx" #include "smtk/io/Logger.h" @@ -25,23 +26,14 @@ namespace task { namespace json { -std::unordered_map Helper::s_types; -Helper::Helper() = default; - -Helper::~Helper() = default; - -bool Helper::registerType(const std::string& typeName, ConfigurationHelper helper) +Helper::Helper() + : m_tasks(this) + , m_adaptors(this) { - std::lock_guard lock(g_types); - return s_types.insert({ typeName, helper }).second; } -bool Helper::unregisterType(const std::string& typeName) -{ - std::lock_guard lock(g_types); - return s_types.erase(typeName) > 0; -} +Helper::~Helper() = default; Helper& Helper::instance() { @@ -52,62 +44,30 @@ Helper& Helper::instance() return *g_instance; } -void Helper::setManagers(const smtk::common::Managers::Ptr& managers) +Configurator& Helper::tasks() { - m_managers = managers; + return m_tasks; } -smtk::common::Managers::Ptr Helper::managers() +Configurator& Helper::adaptors() { - return m_managers; + return m_adaptors; } -Task::Configuration Helper::configuration(const Task* task) +void Helper::setManagers(const smtk::common::Managers::Ptr& managers) { - Task::Configuration config; - if (task) - { - auto typeName = task->typeName(); - ConfigurationHelper taskHelper = nullptr; - HelperTypeMap::const_iterator it; - { - std::lock_guard lock(g_types); - it = s_types.find(typeName); - if (it == s_types.end()) - { - return config; - } - taskHelper = it->second; - } - this->swizzleId(task); // Assign a task ID as early as possible. - config = taskHelper(task, *this); - } - return config; + m_managers = managers; } -void Helper::clear() +smtk::common::Managers::Ptr Helper::managers() { - m_swizzleFwd.clear(); - m_swizzleBck.clear(); - m_nextSwizzle = 1; + return m_managers; } -std::size_t Helper::swizzleId(const Task* task) +void Helper::clear() { - if (!task) - { - return 0; - } - auto* nctask = const_cast(task); // Need a non-const Task in some cases. - const auto& it = m_swizzleFwd.find(nctask); - if (it != m_swizzleFwd.end()) - { - return it->second; - } - std::size_t id = m_nextSwizzle++; - m_swizzleFwd[nctask] = id; - m_swizzleBck[id] = nctask; - return id; + m_tasks.clear(); + m_adaptors.clear(); } Helper::json Helper::swizzleDependencies(const Task::PassedDependencies& deps) @@ -117,7 +77,7 @@ Helper::json Helper::swizzleDependencies(const Task::PassedDependencies& deps) { if (dep) { - std::size_t id = this->swizzleId(const_cast(dep.get())); + std::size_t id = m_tasks.swizzleId(const_cast(dep.get())); if (id) { ids.push_back(id); @@ -133,17 +93,48 @@ Task::PassedDependencies Helper::unswizzleDependencies(const json& ids) const for (const auto& id : ids) { auto taskId = id.get(); - auto it = m_swizzleBck.find(taskId); - if (it == m_swizzleBck.end() || !it->second) + auto* ptr = m_tasks.unswizzle(id); + if (!ptr) { smtkWarningMacro( smtk::io::Logger::instance(), "No task or null task for ID " << taskId << ". Skipping."); } - deps.insert(it->second->shared_from_this()); + deps.insert(ptr->shared_from_this()); } return deps; } +void Helper::setAdaptorTaskIds(std::size_t fromId, std::size_t toId) +{ + m_adaptorFromId = fromId; + m_adaptorToId = toId; +} + +void Helper::clearAdaptorTaskIds() +{ + m_adaptorFromId = ~0; + m_adaptorToId = ~0; +} + +std::pair Helper::getAdaptorTasks() +{ + Task::Ptr fromPtr; + auto* from = m_tasks.unswizzle(m_adaptorFromId); + if (from) + { + fromPtr = from->shared_from_this(); + } + + Task::Ptr toPtr; + auto* to = m_tasks.unswizzle(m_adaptorToId); + if (to) + { + toPtr = to->shared_from_this(); + } + + return std::make_pair(fromPtr, toPtr); +} + } // namespace json } // namespace task } // namespace smtk diff --git a/smtk/task/json/Helper.h b/smtk/task/json/Helper.h index 6600347cd9..998045823d 100644 --- a/smtk/task/json/Helper.h +++ b/smtk/task/json/Helper.h @@ -10,6 +10,9 @@ #ifndef smtk_task_json_Helper_h #define smtk_task_json_Helper_h +#include "smtk/task/json/Configurator.h" + +#include "smtk/task/Adaptor.h" #include "smtk/task/Task.h" #include "smtk/common/Managers.h" @@ -32,82 +35,21 @@ namespace json class SMTKCORE_EXPORT Helper { public: - /// Methods that can produce a configuration for a task have this signature. - using ConfigurationHelper = std::function; - /// How ConfigurationHelper functions are stored. - /// - /// Keys are task class-names; values are functors that produce a JSON - /// object given a task of that type. - using HelperTypeMap = std::unordered_map; /// JSON data type using json = nlohmann::json; /// Destructor is public, but you shouldn't use it. ~Helper(); - ///@{ - /// Methods used in registrars to register/unregister types. - /// - /// These are piggybacked onto the task-manager instance registration (i.e., - /// called within the Registrar's method accepting an smtk::task::Manager), - /// so a Schwarz counter is not required to ensure these are only called - /// when needed. See smtk::task::Registrar for an example of how to use - /// these methods. - /// - /// Also, because serialization and deserialization are inherently a - /// run-time activity, we don't make an attempt at compile-time type-safety. - template - static bool registerTypes() - { - static_assert( - std::tuple_size::value == std::tuple_size::value, - "Class and helper tuples must be of same length."); - return registerTypes<0, ClassList, HelperList>(); - } - template - static bool unregisterTypes() - { - return unregisterTypes<0, ClassList>(); - } - - template - static typename std::enable_if::value, bool>::type registerTypes() - { - auto typeName = smtk::common::typeName::type>(); - using HelperType = - typename std::tuple_element::type; // smtk::task::json::jsonTask; - HelperType helper; - bool registered = Helper::registerType(typeName, helper); - return registered && registerTypes(); - } - template - static typename std::enable_if::value, bool>::type registerTypes() - { - return true; - } - - template - static typename std::enable_if::value, bool>::type - unregisterTypes() - { - auto typeName = smtk::common::typeName::type>(); - bool unregistered = Helper::unregisterType(typeName); - return unregistered && unregisterTypes(); - } - template - static typename std::enable_if::value, bool>::type - unregisterTypes() - { - return true; - } - ///@} - - static bool registerType(const std::string& typeName, ConfigurationHelper helper); - static bool unregisterType(const std::string& typeName); - /// Return the helper singleton. static Helper& instance(); + /// Return an object for registering task classes and serialization helpers. + Configurator& tasks(); + + /// Return an object for registering adaptor classes and serialization helpers. + Configurator& adaptors(); + /// Set/get the managers to use when serializing/deserializing. /// /// Call setManagers() with an instance of all your application's @@ -116,9 +58,6 @@ public: void setManagers(const smtk::common::Managers::Ptr& managers); smtk::common::Managers::Ptr managers(); - /// Return json configuration for the given task using registered helpers. - Task::Configuration configuration(const Task*); - /// Reset the helper's state. /// /// This should be called before beginning serialization or deserialization. @@ -126,9 +65,8 @@ public: /// it will free memory. void clear(); - /// Return the ID of a task as computed by the swizzler. - /// This will allocate a new ID if none exists. - std::size_t swizzleId(const Task* task); + // --- Below here are methods specific to tasks and/or adaptors that + // --- don't fit in the Configurator class. /// Return a serialization of task-references that is consistent within /// the scope of serializing a set of tasks. @@ -137,17 +75,25 @@ public: /// Return a deserialization of de-swizzled task-references. Task::PassedDependencies unswizzleDependencies(const json& ids) const; + void setAdaptorTaskIds(std::size_t fromId, std::size_t toId); + void clearAdaptorTaskIds(); + std::pair getAdaptorTasks(); + protected: Helper(); + Configurator m_tasks; + Configurator m_adaptors; smtk::common::Managers::Ptr m_managers; - std::unordered_map m_swizzleFwd; - std::unordered_map m_swizzleBck; - std::size_t m_nextSwizzle = 1; - static HelperTypeMap s_types; + std::size_t m_adaptorFromId = ~0; + std::size_t m_adaptorToId = ~0; }; } // namespace json } // namespace task } // namespace smtk +// Include the configurator implementation here since +// it requires the Helper class defined. +#include "smtk/task/json/Configurator.txx" + #endif // smtk_task_json_Helper_h diff --git a/smtk/task/json/jsonAdaptor.cxx b/smtk/task/json/jsonAdaptor.cxx new file mode 100644 index 0000000000..b4617eecf4 --- /dev/null +++ b/smtk/task/json/jsonAdaptor.cxx @@ -0,0 +1,69 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/json/jsonAdaptor.h" +#include "smtk/task/json/Helper.h" + +#include "smtk/task/Manager.h" + +#include "smtk/io/Logger.h" + +namespace smtk +{ +namespace task +{ +namespace json +{ + +Adaptor::Configuration jsonAdaptor::operator()(const Adaptor* adaptor, Helper& helper) const +{ + Adaptor::Configuration config; + if (adaptor) + { + config = { { "id", helper.adaptors().swizzleId(adaptor) }, + { "type", adaptor->typeName() }, + { "from", helper.tasks().swizzleId(adaptor->from().get()) }, + { "to", helper.tasks().swizzleId(adaptor->to().get()) } }; + } + return config; +} + +} // namespace json + +void to_json(nlohmann::json& j, const smtk::task::Adaptor::Ptr& adaptor) +{ + if (!adaptor) + { + return; + } + auto& helper = json::Helper::instance(); + j = helper.adaptors().configuration(adaptor.get()); +} + +void from_json(const nlohmann::json& j, smtk::task::Adaptor::Ptr& adaptor) +{ + try + { + auto& helper = json::Helper::instance(); + auto managers = helper.managers(); + auto taskManager = managers->get>(); + auto adaptorType = j.at("type").get(); + auto taskPair = helper.getAdaptorTasks(); + adaptor = taskManager->adaptorInstances().createFromName( + adaptorType, const_cast(j), taskPair.first, taskPair.second); + } + catch (std::exception& e) + { + smtkErrorMacro( + smtk::io::Logger::instance(), "Could not deserialize adaptor (" << e.what() << ")."); + } +} + +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/jsonAdaptor.h b/smtk/task/json/jsonAdaptor.h new file mode 100644 index 0000000000..8b15cf94f1 --- /dev/null +++ b/smtk/task/json/jsonAdaptor.h @@ -0,0 +1,43 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_Adaptor_h +#define smtk_task_json_Adaptor_h + +#include "nlohmann/json.hpp" + +#include "smtk/task/Adaptor.h" + +#include +#include + +namespace smtk +{ +namespace task +{ +namespace json +{ + +class Helper; + +struct SMTKCORE_EXPORT jsonAdaptor +{ + Adaptor::Configuration operator()(const Adaptor* adaptor, Helper& helper) const; +}; + +} // namespace json + +SMTKCORE_EXPORT void to_json(nlohmann::json& j, const smtk::task::Adaptor::Ptr& adaptor); + +SMTKCORE_EXPORT void from_json(const nlohmann::json& j, smtk::task::Adaptor::Ptr& adaptor); + +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_Adaptor_h diff --git a/smtk/task/json/jsonFillOutAttributes.cxx b/smtk/task/json/jsonFillOutAttributes.cxx index b1dd6700ba..78f2c1d7da 100644 --- a/smtk/task/json/jsonFillOutAttributes.cxx +++ b/smtk/task/json/jsonFillOutAttributes.cxx @@ -28,6 +28,8 @@ void from_json(const nlohmann::json& j, FillOutAttributes::AttributeSet& attribu { j.at("definitions").get_to(attributeSet.m_definitions); } + attributeSet.m_autoconfigure = + (j.contains("auto-configure") ? j.at("auto-configure").get() : false); if (j.contains("resource-attributes")) { j.at("resource-attributes").get_to(attributeSet.m_resources); diff --git a/smtk/task/json/jsonGatherResources.cxx b/smtk/task/json/jsonGatherResources.cxx index 0e05eda750..07d2d04bc9 100644 --- a/smtk/task/json/jsonGatherResources.cxx +++ b/smtk/task/json/jsonGatherResources.cxx @@ -29,30 +29,29 @@ Task::Configuration jsonGatherResources::operator()(const Task* task, Helper& he { jsonTask superclass; config = superclass(gatherResources, helper); - nlohmann::json::array_t predicates; - nlohmann::json::array_t outputs; - gatherResources->visitPredicates( - [&predicates, &outputs, gatherResources]( - const GatherResources::Predicate& predicate) -> smtk::common::Visit { - nlohmann::json jsonPredicate = { - { "role", predicate.m_role }, - { "type", predicate.m_type }, + nlohmann::json::array_t resourceSets; + gatherResources->visitResourceSets( + [&resourceSets, + gatherResources](const GatherResources::ResourceSet& resourceSet) -> smtk::common::Visit { + nlohmann::json jsonResourceSet = { + { "role", resourceSet.m_role }, + { "type", resourceSet.m_type }, }; - if (predicate.m_minimumCount != 1) + if (resourceSet.m_minimumCount != 1) { - jsonPredicate["min"] = predicate.m_minimumCount; + jsonResourceSet["min"] = resourceSet.m_minimumCount; } - if (predicate.m_maximumCount != -1) + if (resourceSet.m_maximumCount != -1) { - jsonPredicate["max"] = predicate.m_maximumCount; + jsonResourceSet["max"] = resourceSet.m_maximumCount; } - predicates.push_back(jsonPredicate); + resourceSets.push_back(jsonResourceSet); nlohmann::json jsonOutput = { - { "role", predicate.m_role }, + { "role", resourceSet.m_role }, }; nlohmann::json::array_t jsonResources; - for (const auto& weakResource : predicate.m_resources) + for (const auto& weakResource : resourceSet.m_resources) { if (auto resource = weakResource.lock()) { @@ -60,11 +59,9 @@ Task::Configuration jsonGatherResources::operator()(const Task* task, Helper& he } } jsonOutput["resources"] = jsonResources; - outputs.push_back(jsonOutput); return smtk::common::Visit::Continue; }); - config["resources"] = predicates; - config["output"] = outputs; + config["resources"] = resourceSets; } return config; } diff --git a/smtk/task/json/jsonGroup.cxx b/smtk/task/json/jsonGroup.cxx new file mode 100644 index 0000000000..84691d5c97 --- /dev/null +++ b/smtk/task/json/jsonGroup.cxx @@ -0,0 +1,45 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/json/jsonGroup.h" +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonTask.h" + +#include "smtk/task/Group.h" + +namespace smtk +{ +namespace task +{ +namespace json +{ + +Task::Configuration jsonGroup::operator()(const Task* task, Helper& helper) const +{ + Task::Configuration config; + auto* nctask = const_cast(task); + auto* group = dynamic_cast(nctask); + if (group) + { + jsonTask superclass; + config = superclass(group, helper); + nlohmann::json::array_t children; + for (const auto& child : group->children()) + { + nlohmann::json jsonChild = child; + children.push_back(jsonChild); + } + config["children"] = children; + } + return config; +} + +} // namespace json +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/jsonGroup.h b/smtk/task/json/jsonGroup.h new file mode 100644 index 0000000000..94a85a31d5 --- /dev/null +++ b/smtk/task/json/jsonGroup.h @@ -0,0 +1,36 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_Group_h +#define smtk_task_json_Group_h + +#include "smtk/task/Task.h" + +#include +#include + +namespace smtk +{ +namespace task +{ +namespace json +{ + +class Helper; + +struct SMTKCORE_EXPORT jsonGroup +{ + Task::Configuration operator()(const Task* task, Helper& helper) const; +}; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_Group_h diff --git a/smtk/task/json/jsonManager.cxx b/smtk/task/json/jsonManager.cxx index b3b8ddaeb6..ff170276ac 100644 --- a/smtk/task/json/jsonManager.cxx +++ b/smtk/task/json/jsonManager.cxx @@ -8,9 +8,11 @@ // PURPOSE. See the above copyright notice for more information. //========================================================================= #include "smtk/task/json/jsonManager.h" +#include "smtk/task/Adaptor.h" #include "smtk/task/Manager.h" #include "smtk/task/Task.h" #include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonAdaptor.h" #include "smtk/task/json/jsonTask.h" #include "smtk/io/Logger.h" @@ -44,12 +46,21 @@ bool jsonManager::serialize( // Serialize tasks nlohmann::json::array_t taskList; - taskManager->instances().visit([&taskList](const smtk::task::Task::Ptr& task) { + taskManager->taskInstances().visit([&taskList](const smtk::task::Task::Ptr& task) { nlohmann::json jsonTask = task; taskList.push_back(jsonTask); return smtk::common::Visit::Continue; }); json["tasks"] = taskList; + + // Serialize adaptors + nlohmann::json::array_t adaptorList; + taskManager->adaptorInstances().visit([&adaptorList](const smtk::task::Adaptor::Ptr& adaptor) { + nlohmann::json jsonAdaptor = adaptor; + adaptorList.push_back(jsonAdaptor); + return smtk::common::Visit::Continue; + }); + json["adaptors"] = adaptorList; return true; } @@ -76,7 +87,7 @@ bool jsonManager::deserialize( auto taskId = jsonTask.at("id").get(); Task::Ptr task = jsonTask; taskMap[taskId] = task; - helper.swizzleId(task.get()); + helper.tasks().swizzleId(task.get()); } // Do a second pass to deserialize dependencies. for (const auto& jsonTask : json.at("tasks")) @@ -90,14 +101,28 @@ bool jsonManager::deserialize( } } // Now configure dependent tasks with adaptors if specified. - if (json.contains("task-adaptors")) + // Note that tasks have already been deserialized, so the + // helper's map from task-id to task-pointer is complete. + if (json.contains("adaptors")) { - /* TODO - for (const auto& jsonAdaptor : json.at("task-adaptors")) + for (const auto& jsonAdaptor : json.at("adaptors")) { - std::cout << "Adaptor " << jsonAdaptor.at("type") << "\n"; + try + { + auto adaptorId = jsonAdaptor.at("id").get(); + auto taskFromId = jsonAdaptor.at("from").get(); + auto taskToId = jsonAdaptor.at("to").get(); + helper.setAdaptorTaskIds(taskFromId, taskToId); + Adaptor::Ptr adaptor = jsonAdaptor; + helper.clearAdaptorTaskIds(); + } + catch (std::exception&) + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Skipping task because 'id', 'from', and/or 'to' fields are missing."); + } } - */ } helper.clear(); diff --git a/smtk/task/json/jsonManager.h b/smtk/task/json/jsonManager.h index 5fe08599ca..211fd8e1c5 100644 --- a/smtk/task/json/jsonManager.h +++ b/smtk/task/json/jsonManager.h @@ -10,7 +10,7 @@ #ifndef smtk_task_json_Manager_h #define smtk_task_json_Manager_h -#include "smtk/task/Task.h" +#include "smtk/task/Manager.h" #include "smtk/common/Managers.h" diff --git a/smtk/task/json/jsonResourceAndRole.cxx b/smtk/task/json/jsonResourceAndRole.cxx new file mode 100644 index 0000000000..bd839255b3 --- /dev/null +++ b/smtk/task/json/jsonResourceAndRole.cxx @@ -0,0 +1,41 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/json/jsonResourceAndRole.h" +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonAdaptor.h" + +#include "smtk/task/adaptor/ResourceAndRole.h" + +namespace smtk +{ +namespace task +{ + +using adaptor::ResourceAndRole; + +namespace json +{ + +Adaptor::Configuration jsonResourceAndRole::operator()(const Adaptor* adaptor, Helper& helper) const +{ + Adaptor::Configuration config; + auto* ncadaptor = const_cast(adaptor); + auto* resourceAndRole = dynamic_cast(ncadaptor); + if (resourceAndRole) + { + jsonAdaptor superclass; + config = superclass(resourceAndRole, helper); + } + return config; +} + +} // namespace json +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/jsonResourceAndRole.h b/smtk/task/json/jsonResourceAndRole.h new file mode 100644 index 0000000000..ac309595f4 --- /dev/null +++ b/smtk/task/json/jsonResourceAndRole.h @@ -0,0 +1,38 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_json_ResourceAndRole_h +#define smtk_task_json_ResourceAndRole_h + +#include "smtk/task/adaptor/ResourceAndRole.h" + +#include "nlohmann/json.hpp" + +#include +#include + +namespace smtk +{ +namespace task +{ +namespace json +{ + +class Helper; + +struct SMTKCORE_EXPORT jsonResourceAndRole +{ + Adaptor::Configuration operator()(const Adaptor* task, Helper& helper) const; +}; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_ResourceAndRole_h diff --git a/smtk/task/json/jsonTask.cxx b/smtk/task/json/jsonTask.cxx index 9cd4db527d..f898bdd556 100644 --- a/smtk/task/json/jsonTask.cxx +++ b/smtk/task/json/jsonTask.cxx @@ -26,7 +26,7 @@ Task::Configuration jsonTask::operator()(const Task* task, Helper& helper) const Task::Configuration config; if (task) { - config["id"] = helper.swizzleId(task); + config["id"] = helper.tasks().swizzleId(task); config["type"] = task->typeName(); config["title"] = task->title(); config["state"] = stateName(task->internalState()); @@ -48,7 +48,7 @@ void to_json(nlohmann::json& j, const smtk::task::Task::Ptr& task) return; } auto& helper = json::Helper::instance(); - j = helper.configuration(task.get()); + j = helper.tasks().configuration(task.get()); } void from_json(const nlohmann::json& j, smtk::task::Task::Ptr& task) @@ -59,8 +59,8 @@ void from_json(const nlohmann::json& j, smtk::task::Task::Ptr& task) auto managers = helper.managers(); auto taskManager = managers->get>(); auto taskType = j.at("type").get(); - task = - taskManager->instances().createFromName(taskType, const_cast(j), managers); + task = taskManager->taskInstances().createFromName( + taskType, const_cast(j), managers); } catch (std::exception& e) { diff --git a/smtk/task/pybind11/PybindGatherResources.h b/smtk/task/pybind11/PybindGatherResources.h index 86c3289ba8..9640bef61e 100644 --- a/smtk/task/pybind11/PybindGatherResources.h +++ b/smtk/task/pybind11/PybindGatherResources.h @@ -32,16 +32,16 @@ inline PySharedPtrClass< smtk::task::GatherResources, smtk::task::Task > pybind1 .def("typeName", &smtk::task::GatherResources::typeName) .def_readonly_static("type_name", &smtk::task::GatherResources::type_name) ; - py::class_< smtk::task::GatherResources::Predicate >(instance, "Predicate") - .def(py::init<::smtk::task::GatherResources::Predicate const &>()) + py::class_< smtk::task::GatherResources::ResourceSet >(instance, "ResourceSet") + .def(py::init<::smtk::task::GatherResources::ResourceSet const &>()) .def(py::init<>()) - .def("deepcopy", (smtk::task::GatherResources::Predicate & (smtk::task::GatherResources::Predicate::*)(::smtk::task::GatherResources::Predicate const &)) &smtk::task::GatherResources::Predicate::operator=) - .def_readwrite("m_role", &smtk::task::GatherResources::Predicate::m_role) - .def_readwrite("m_minimumCount", &smtk::task::GatherResources::Predicate::m_minimumCount) - .def_readwrite("m_maximumCount", &smtk::task::GatherResources::Predicate::m_maximumCount) - .def_readwrite("m_type", &smtk::task::GatherResources::Predicate::m_type) - .def_readwrite("m_validator", &smtk::task::GatherResources::Predicate::m_validator) - .def_readwrite("m_resources", &smtk::task::GatherResources::Predicate::m_resources) + .def("deepcopy", (smtk::task::GatherResources::ResourceSet & (smtk::task::GatherResources::ResourceSet::*)(::smtk::task::GatherResources::ResourceSet const &)) &smtk::task::GatherResources::ResourceSet::operator=) + .def_readwrite("m_role", &smtk::task::GatherResources::ResourceSet::m_role) + .def_readwrite("m_minimumCount", &smtk::task::GatherResources::ResourceSet::m_minimumCount) + .def_readwrite("m_maximumCount", &smtk::task::GatherResources::ResourceSet::m_maximumCount) + .def_readwrite("m_type", &smtk::task::GatherResources::ResourceSet::m_type) + .def_readwrite("m_validator", &smtk::task::GatherResources::ResourceSet::m_validator) + .def_readwrite("m_resources", &smtk::task::GatherResources::ResourceSet::m_resources) ; return instance; } diff --git a/smtk/task/pybind11/PybindManager.h b/smtk/task/pybind11/PybindManager.h index 1174fb4081..de03506f52 100644 --- a/smtk/task/pybind11/PybindManager.h +++ b/smtk/task/pybind11/PybindManager.h @@ -27,8 +27,10 @@ inline PySharedPtrClass< smtk::task::Manager > pybind11_init_smtk_task_Manager(p .def("active", (smtk::task::Active const & (smtk::task::Manager::*)() const) &smtk::task::Manager::active) .def_static("create", (std::shared_ptr (*)()) &smtk::task::Manager::create) .def_static("create", (std::shared_ptr (*)(::std::shared_ptr &)) &smtk::task::Manager::create, py::arg("ref")) - .def("instances", (smtk::task::Manager::Instances & (smtk::task::Manager::*)()) &smtk::task::Manager::instances) - .def("instances", (smtk::task::Manager::Instances const & (smtk::task::Manager::*)() const) &smtk::task::Manager::instances) + .def("adaptorInstances", (smtk::task::Manager::AdaptorInstances & (smtk::task::Manager::*)()) &smtk::task::Manager::adaptorInstances) + .def("adaptorInstances", (smtk::task::Manager::AdaptorInstances const & (smtk::task::Manager::*)() const) &smtk::task::Manager::adaptorInstances) + .def("taskInstances", (smtk::task::Manager::TaskInstances & (smtk::task::Manager::*)()) &smtk::task::Manager::taskInstances) + .def("taskInstances", (smtk::task::Manager::TaskInstances const & (smtk::task::Manager::*)() const) &smtk::task::Manager::taskInstances) .def("managers", &smtk::task::Manager::managers) .def("setManagers", &smtk::task::Manager::setManagers, py::arg("managers")) .def("typeName", &smtk::task::Manager::typeName) diff --git a/smtk/task/testing/cxx/TestActiveTask.cxx b/smtk/task/testing/cxx/TestActiveTask.cxx index cf58f9bd24..d729462943 100644 --- a/smtk/task/testing/cxx/TestActiveTask.cxx +++ b/smtk/task/testing/cxx/TestActiveTask.cxx @@ -71,8 +71,8 @@ int TestActiveTask(int, char*[]) test(previousTask == nullptr && nextTask == nullptr, "Unexpected initialization."); { - std::shared_ptr t1 = - taskManager->instances().create(Task::Configuration{ { "title", "Task 1" } }, managers); + std::shared_ptr t1 = taskManager->taskInstances().create( + Task::Configuration{ { "title", "Task 1" } }, managers); std::cout << "Attempting to set active task:\n"; taskManager->active().switchTo(t1.get()); test(count == 2, "Expected to switch active task."); @@ -92,8 +92,8 @@ int TestActiveTask(int, char*[]) success = t1->markCompleted(false); // Now add a task and switch to it. - std::shared_ptr t2 = - taskManager->instances().create(Task::Configuration{ { "title", "Task 2" } }, managers); + std::shared_ptr t2 = taskManager->taskInstances().create( + Task::Configuration{ { "title", "Task 2" } }, managers); success = t1->addDependency(t2); std::cout << "Switching to task 2:\n"; taskManager->active().switchTo(t2.get()); @@ -109,7 +109,7 @@ int TestActiveTask(int, char*[]) { { { "role", "model geometry" }, { "type", "smtk::model::Resource" }, { "max", 2 } }, { { "role", "simulation attribute" }, { "type", "smtk::attribute::Resource" } } } } }; - auto t4 = taskManager->instances().create(c4, managers); + auto t4 = taskManager->taskInstances().create(c4, managers); t1->addDependency(t4); std::cout << "Ensuring switches to unavailable tasks fail.\n"; bool didSwitch = taskManager->active().switchTo(t1.get()); @@ -159,7 +159,7 @@ int TestActiveTask(int, char*[]) test(previousTask == nullptr && nextTask == t1.get(), "Expected active switch (none) ⟶ t1."); std::cout << "Unmanaging the active task should reset the active task:\n"; - taskManager->instances().clear(); + taskManager->taskInstances().clear(); test(count == 7, "Expected switch to null task when a task is dropped from manager."); test(previousTask == t1.get() && nextTask == nullptr, "Expected active switch t1 ⟶ (none)."); } diff --git a/smtk/task/testing/cxx/TestTaskBasics.cxx b/smtk/task/testing/cxx/TestTaskBasics.cxx index 887e278384..8250a4dc46 100644 --- a/smtk/task/testing/cxx/TestTaskBasics.cxx +++ b/smtk/task/testing/cxx/TestTaskBasics.cxx @@ -83,9 +83,9 @@ int TestTaskBasics(int, char*[]) auto modelRegistry = smtk::plugin::addToManagers(resourceManager); auto taskTaskRegistry = smtk::plugin::addToManagers(taskManager); - taskManager->instances().registerType(); + taskManager->taskInstances().registerType(); - auto ikey = taskManager->instances().observers().insert( + auto ikey = taskManager->taskInstances().observers().insert( [](smtk::common::InstanceEvent event, const smtk::task::Task::Ptr& task) { std::cout << (event == smtk::common::InstanceEvent::Managed ? "Manage" : "Unmanage") << " " << task->title() << "\n"; @@ -99,8 +99,8 @@ int TestTaskBasics(int, char*[]) }; { - std::shared_ptr t1 = - taskManager->instances().create(Task::Configuration{ { "title", "Task 1" } }, managers); + std::shared_ptr t1 = taskManager->taskInstances().create( + Task::Configuration{ { "title", "Task 1" } }, managers); test(!!t1, "Expecting to create a non-null task."); test( t1->state() == State::Completable, "Expected task without dependencies to be completable."); @@ -139,9 +139,9 @@ int TestTaskBasics(int, char*[]) // Now add a dependent task that is unavailable. test( - taskManager->instances().contains(), + taskManager->taskInstances().contains(), "Expected UnavailableTask to be registered."); - std::shared_ptr t2 = taskManager->instances().create( + std::shared_ptr t2 = taskManager->taskInstances().create( Task::Configuration{ { "title", "Task 2" } }, managers); called = 0; success = t1->addDependency(t2); @@ -155,7 +155,7 @@ int TestTaskBasics(int, char*[]) // Test construction with dependencies Task::Configuration c3{ { "title", "Task 3" }, { "completed", true } }; - auto t3 = taskManager->instances().create( + auto t3 = taskManager->taskInstances().create( c3, std::set>{ { t1, t2 } }, managers); // Test dependency removal and notification. @@ -184,7 +184,7 @@ int TestTaskBasics(int, char*[]) { { { "role", "model geometry" }, { "type", "smtk::model::Resource" }, { "max", 2 } }, { { "role", "simulation attribute" }, { "type", "smtk::attribute::Resource" } } } } }; - auto t4 = taskManager->instances().create(c4, managers); + auto t4 = taskManager->taskInstances().create(c4, managers); test(!!t4, "Could not create GatherResources."); test(t4->state() == State::Incomplete, "Task with no resources should be incomplete."); auto hokey = t4->observers().insert(callback); @@ -247,7 +247,8 @@ int TestTaskBasics(int, char*[]) { "resources", { { { "type", "smtk::model::Resource" } }, { { "role", "simulation attribute" } } } } }; - auto t5 = taskManager->instances().createFromName("smtk::task::GatherResources", c5, managers); + auto t5 = + taskManager->taskInstances().createFromName("smtk::task::GatherResources", c5, managers); test(!!t5, "Could not create GatherResources."); auto pokey = t5->observers().insert(callback); test(t5->state() == State::Completable, "Task 5 should be completable initially."); @@ -263,29 +264,29 @@ int TestTaskBasics(int, char*[]) // Test task Instances methods: // Verify double-add fails. - didAdd = taskManager->instances().manage(t5); + didAdd = taskManager->taskInstances().manage(t5); test(!didAdd, "Should not return true when duplicate instance managed."); // Test visit() std::cout << "Tasks:\n"; - taskManager->instances().visit(countInstances); + taskManager->taskInstances().visit(countInstances); test(count == 5, "Expected 5 tasks."); // Test removal and addition. - didRemove = taskManager->instances().unmanage(t5); + didRemove = taskManager->taskInstances().unmanage(t5); test(didRemove, "Expected to unmanage task 5."); - test(!taskManager->instances().contains(t5), "Expected task 5 to be absent."); - didAdd = taskManager->instances().manage(t5); + test(!taskManager->taskInstances().contains(t5), "Expected task 5 to be absent."); + didAdd = taskManager->taskInstances().manage(t5); test(didAdd, "Expected to manage task 5."); - test(taskManager->instances().contains(t5), "Expected task 5 to be present."); + test(taskManager->taskInstances().contains(t5), "Expected task 5 to be present."); - taskManager->instances().clear(); + taskManager->taskInstances().clear(); test(!test_task::taskDestroyed, "Task 2 should still be alive while t2 in scope."); } test(test_task::taskDestroyed, "Task 2 should be dead once t2 is out of scope."); count = 0; - taskManager->instances().visit(countInstances); + taskManager->taskInstances().visit(countInstances); test(count == 0, "Expected 0 tasks."); return 0; diff --git a/smtk/task/testing/cxx/TestTaskGroup.cxx b/smtk/task/testing/cxx/TestTaskGroup.cxx index 361a600c6e..d274425498 100644 --- a/smtk/task/testing/cxx/TestTaskGroup.cxx +++ b/smtk/task/testing/cxx/TestTaskGroup.cxx @@ -89,42 +89,57 @@ int TestTaskGroup(int, char*[]) }, { "id": 2, - "type": "smtk::task::FillOutAttributes", - "title": "Mark up model", - "state": "incomplete", - "attribute-sets": [ - { - "role": "simulation attribute", - "definitions": [ - "Material", - "BoundaryCondition" - ] - } - ] - }, - { - "id": 3, - "type": "smtk::task::FillOutAttributes", - "title": "Specify simulation parameters", - "state": "incomplete", - "attribute-sets": [ - { - "role": "simulation attribute", - "definitions": [ - "Globals" - ] - } - ] - } - { - "id": 4, "type": "smtk::task::Group", "title": "Prepare simulation", "state": "incomplete", "dependencies": [ 1 ], "grouping": { - "children": [2, 3], "mode": "parallel" + "children": [ + { + "id": -1, + "type": "smtk::task::FillOutAttributes", + "title": "Mark up model", + "state": "incomplete", + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": [ + "Material", + "BoundaryCondition" + ] + } + ] + }, + { + "id": -2, + "type": "smtk::task::FillOutAttributes", + "title": "Specify simulation parameters", + "state": "incomplete", + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": [ + "Globals" + ] + } + ] + } + ], + "adaptors": [ + { + "id": -1, + "type": "smtk::task::adaptor::ResourceAndRole", + "from": 2, + "to": -1 + }, + { + "id": -1, + "type": "smtk::task::adaptor::ResourceAndRole", + "from": 2, + "to": -2 + } + ] } } ], @@ -136,9 +151,9 @@ int TestTaskGroup(int, char*[]) "'Ignore' adaptor as a default – which never modifies the", "depedent task's configuration." ], - "task-adaptors": [ + "adaptors": [ { - "type": "smtk::task::adaptor::Ignore", + "type": "smtk::task::adaptor::ResourceAndRole", "from": 1, "to": 2 } @@ -154,7 +169,7 @@ int TestTaskGroup(int, char*[]) std::cout << config.dump(2) << "\n"; bool ok = smtk::task::json::jsonManager::deserialize(managers, config); test(ok, "Failed to parse configuration."); - test(taskManager->instances().size() == 2, "Expected to deserialize 2 tasks."); + test(taskManager->taskInstances().size() == 2, "Expected to deserialize 2 tasks."); // Round trip it. nlohmann::json config2; diff --git a/smtk/task/testing/cxx/TestTaskJSON.cxx b/smtk/task/testing/cxx/TestTaskJSON.cxx index 7ded5f0a51..55b954b803 100644 --- a/smtk/task/testing/cxx/TestTaskJSON.cxx +++ b/smtk/task/testing/cxx/TestTaskJSON.cxx @@ -7,16 +7,26 @@ // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //========================================================================= +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/ComponentItem.h" +#include "smtk/attribute/Definition.h" +#include "smtk/attribute/ReferenceItem.h" +#include "smtk/attribute/ReferenceItemDefinition.h" #include "smtk/attribute/Registrar.h" #include "smtk/attribute/Resource.h" +#include "smtk/attribute/operators/Signal.h" #include "smtk/common/Managers.h" #include "smtk/model/Registrar.h" #include "smtk/model/Resource.h" +#include "smtk/model/Volume.h" #include "smtk/operation/Manager.h" #include "smtk/operation/Registrar.h" #include "smtk/plugin/Registry.h" #include "smtk/resource/Manager.h" #include "smtk/resource/Registrar.h" +#include "smtk/task/FillOutAttributes.h" +#include "smtk/task/GatherResources.h" +#include "smtk/task/Instances.h" #include "smtk/task/Manager.h" #include "smtk/task/Registrar.h" #include "smtk/task/Task.h" @@ -34,6 +44,19 @@ using namespace smtk::task; } // namespace test_task +namespace +{ + +void printTaskStates(smtk::task::Manager::Ptr taskManager) +{ + taskManager->taskInstances().visit([](const std::shared_ptr& task) { + std::cout << task->title() << ": " << smtk::task::stateName(task->state()) << "\n"; + return smtk::common::Visit::Continue; + }); +} + +} // anonymous namespace + int TestTaskJSON(int, char*[]) { using smtk::task::State; @@ -52,6 +75,8 @@ int TestTaskJSON(int, char*[]) auto attributeResourceRegistry = smtk::plugin::addToManagers(resourceManager); + auto attributeOperationRegistry = + smtk::plugin::addToManagers(operationManager); auto modelRegistry = smtk::plugin::addToManagers(resourceManager); auto taskTaskRegistry = smtk::plugin::addToManagers(taskManager); @@ -112,9 +137,10 @@ int TestTaskJSON(int, char*[]) "'Ignore' adaptor as a default – which never modifies the", "depedent task's configuration." ], - "task-adaptors": [ + "adaptors": [ { - "type": "smtk::task::adaptor::Ignore", + "id": 1, + "type": "smtk::task::adaptor::ResourceAndRole", "from": 1, "to": 2 } @@ -130,16 +156,84 @@ int TestTaskJSON(int, char*[]) std::cout << config.dump(2) << "\n"; bool ok = smtk::task::json::jsonManager::deserialize(managers, config); test(ok, "Failed to parse configuration."); - test(taskManager->instances().size() == 2, "Expected to deserialize 2 tasks."); + test(taskManager->taskInstances().size() == 2, "Expected to deserialize 2 tasks."); + + smtk::task::GatherResources::Ptr gatherResources; + smtk::task::FillOutAttributes::Ptr fillOutAttributes; + taskManager->taskInstances().visit( + [&gatherResources, &fillOutAttributes](const smtk::task::Task::Ptr& task) { + if (!gatherResources) + { + gatherResources = std::dynamic_pointer_cast(task); + } + if (!fillOutAttributes) + { + fillOutAttributes = std::dynamic_pointer_cast(task); + } + return smtk::common::Visit::Continue; + }); + + // Add components and signal to test FillOutAttributes. + // First, the FillOutAttributes task is irrelevant + printTaskStates(taskManager); + gatherResources->markCompleted(true); + + printTaskStates(taskManager); + + auto volume1 = model->addVolume(); + auto volume2 = model->addVolume(); + auto def = attrib->createDefinition("Material"); + auto assoc = def->createLocalAssociationRule(); + assoc->setAcceptsEntries("smtk::model::Resource", "volume", true); + assoc->setNumberOfRequiredValues(1); + assoc->setIsExtensible(true); + auto material1 = attrib->createAttribute("CarbonFiber", "Material"); + + // Signal the change + auto signal = operationManager->create(); + signal->parameters()->findComponent("created")->appendValue(material1); + auto result = signal->operate(); + std::cout << "Signaled after adding a material\n"; + // Now, the FillOutAttributes task is incomplete + printTaskStates(taskManager); - // Round trip it. - nlohmann::json config2; - ok = smtk::task::json::jsonManager::serialize(managers, config2); - test(ok, "Failed to serialize task manager."); - std::cout << config2.dump(2) << "\n"; + material1->associate(volume1.component()); + signal->parameters()->findComponent("created")->setNumberOfValues(0); + signal->parameters()->findComponent("modified")->appendValue(material1); + result = signal->operate(); + std::cout << "Signaled after associating to the material\n"; + // Finally, the FillOutAttributes task is completable + printTaskStates(taskManager); - // TODO: Round trip a second time so we should be guaranteed to have - // two machine-(not-hand-)generated strings to compare. + std::string configString2; + { + // Round trip it. + nlohmann::json config2; + ok = smtk::task::json::jsonManager::serialize(managers, config2); + test(ok, "Failed to serialize task manager."); + configString2 = config2.dump(2); + std::cout << configString2 << "\n"; + } + { + // TODO: Round trip a second time so we should be guaranteed to have + // two machine-(not-hand-)generated strings to compare. + auto managers2 = smtk::common::Managers::create(); + managers2->insert(resourceManager); + managers2->insert(operationManager); + auto taskManager2 = smtk::task::Manager::create(); + smtk::task::Registrar::registerTo(taskManager2); + managers2->insert(taskManager2); + auto config3 = nlohmann::json::parse(configString2); + ok = smtk::task::json::jsonManager::deserialize(managers2, config3); + test(ok, "Failed to parse second configuration."); + test(taskManager2->taskInstances().size() == 2, "Expected to deserialize 2 tasks."); + nlohmann::json config4; + ok = smtk::task::json::jsonManager::serialize(managers2, config4); + test(ok, "Failed to serialize second manager."); + std::string configString3 = config4.dump(2); + std::cout << "----\n" << configString3 << "\n"; + // test(configString2 == configString3, "Failed to match strings."); + } return 0; } -- GitLab From c2fee0569906a52d32171c723a5ad7e3c9a8e875 Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Tue, 17 Aug 2021 19:47:11 -0400 Subject: [PATCH 24/52] BUG: Fixing Issues qtAttributeView's operation handler In qtAttributeView::handleOperationEvent, the code had the following issues: 1. It assumed that an Attribute that is in the operation's result must be based on the View's current definition. This is only true if the View only contained one Definition. In reality, the attribute's Definition needs to be tested against the Definitions that defined the View and if it matches any of them it needs to be processed. 2. It always assumed that there was a Current Attribute selected. This would result in a crash if there wasn't any. 3. There was a bug in qtAttributeView::getItemFromAttribute, it was getting items from column 0 instead of the name column. This resulted in names not being properly updated Added qtAttributeView::matchesDefinitions This method tests a Definition against the Definitions that define the View and returns true if it matches or is derived from any in the list. --- ...fixingQtAttributeViewOperationHandling.rst | 12 +++ smtk/extension/qt/qtAttributeView.cxx | 96 +++++++++---------- smtk/extension/qt/qtAttributeView.h | 2 + 3 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 doc/release/notes/fixingQtAttributeViewOperationHandling.rst diff --git a/doc/release/notes/fixingQtAttributeViewOperationHandling.rst b/doc/release/notes/fixingQtAttributeViewOperationHandling.rst new file mode 100644 index 0000000000..84804b0c4a --- /dev/null +++ b/doc/release/notes/fixingQtAttributeViewOperationHandling.rst @@ -0,0 +1,12 @@ +Fixing qtAttributeView handling of Operations +--------------------------------------------- + +In qtAttributeView::handleOperationEvent, the code had the following issues: + +1. It assumed that an Attribute that is in the operation's result must be based on the View's current definition. This is only true if the View only contained one Definition. In reality, the attribute's Definition needs to be tested against the Definitions that defined the View and if it matches any of them it needs to be processed. +2. It always assumed that there was a Current Attribute selected. This would result in a crash if there wasn't any. +3. There was a bug in qtAttributeView::getItemFromAttribute, it was getting items from column 0 instead of the name column. This resulted in names not being properly updated + +Added qtAttributeView::matchesDefinitions +~~~~~~~~~~~~~~~~~~ +This method tests a Definition against the Definitions that define the View and returns true if it matches or is derived from any in the list. diff --git a/smtk/extension/qt/qtAttributeView.cxx b/smtk/extension/qt/qtAttributeView.cxx index d6701a4467..f93ea711ce 100644 --- a/smtk/extension/qt/qtAttributeView.cxx +++ b/smtk/extension/qt/qtAttributeView.cxx @@ -584,7 +584,7 @@ QStandardItem* qtAttributeView::getItemFromAttribute(smtk::attribute::Attribute* int n = m_internals->ListTableModel->rowCount(); for (int i = 0; i < n; i++) { - QStandardItem* item = m_internals->ListTableModel->item(i); + QStandardItem* item = m_internals->ListTableModel->item(i, name_column); if (this->getRawAttributeFromItem(item) == attribute) { return item; @@ -1471,6 +1471,14 @@ void qtAttributeView::updateAttributeStatus(Attribute* att) } } +bool qtAttributeView::matchesDefinitions(const smtk::attribute::DefinitionPtr& def) const +{ + return std::any_of( + m_internals->m_attDefinitions.begin(), + m_internals->m_attDefinitions.end(), + [=](const smtk::attribute::DefinitionPtr& viewDef) { return def->isA(viewDef); }); +} + int qtAttributeView::handleOperationEvent( const smtk::operation::Operation& op, smtk::operation::EventType event, @@ -1514,43 +1522,40 @@ int qtAttributeView::handleOperationEvent( } auto att = dynamic_pointer_cast(compItem->value(i)); - if (att == nullptr) + // If there is no attribute or it's definition is not being displayed in the View - skip it + if (!(att && this->matchesDefinitions(att->definition()))) { continue; } - smtk::attribute::DefinitionPtr attDef = att->definition(); - if (attDef->isA(currentDef)) + // Is this the current attribute being displayed? + if (m_internals->CurrentAtt && (att == m_internals->CurrentAtt->attribute())) { - // Is this the current attribute being displayed? - if (att == m_internals->CurrentAtt->attribute()) + // Update the attribute's items + auto items = m_internals->CurrentAtt->items(); + for (auto* item : items) { - // Update the attribute's items - auto items = m_internals->CurrentAtt->items(); - for (auto* item : items) - { - item->updateItemData(); - } + item->updateItemData(); } - // Need to update the item's name and edit ability - auto* item = this->getItemFromAttribute(att.get()); - if (item) + } + // Need to update the item's name and edit ability + auto* item = this->getItemFromAttribute(att.get()); + if (item) + { + item->setText(QString::fromUtf8(att->name().c_str())); + if (!this->attributeNamesConstant()) { - item->setText(QString::fromUtf8(att->name().c_str())); - if (!this->attributeNamesConstant()) + // Need to see if the name is editable + if ( + att->properties().contains("smtk.extensions.attribute_view.name_read_only") && + att->properties().at("smtk.extensions.attribute_view.name_read_only")) + { + Qt::ItemFlags itemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setFlags(itemFlags); + } + else { - // Need to see if the name is editable - if ( - att->properties().contains("smtk.extensions.attribute_view.name_read_only") && - att->properties().at("smtk.extensions.attribute_view.name_read_only")) - { - Qt::ItemFlags itemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - item->setFlags(itemFlags); - } - else - { - Qt::ItemFlags itemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); - item->setFlags(itemFlags); - } + Qt::ItemFlags itemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + item->setFlags(itemFlags); } } } @@ -1570,24 +1575,20 @@ int qtAttributeView::handleOperationEvent( } auto att = dynamic_pointer_cast(compItem->value(i)); - if (att == nullptr) + // If there is no attribute or it's definition is not being displayed in the View - skip it + if (!(att && this->matchesDefinitions(att->definition()))) { continue; } - smtk::attribute::DefinitionPtr attDef = att->definition(); - // Is this type of attribute being displayed? - if (attDef->isA(currentDef)) + int row, numRows = m_internals->ListTableModel->rowCount(); + for (row = 0; row < numRows; ++row) { - int row, numRows = m_internals->ListTableModel->rowCount(); - for (row = 0; row < numRows; ++row) + QStandardItem* item = m_internals->ListTableModel->item(row, name_column); + smtk::attribute::Attribute* itemAtt = this->getRawAttributeFromItem(item); + if (att.get() == itemAtt) { - QStandardItem* item = m_internals->ListTableModel->item(row, name_column); - smtk::attribute::Attribute* itemAtt = this->getRawAttributeFromItem(item); - if (att.get() == itemAtt) - { - m_internals->ListTableModel->removeRow(row); - break; - } + m_internals->ListTableModel->removeRow(row); + break; } } } @@ -1600,15 +1601,12 @@ int qtAttributeView::handleOperationEvent( if (compItem->isSet(i)) { auto att = dynamic_pointer_cast(compItem->value(i)); - if (att == nullptr) + // If there is no attribute or it's definition is not being displayed in the View - skip it + if (!(att && this->matchesDefinitions(att->definition()))) { continue; } - smtk::attribute::DefinitionPtr attDef = att->definition(); - if (attDef->isA(currentDef)) - { - this->addAttributeListItem(att); - } + this->addAttributeListItem(att); } } return 0; diff --git a/smtk/extension/qt/qtAttributeView.h b/smtk/extension/qt/qtAttributeView.h index c957c2cb46..9733542bee 100644 --- a/smtk/extension/qt/qtAttributeView.h +++ b/smtk/extension/qt/qtAttributeView.h @@ -82,6 +82,8 @@ public: smtk::attribute::DefinitionPtr getCurrentDef() const; + // Returns true if the Definition matches any of the View's Definitions + bool matchesDefinitions(const smtk::attribute::DefinitionPtr& def) const; enum enumViewBy { VIEWBY_Attribute = 0, -- GitLab From e006747f492507a4de89af456380d20bfbe3a98a Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 19 Aug 2021 12:54:06 -0400 Subject: [PATCH 25/52] Update sphinx packages to fix docs. Now bulleted lists will have bullets. --- doc/conf.py | 2 +- doc/requirements/dev.txt | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 4042d9b271..3edbc0d032 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -34,7 +34,7 @@ builddir = sys.argv[-1] def setup(app): # prevent stupid-wide table columns. - app.add_stylesheet("theme-overrides.css") + app.add_css_file("theme-overrides.css") def runDoxygen(rtdsrcdir, rtdblddir, doxyfileIn, doxyfileOut): diff --git a/doc/requirements/dev.txt b/doc/requirements/dev.txt index fd3c0b0f03..8f1ed1d4c0 100644 --- a/doc/requirements/dev.txt +++ b/doc/requirements/dev.txt @@ -1,9 +1,9 @@ -Sphinx==2.2.1 -pyparsing==2.2.0 -Pygments==2.4.2 -actdiag==0.5.4 -blockdiag==1.5.4 +Sphinx==4.1.2 +pyparsing==2.4.7 +Pygments==2.10.0 +actdiag==2.0.0 +blockdiag==2.0.1 sphinxcontrib-actdiag==2.0.0 -sphinxcontrib-doxylink==1.6.1 -sphinx-rtd-theme==0.4.3 -breathe==4.13.1 +sphinxcontrib-doxylink==1.8 +sphinx-rtd-theme==0.5.2 +breathe==4.30.0 -- GitLab From 62350dc86ee18835f91f78eddba094300968b6e5 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 19 Aug 2021 13:05:48 -0400 Subject: [PATCH 26/52] Add release notes to read-the-docs. --- doc/index.rst | 1 + doc/release/index.rst | 9 +++++++++ doc/release/smtk-21.05.rst | 1 + doc/release/smtk-21.07.rst | 6 +++--- 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 doc/release/index.rst diff --git a/doc/index.rst b/doc/index.rst index b80e6b90e4..c4e3ce7d25 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -17,6 +17,7 @@ Contents: userguide/index.rst tutorials/index.rst + release/index.rst ################## diff --git a/doc/release/index.rst b/doc/release/index.rst new file mode 100644 index 0000000000..5e771a1385 --- /dev/null +++ b/doc/release/index.rst @@ -0,0 +1,9 @@ +============= +Release notes +============= + +.. toctree:: + :maxdepth: 2 + + smtk-21.07.rst + smtk-21.05.rst diff --git a/doc/release/smtk-21.05.rst b/doc/release/smtk-21.05.rst index e93039cf4a..fbee5ab111 100644 --- a/doc/release/smtk-21.05.rst +++ b/doc/release/smtk-21.05.rst @@ -1,3 +1,4 @@ +.. _release-notes-21.05: ======================== SMTK 21.05 Release Notes diff --git a/doc/release/smtk-21.07.rst b/doc/release/smtk-21.07.rst index a1bde93c36..93d1b4ee12 100644 --- a/doc/release/smtk-21.07.rst +++ b/doc/release/smtk-21.07.rst @@ -1,10 +1,10 @@ +.. _release-notes-21.07: + ========================= SMTK 21.07 Release Notes ========================= -See also `SMTK 21.05 Release Notes`_ for previous changes. - -.. _`SMTK 21.05 Release Notes`: smtk-21.05.md +See also :ref:`release-notes-21.05` for previous changes. Registration Changes ==================== -- GitLab From c6f4f65e12f21fc3d39af51336c1b87e55878f80 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 19 Aug 2021 13:18:16 -0400 Subject: [PATCH 27/52] Try to fix failing macos builds. --- .gitlab/os-macos.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 2074238dd1..5f4dcbae35 100644 --- a/.gitlab/os-macos.yml +++ b/.gitlab/os-macos.yml @@ -11,8 +11,6 @@ DEVELOPER_DIR: "/Applications/Xcode-12.4.app/Contents/Developer" # Avoid conflicting with other projects running on the same machine. SCCACHE_SERVER_PORT: 4228 - # Do not let the sccache server shut down due to a slow machine. - SCCACHE_IDLE_TIMEOUT: 0 ### Build and test -- GitLab From 2ff2059f4811b5eee0b9e6af146b785cb5bb0050 Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Thu, 19 Aug 2021 18:48:43 -0400 Subject: [PATCH 28/52] ENH: Added Ability to Set Attribute Editor Panel's Title The Attribute Editor Panel name can now be configure by a smtk::view::Configuration. If the Configuration is Top Level then the following Configuration Attributes can be used: * AttributePanelTitle - defines the base name of the Panel. If not specified it defaults to Attribute Editor. * IncludeResourceNameInPanel - if specified and set to true, the Panel's title will include the name of the resource in () SimpleAttribute.sbt contains an example: .. code-block:: xml Developer changes ~~~~~~~~~~~~~~~~~~ * pqSMTKAttributePanel::updateTitle now takes in a const smtk::view::ConfigurationPtr& --- .../attribute_collection/SimpleAttribute.sbt | 3 ++ doc/release/notes/settingEditorTitle.rst | 28 ++++++++++++++++++ .../appcomponents/pqSMTKAttributePanel.cxx | 29 ++++++++++++++----- .../appcomponents/pqSMTKAttributePanel.h | 2 +- 4 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 data/attribute/attribute_collection/SimpleAttribute.sbt create mode 100644 doc/release/notes/settingEditorTitle.rst diff --git a/data/attribute/attribute_collection/SimpleAttribute.sbt b/data/attribute/attribute_collection/SimpleAttribute.sbt new file mode 100644 index 0000000000..768f04c626 --- /dev/null +++ b/data/attribute/attribute_collection/SimpleAttribute.sbt @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43f373091dfadef4387c09c947bf0df4dc074149eccc34d71258d3cd9ce9b352 +size 578 diff --git a/doc/release/notes/settingEditorTitle.rst b/doc/release/notes/settingEditorTitle.rst new file mode 100644 index 0000000000..7be1d65398 --- /dev/null +++ b/doc/release/notes/settingEditorTitle.rst @@ -0,0 +1,28 @@ +Added Ability to Set Attribute Editor Panel's Title +---------------------------------------------------- + +The Attribute Editor Panel name can now be configure by a smtk::view::Configuration. + +If the Configuration is Top Level then the following Configuration Attributes can be used: + +* AttributePanelTitle - defines the base name of the Panel. If not specified it defaults to Attribute Editor. +* IncludeResourceNameInPanel - if specified and set to true, the Panel's title will include the name of the resource in () + +SimpleAttribute.sbt contains an example: + +.. code-block:: xml + + + + + + + + + + +Developer changes +~~~~~~~~~~~~~~~~~~ + +* pqSMTKAttributePanel::updateTitle now takes in a const smtk::view::ConfigurationPtr& diff --git a/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx b/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx index cdc7cd4bdd..6d99f0dc2c 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx +++ b/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx @@ -46,7 +46,7 @@ pqSMTKAttributePanel::pqSMTKAttributePanel(QWidget* parent) : Superclass(parent) { this->setObjectName("attributeEditor"); - updateTitle(); + this->updateTitle(); QWidget* w = new QWidget(this); w->setObjectName("attributePanel"); this->setWidget(w); @@ -159,8 +159,10 @@ bool pqSMTKAttributePanel::displayResource(const smtk::attribute::ResourcePtr& r resetPanel(rsrc->manager()); } } - - this->updateTitle(); + else + { + this->updateTitle(); + } return didDisplay; } @@ -228,6 +230,7 @@ bool pqSMTKAttributePanel::displayResourceInternal(const smtk::attribute::Resour { didDisplay = this->displayView(view); } + this->updateTitle(view); auto rsrcMgr = rsrc->manager(); if (rsrcMgr) { @@ -312,10 +315,20 @@ void pqSMTKAttributePanel::updateSettings() m_attrUIMgr->setHighlightOnHover(smtkSettings->GetHighlightOnHover()); } -void pqSMTKAttributePanel::updateTitle() +void pqSMTKAttributePanel::updateTitle(const smtk::view::ConfigurationPtr& view) { - auto rsrc = m_rsrc.lock(); - QString panelName = "Attribute Editor"; - QString title = rsrc ? (panelName + '(' + rsrc->name().c_str() + ')') : panelName; - this->setWindowTitle(title); + // By default the Panel's name is Attribute Editor + std::string panelName = "Attribute Editor"; + if (view) + { + // Lets see if the view wants a different base name + view->details().attribute("AttributePanelTitle", panelName); + // Lets see if we are suppose to add the resource name to it + if (view->details().attributeAsBool("IncludeResourceNameInPanel")) + { + auto rsrc = m_rsrc.lock(); + panelName = rsrc ? (panelName + '(' + rsrc->name() + ')') : panelName; + } + } + this->setWindowTitle(panelName.c_str()); } diff --git a/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.h b/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.h index d654e9e36b..97d9e934f8 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.h +++ b/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.h @@ -101,7 +101,7 @@ protected slots: protected: virtual bool displayResourceInternal(const smtk::attribute::ResourcePtr& rsrc); - virtual void updateTitle(); + virtual void updateTitle(const smtk::view::ConfigurationPtr& view = nullptr); smtk::extension::qtUIManager* m_attrUIMgr{ nullptr }; std::weak_ptr m_rsrc; smtk::view::SelectionPtr m_seln; -- GitLab From f6b4d94afb2a71a68a1334cd5e5ead35aef80da9 Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Fri, 20 Aug 2021 13:27:30 -0400 Subject: [PATCH 29/52] SP: Added deprecation macro for 21.09 --- smtk/common/Deprecation.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/smtk/common/Deprecation.h b/smtk/common/Deprecation.h index e88b0bbab8..8189d9ba9d 100644 --- a/smtk/common/Deprecation.h +++ b/smtk/common/Deprecation.h @@ -56,6 +56,12 @@ #define SMTK_DEPRECATION_REASON(version_major, version_minor, reason) \ "SMTK Deprecated in " #version_major "." #version_minor ": " reason +#if SMTK_DEPRECATION_LEVEL >= SMTK_VERSION_CHECK(21, 9) +#define SMTK_DEPRECATED_IN_21_09(reason) SMTK_DEPRECATION(SMTK_DEPRECATION_REASON(21, 09, reason)) +#else +#define SMTK_DEPRECATED_IN_21_09(reason) +#endif + #if SMTK_DEPRECATION_LEVEL >= SMTK_VERSION_CHECK(21, 8) #define SMTK_DEPRECATED_IN_21_08(reason) SMTK_DEPRECATION(SMTK_DEPRECATION_REASON(21, 08, reason)) #else -- GitLab From 2c8c02886376fdd7a8a4463d6f1bc250dbc7696d Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Fri, 20 Aug 2021 15:42:04 -0400 Subject: [PATCH 30/52] ENH: Deprecating qtBaseView::getObject() Developers should be calling qtBaseView::configuration() instead --- doc/release/notes/replacingGetObject.rst | 8 ++++ .../operators/smtkAssignColorsView.cxx | 6 +-- .../qtSimpleExpressionEvaluationView.cxx | 4 +- smtk/extension/qt/qtAnalysisView.cxx | 2 +- .../qt/qtAssociation2ColumnWidget.cxx | 2 +- smtk/extension/qt/qtAssociationView.cxx | 2 +- smtk/extension/qt/qtAttributeView.cxx | 10 ++--- smtk/extension/qt/qtBaseAttributeView.cxx | 4 +- smtk/extension/qt/qtBaseView.cxx | 4 +- smtk/extension/qt/qtBaseView.h | 3 ++ smtk/extension/qt/qtCategorySelectorView.cxx | 10 ++--- smtk/extension/qt/qtGroupView.cxx | 38 +++++++++---------- smtk/extension/qt/qtInstancedView.cxx | 6 +-- .../qt/qtModelEntityAttributeView.cxx | 4 +- smtk/extension/qt/qtOperationView.cxx | 2 +- smtk/extension/qt/qtReferenceItem.h | 2 +- smtk/extension/qt/qtResourceItem.cxx | 2 +- smtk/extension/qt/qtSelectorView.cxx | 12 +++--- smtk/extension/qt/qtSimpleExpressionView.cxx | 6 +-- smtk/extension/qt/qtUIManager.cxx | 7 ++-- 20 files changed, 73 insertions(+), 61 deletions(-) create mode 100644 doc/release/notes/replacingGetObject.rst diff --git a/doc/release/notes/replacingGetObject.rst b/doc/release/notes/replacingGetObject.rst new file mode 100644 index 0000000000..6974ef7d4c --- /dev/null +++ b/doc/release/notes/replacingGetObject.rst @@ -0,0 +1,8 @@ +Replacing qtBaseView::getObject() +--------------------------------- + +This method returns the instance's view::Configuration but the name didn't reflect that. Also the getObject method returned a smtk::view::ConfigurationPtr which means that a new shared pointer was always being created and as a result, incrementing its reference count. The new configuration() method returns a const smtk::view::ConfigurationPtr& instead which does not effect the shared point reference count. + +Developer changes +~~~~~~~~~~~~~~~~~~ +* getObject method is now deprecated. diff --git a/smtk/extension/paraview/operators/smtkAssignColorsView.cxx b/smtk/extension/paraview/operators/smtkAssignColorsView.cxx index ffd17fd8b8..0bd28a1167 100644 --- a/smtk/extension/paraview/operators/smtkAssignColorsView.cxx +++ b/smtk/extension/paraview/operators/smtkAssignColorsView.cxx @@ -260,7 +260,7 @@ void smtkAssignColorsView::prepPaletteChooser() void smtkAssignColorsView::createWidget() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -360,7 +360,7 @@ void smtkAssignColorsView::onShowCategory() void smtkAssignColorsView::updateUI() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view || !this->Widget) { return; @@ -571,5 +571,5 @@ void smtkAssignColorsView::requestModelEntityAssociation() void smtkAssignColorsView::setInfoToBeDisplayed() { - m_infoDialog->displayInfo(this->getObject()); + m_infoDialog->displayInfo(this->configuration()); } diff --git a/smtk/extension/paraview/widgets/qtSimpleExpressionEvaluationView.cxx b/smtk/extension/paraview/widgets/qtSimpleExpressionEvaluationView.cxx index 72e5f16589..e6f1fcde53 100644 --- a/smtk/extension/paraview/widgets/qtSimpleExpressionEvaluationView.cxx +++ b/smtk/extension/paraview/widgets/qtSimpleExpressionEvaluationView.cxx @@ -79,7 +79,7 @@ qtSimpleExpressionEvaluationView::~qtSimpleExpressionEvaluationView() = default; void qtSimpleExpressionEvaluationView::createWidget() { - if (!this->getObject()) + if (!this->configuration()) { return; } @@ -90,7 +90,7 @@ void qtSimpleExpressionEvaluationView::createWidget() // A common add/delete/(copy/paste ??) widget QSplitter* frame = new QSplitter(this->parentWidget()); - frame->setObjectName(this->getObject()->name().c_str()); + frame->setObjectName(this->configuration()->name().c_str()); QFrame* leftFrame = new QFrame(frame); leftFrame->setObjectName("left"); QFrame* rightFrame = new QFrame(frame); diff --git a/smtk/extension/qt/qtAnalysisView.cxx b/smtk/extension/qt/qtAnalysisView.cxx index a3f2f52995..eaa25db501 100644 --- a/smtk/extension/qt/qtAnalysisView.cxx +++ b/smtk/extension/qt/qtAnalysisView.cxx @@ -65,7 +65,7 @@ void qtAnalysisView::createWidget() // If there is a previous qt analysis attribute delete it delete m_qtAnalysisAttribute; - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; diff --git a/smtk/extension/qt/qtAssociation2ColumnWidget.cxx b/smtk/extension/qt/qtAssociation2ColumnWidget.cxx index 2590ca39ba..7adedea172 100644 --- a/smtk/extension/qt/qtAssociation2ColumnWidget.cxx +++ b/smtk/extension/qt/qtAssociation2ColumnWidget.cxx @@ -88,7 +88,7 @@ qtAssociation2ColumnWidget::qtAssociation2ColumnWidget(QWidget* _p, qtBaseView* this->initWidget(); // Are there any customizations? - const smtk::view::Configuration::Component& config = m_view->getObject()->details(); + const smtk::view::Configuration::Component& config = m_view->configuration()->details(); m_allAssociatedMode = config.attributeAsBool("RequireAllAssociated"); std::string val; if (config.attribute("AvailableLabel", val)) diff --git a/smtk/extension/qt/qtAssociationView.cxx b/smtk/extension/qt/qtAssociationView.cxx index f41009629e..eb5876f62f 100644 --- a/smtk/extension/qt/qtAssociationView.cxx +++ b/smtk/extension/qt/qtAssociationView.cxx @@ -251,7 +251,7 @@ void qtAssociationView::onShowCategory() void qtAssociationView::getAllDefinitions() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; diff --git a/smtk/extension/qt/qtAttributeView.cxx b/smtk/extension/qt/qtAttributeView.cxx index f93ea711ce..428557e136 100644 --- a/smtk/extension/qt/qtAttributeView.cxx +++ b/smtk/extension/qt/qtAttributeView.cxx @@ -208,7 +208,7 @@ qtAttributeView::qtAttributeView(const smtk::view::Information& info) m_internals = new qtAttributeViewInternals; m_internals->m_alertIcon = QIcon(this->uiManager()->alertPixmap()); m_internals->m_alertSize = this->uiManager()->alertPixmap().size(); - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); m_hideAssociations = false; m_allAssociatedMode = false; m_disableNameField = false; @@ -254,7 +254,7 @@ smtk::extension::qtAssociationWidget* qtAttributeView::createAssociationWidget( void qtAttributeView::createWidget() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (view == nullptr) { return; @@ -671,7 +671,7 @@ void qtAttributeView::onListBoxSelectionChanged() if (dataItem) { - this->getObject()->details().setAttribute( + this->configuration()->details().setAttribute( m_internals->m_activeAttributeViewAttName, dataItem->id().toString()); this->updateAssociationEnableState(dataItem); this->updateTableWithAttribute(dataItem); @@ -1070,7 +1070,7 @@ void qtAttributeView::onViewBy() // so switch tabs would not reset selection // get the active tab from the view config if it exists std::string activeAttUuid; - this->getObject()->details().attribute( + this->configuration()->details().attribute( m_internals->m_activeAttributeViewAttName, activeAttUuid); smtk::attribute::ConstAttributePtr activeAtt = this->uiManager()->attResource()->findAttribute(smtk::common::UUID(activeAttUuid)); @@ -1242,7 +1242,7 @@ int qtAttributeView::currentViewBy() void qtAttributeView::getAllDefinitions() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; diff --git a/smtk/extension/qt/qtBaseAttributeView.cxx b/smtk/extension/qt/qtBaseAttributeView.cxx index f4d32e19e6..06648ef4fc 100644 --- a/smtk/extension/qt/qtBaseAttributeView.cxx +++ b/smtk/extension/qt/qtBaseAttributeView.cxx @@ -549,7 +549,7 @@ void qtBaseAttributeView::makeTopLevel() this->qtBaseView::makeTopLevel(); m_topLevelInitialized = true; - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); this->Internals->clearWidgets(); const attribute::ResourcePtr attResource = this->uiManager()->attResource(); @@ -634,7 +634,7 @@ void qtBaseAttributeView::showAdvanceLevel(int level) return; } - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; diff --git a/smtk/extension/qt/qtBaseView.cxx b/smtk/extension/qt/qtBaseView.cxx index 46acff7c06..5b37a3c3e3 100644 --- a/smtk/extension/qt/qtBaseView.cxx +++ b/smtk/extension/qt/qtBaseView.cxx @@ -49,7 +49,7 @@ qtBaseView::~qtBaseView() void qtBaseView::makeTopLevel() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -111,5 +111,5 @@ void qtBaseView::onInfo() void qtBaseView::setInfoToBeDisplayed() { - m_infoDialog->displayInfo(this->getObject()); + m_infoDialog->displayInfo(this->configuration()); } diff --git a/smtk/extension/qt/qtBaseView.h b/smtk/extension/qt/qtBaseView.h index f0eb154e11..60198bde57 100644 --- a/smtk/extension/qt/qtBaseView.h +++ b/smtk/extension/qt/qtBaseView.h @@ -18,6 +18,7 @@ #include "smtk/PublicPointerDefs.h" #include "smtk/SharedFromThis.h" +#include "smtk/common/Deprecation.h" #include "smtk/extension/qt/Exports.h" #include "smtk/extension/qt/qtViewInfoDialog.h" #include "smtk/view/BaseView.h" @@ -89,7 +90,9 @@ public: ~qtBaseView() override; + SMTK_DEPRECATED_IN_21_09("Method has been replaced by qtBaseView::configuration") smtk::view::ConfigurationPtr getObject() const { return m_viewInfo.m_view; } + const smtk::view::ConfigurationPtr& configuration() const { return m_viewInfo.m_view; } QWidget* widget() const { return this->Widget; } QWidget* parentWidget() const { return m_viewInfo.m_parent; } qtUIManager* uiManager() const { return m_viewInfo.m_UIManager; } diff --git a/smtk/extension/qt/qtCategorySelectorView.cxx b/smtk/extension/qt/qtCategorySelectorView.cxx index 72faca772d..61b2e50e02 100644 --- a/smtk/extension/qt/qtCategorySelectorView.cxx +++ b/smtk/extension/qt/qtCategorySelectorView.cxx @@ -67,7 +67,7 @@ qtCategorySelectorView::~qtCategorySelectorView() void qtCategorySelectorView::createWidget() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -83,7 +83,7 @@ void qtCategorySelectorView::createWidget() bool qtCategorySelectorView::createChildren() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); int viewsIndex; @@ -144,11 +144,11 @@ void qtCategorySelectorView::getChildView(const std::string& viewType, QListInternals->ChildViews) { - if (childView->getObject()->type() == viewType) + if (childView->configuration()->type() == viewType) { views.append(childView); } - else if (childView->getObject()->type() == "Group") + else if (childView->configuration()->type() == "Group") { qobject_cast(childView)->getChildView(viewType, views); } @@ -171,7 +171,7 @@ void qtCategorySelectorView::addChildView(qtBaseView* child, const std::string& this->Internals->ChildViews.append(child); this->Internals->m_viewCategories.append(cval); QFrame* frame = dynamic_cast(this->Widget); - if (!frame || !child || !child->getObject()) + if (!frame || !child || !child->configuration()) { return; } diff --git a/smtk/extension/qt/qtGroupView.cxx b/smtk/extension/qt/qtGroupView.cxx index 3be1e46834..5508e51784 100644 --- a/smtk/extension/qt/qtGroupView.cxx +++ b/smtk/extension/qt/qtGroupView.cxx @@ -97,7 +97,7 @@ void qtGroupViewInternals::updateChildren(qtGroupView* gview, qtBaseViewMemFn mf tabWidget->blockSignals(true); tabWidget->clear(); std::string lastSavedViewName; - gview->getObject()->details().attribute(m_activeTabViewAttNamee, lastSavedViewName); + gview->configuration()->details().attribute(m_activeTabViewAttNamee, lastSavedViewName); m_TabbedViews.clear(); m_currentTabSelected = -1; int i, size = m_ChildViews.size(); @@ -111,13 +111,13 @@ void qtGroupViewInternals::updateChildren(qtGroupView* gview, qtBaseViewMemFn mf } m_PageWidgets.at(i)->show(); m_TabbedViews.append(child); - if (child->getObject()->name() == lastSavedViewName) + if (child->configuration()->name() == lastSavedViewName) { m_currentTabSelected = m_TabbedViews.size() - 1; } if (m_PageIcons.at(i).isNull()) { - QString secTitle = child->getObject()->label().c_str(); + QString secTitle = child->configuration()->label().c_str(); if (child->isValid()) { tabWidget->addTab(m_PageWidgets.at(i), secTitle); @@ -164,7 +164,7 @@ qtGroupView::qtGroupView(const smtk::view::Information& info) QPixmap image = this->uiManager()->alertPixmap(); QTransform transform; std::string val; - auto view = this->getObject(); + auto view = this->configuration(); transform.scale(0.5, 0.5); if (view) { @@ -206,21 +206,21 @@ void qtGroupView::updateCurrentTab(int ithTab) if (ithTab == -1) { //Clear the active tab info since nothing is selected - this->getObject()->details().setAttribute(m_internals->m_activeTabViewAttNamee, ""); + this->configuration()->details().setAttribute(m_internals->m_activeTabViewAttNamee, ""); return; } qtBaseView* currView = this->getChildView(ithTab); if (currView) { - this->getObject()->details().setAttribute( - m_internals->m_activeTabViewAttNamee, currView->getObject()->name()); + this->configuration()->details().setAttribute( + m_internals->m_activeTabViewAttNamee, currView->configuration()->name()); currView->updateUI(); } } void qtGroupView::createWidget() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -257,7 +257,7 @@ void qtGroupView::createWidget() QTabWidget* tab = new QTabWidget(this->parentWidget()); // If we have previously created a widget for this view // lets get the name of the last selected tab View name - this->getObject()->details().attribute( + this->configuration()->details().attribute( m_internals->m_activeTabViewAttNamee, m_internals->m_savedViewName); tab->setUsesScrollButtons(true); this->Widget = tab; @@ -325,8 +325,8 @@ void qtGroupView::createWidget() qtBaseView* currView = this->getChildView(m_internals->m_currentTabSelected); if (currView) { - m_internals->m_savedViewName = currView->getObject()->name(); - this->getObject()->details().setAttribute( + m_internals->m_savedViewName = currView->configuration()->name(); + this->configuration()->details().setAttribute( m_internals->m_activeTabViewAttNamee, m_internals->m_savedViewName); } } @@ -430,7 +430,7 @@ void qtGroupView::showAdvanceLevelOverlay(bool show) void qtGroupView::addTabEntry(qtBaseView* child) { QTabWidget* tabWidget = dynamic_cast(this->Widget); - if (!tabWidget || !child || !child->getObject()) + if (!tabWidget || !child || !child->configuration()) { return; } @@ -441,7 +441,7 @@ void qtGroupView::addTabEntry(qtBaseView* child) QScrollArea* tabPage = new QScrollArea(tabWidget); tabPage->setWidgetResizable(true); tabPage->setFrameShape(QFrame::NoFrame); - QString secTitle = child->getObject()->label().c_str(); + QString secTitle = child->configuration()->label().c_str(); QString name = "tab" + QString(secTitle); tabPage->setObjectName(name); tabPage->setWidget(content); @@ -454,7 +454,7 @@ void qtGroupView::addTabEntry(qtBaseView* child) //using the ui label name find if we have an icon resource QString resourceName = QApplication::applicationDirPath().append("/../Resources/Icons/"); //QString resourceName = ":/SimBuilder/Icons/"; - resourceName.append(child->getObject()->iconName().c_str()); + resourceName.append(child->configuration()->iconName().c_str()); resourceName.append(".png"); // If user specified icons are not found, use default ones @@ -504,7 +504,7 @@ void qtGroupView::addTabEntry(qtBaseView* child) m_internals->m_TabbedViews.append(child); tabWidget->setTabToolTip(index, secTitle); - if (child->getObject()->name() == m_internals->m_savedViewName) + if (child->configuration()->name() == m_internals->m_savedViewName) { m_internals->m_currentTabSelected = index; } @@ -549,14 +549,14 @@ void qtGroupView::childModified() void qtGroupView::addGroupBoxEntry(qtBaseView* child) { QFrame* frame = dynamic_cast(this->Widget); - if (!frame || !child || !child->getObject()) + if (!frame || !child || !child->configuration()) { return; } QObject::connect(child, &qtBaseView::modified, this, &qtGroupView::childModified); smtk::extension::qtCollapsibleGroupWidget* gw = new qtCollapsibleGroupWidget(frame); this->Widget->layout()->addWidget(gw); - gw->setName(child->getObject()->label().c_str()); + gw->setName(child->configuration()->label().c_str()); gw->contentsLayout()->addWidget(child->widget()); gw->collapse(); } @@ -564,12 +564,12 @@ void qtGroupView::addGroupBoxEntry(qtBaseView* child) void qtGroupView::addTileEntry(qtBaseView* child) { QFrame* frame = dynamic_cast(this->Widget); - if (!frame || !child || !child->getObject()) + if (!frame || !child || !child->configuration()) { return; } QObject::connect(child, &qtBaseView::modified, this, &qtGroupView::childModified); - QLabel* label = new QLabel(child->getObject()->label().c_str(), this->Widget); + QLabel* label = new QLabel(child->configuration()->label().c_str(), this->Widget); m_internals->m_Labels.append(label); QFont titleFont; titleFont.setBold(true); diff --git a/smtk/extension/qt/qtInstancedView.cxx b/smtk/extension/qt/qtInstancedView.cxx index 2cf6f459f0..d33693f493 100644 --- a/smtk/extension/qt/qtInstancedView.cxx +++ b/smtk/extension/qt/qtInstancedView.cxx @@ -86,7 +86,7 @@ qtInstancedView::~qtInstancedView() void qtInstancedView::createWidget() { - if (!this->getObject()) + if (!this->configuration()) { return; } @@ -101,7 +101,7 @@ void qtInstancedView::createWidget() } this->Widget = new QFrame(this->parentWidget()); - this->Widget->setObjectName(this->getObject()->name().c_str()); + this->Widget->setObjectName(this->configuration()->name().c_str()); //create the layout for the tabs area QVBoxLayout* layout = new QVBoxLayout(this->Widget); layout->setMargin(0); @@ -135,7 +135,7 @@ void qtInstancedView::onShowCategory() void qtInstancedView::updateUI() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; diff --git a/smtk/extension/qt/qtModelEntityAttributeView.cxx b/smtk/extension/qt/qtModelEntityAttributeView.cxx index ae411115ee..e6fea02d4a 100644 --- a/smtk/extension/qt/qtModelEntityAttributeView.cxx +++ b/smtk/extension/qt/qtModelEntityAttributeView.cxx @@ -257,7 +257,7 @@ qtModelEntityAttributeView::attDefinitionMap() const void qtModelEntityAttributeView::createWidget() { - auto view = this->getObject(); + auto view = this->configuration(); if (view == nullptr) { return; @@ -699,7 +699,7 @@ void qtModelEntityAttributeView::displayAttribute(smtk::attribute::AttributePtr void qtModelEntityAttributeView::getAllDefinitions() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; diff --git a/smtk/extension/qt/qtOperationView.cxx b/smtk/extension/qt/qtOperationView.cxx index 323843dd4f..c87b979d6a 100644 --- a/smtk/extension/qt/qtOperationView.cxx +++ b/smtk/extension/qt/qtOperationView.cxx @@ -73,7 +73,7 @@ qtOperationView::qtOperationView(const OperationViewInfo& info) this->Internals->m_operator = info.m_operator; // We need to create a new View for the internal instanced View this->Internals->m_instancedViewDef = smtk::view::Configuration::New("Instanced", "Parameters"); - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (view) { this->Internals->m_instancedViewDef->copyContents(*view); diff --git a/smtk/extension/qt/qtReferenceItem.h b/smtk/extension/qt/qtReferenceItem.h index eef104c038..bcdf734475 100644 --- a/smtk/extension/qt/qtReferenceItem.h +++ b/smtk/extension/qt/qtReferenceItem.h @@ -124,7 +124,7 @@ protected slots: protected: /**\brief Subclasses override this to create a model of the appropriate type. * - * The model should be configured using information the item (this->getObject()) + * The model should be configured using information the item (this->configuration()) * and be ready for use. */ virtual smtk::view::PhraseModelPtr createPhraseModel() const; diff --git a/smtk/extension/qt/qtResourceItem.cxx b/smtk/extension/qt/qtResourceItem.cxx index fe2d6bb24f..ff08398242 100644 --- a/smtk/extension/qt/qtResourceItem.cxx +++ b/smtk/extension/qt/qtResourceItem.cxx @@ -97,7 +97,7 @@ smtk::view::PhraseModelPtr qtResourceItem::createPhraseModel() const // auto phraseModel = smtk::view::ResourcePhraseModel::create(config); auto phraseModel = m_itemInfo.uiManager()->viewManager()->phraseModelFactory().createFromConfiguration( - m_itemInfo.baseView()->getObject().get()); + m_itemInfo.baseView()->configuration().get()); if ( !phraseModel || !m_p->m_phraseModel->badges().findBadgeOfType()) diff --git a/smtk/extension/qt/qtSelectorView.cxx b/smtk/extension/qt/qtSelectorView.cxx index f28070c463..2e4cbf265f 100644 --- a/smtk/extension/qt/qtSelectorView.cxx +++ b/smtk/extension/qt/qtSelectorView.cxx @@ -71,7 +71,7 @@ qtSelectorView::~qtSelectorView() void qtSelectorView::createWidget() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -94,7 +94,7 @@ void qtSelectorView::createWidget() bool qtSelectorView::createSelector() { //create the layout for the frame area - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); QVBoxLayout* layout = new QVBoxLayout(this->Widget); layout->setMargin(0); this->Widget->setLayout(layout); @@ -172,7 +172,7 @@ bool qtSelectorView::isEmpty() const bool qtSelectorView::createChildren() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); // We need the selector item's definition in order to get the enumeration info @@ -245,11 +245,11 @@ void qtSelectorView::getChildView(const std::string& viewType, QListChildViews) { - if (childView->getObject()->type() == viewType) + if (childView->configuration()->type() == viewType) { views.append(childView); } - else if (childView->getObject()->type() == "Group") + else if (childView->configuration()->type() == "Group") { qobject_cast(childView)->getChildView(viewType, views); } @@ -272,7 +272,7 @@ void qtSelectorView::addChildView(qtBaseView* child, int viewEnumIndex) m_internals->ChildViews.append(child); m_internals->m_viewEnumIdices.append(viewEnumIndex); QFrame* frame = dynamic_cast(this->Widget); - if (!frame || !child || !child->getObject()) + if (!frame || !child || !child->configuration()) { return; } diff --git a/smtk/extension/qt/qtSimpleExpressionView.cxx b/smtk/extension/qt/qtSimpleExpressionView.cxx index 92d6244594..3ece5da647 100644 --- a/smtk/extension/qt/qtSimpleExpressionView.cxx +++ b/smtk/extension/qt/qtSimpleExpressionView.cxx @@ -99,7 +99,7 @@ qtSimpleExpressionView::~qtSimpleExpressionView() void qtSimpleExpressionView::createWidget() { - if (!this->getObject()) + if (!this->configuration()) { return; } @@ -110,7 +110,7 @@ void qtSimpleExpressionView::createWidget() // A common add/delete/(copy/paste ??) widget QSplitter* frame = new QSplitter(this->parentWidget()); - frame->setObjectName(this->getObject()->name().c_str()); + frame->setObjectName(this->configuration()->name().c_str()); QFrame* leftFrame = new QFrame(frame); leftFrame->setObjectName("left"); QFrame* rightFrame = new QFrame(frame); @@ -774,7 +774,7 @@ void qtSimpleExpressionView::onShowCategory() void qtSimpleExpressionView::updateUI() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; diff --git a/smtk/extension/qt/qtUIManager.cxx b/smtk/extension/qt/qtUIManager.cxx index f2ae607ac4..f4cb7a524f 100644 --- a/smtk/extension/qt/qtUIManager.cxx +++ b/smtk/extension/qt/qtUIManager.cxx @@ -232,9 +232,9 @@ void qtUIManager::initializeUI(QWidget* pWidget, bool useInternalFileBrowser) } if (m_topView) { - if (m_topView->getObject()->details().attribute(m_activeAdvLevelXmlAttName)) + if (m_topView->configuration()->details().attribute(m_activeAdvLevelXmlAttName)) { - m_topView->getObject()->details().attributeAsInt( + m_topView->configuration()->details().attributeAsInt( m_activeAdvLevelXmlAttName, m_currentAdvLevel); } if (m_currentAdvLevel) // only build advanced level when needed) @@ -447,7 +447,8 @@ void qtUIManager::setAdvanceLevel(int b) if (m_topView) { m_topView->showAdvanceLevel(b); - m_topView->getObject()->details().setAttribute(m_activeAdvLevelXmlAttName, std::to_string(b)); + m_topView->configuration()->details().setAttribute( + m_activeAdvLevelXmlAttName, std::to_string(b)); } } -- GitLab From 5f10112d18fa4e210a23ab95830f79882a4e27bb Mon Sep 17 00:00:00 2001 From: Ryan Krattiger Date: Mon, 23 Aug 2021 22:12:30 -0500 Subject: [PATCH 31/52] Make polygon session resource clean after Read --- smtk/session/polygon/operators/Read.cxx | 12 ++++++++++++ smtk/session/polygon/operators/Read.h | 1 + 2 files changed, 13 insertions(+) diff --git a/smtk/session/polygon/operators/Read.cxx b/smtk/session/polygon/operators/Read.cxx index 3152b174cf..e32bba1eb1 100644 --- a/smtk/session/polygon/operators/Read.cxx +++ b/smtk/session/polygon/operators/Read.cxx @@ -65,6 +65,18 @@ Read::Result Read::operateInternal() return result; } +void Read::markModifiedResources(Read::Result& res) +{ + auto resourceItem = res->findResource("resource"); + for (auto rit = resourceItem->begin(); rit != resourceItem->end(); ++rit) + { + auto resource = std::dynamic_pointer_cast(*rit); + + // Set the resource as unmodified from its persistent (i.e. on-disk) state + resource->setClean(true); + } +} + const char* Read::xmlDescription() const { return Read_xml; diff --git a/smtk/session/polygon/operators/Read.h b/smtk/session/polygon/operators/Read.h index 6b2a1bab40..d267544ac2 100644 --- a/smtk/session/polygon/operators/Read.h +++ b/smtk/session/polygon/operators/Read.h @@ -33,6 +33,7 @@ public: protected: Result operateInternal() override; + void markModifiedResources(Result& res) override; const char* xmlDescription() const override; }; -- GitLab From 66c14a280ef283936815a44d2718efd549d654ac Mon Sep 17 00:00:00 2001 From: David Thompson Date: Tue, 24 Aug 2021 08:37:49 -0400 Subject: [PATCH 32/52] Fix misnamed header file that broke install. --- smtk/task/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smtk/task/CMakeLists.txt b/smtk/task/CMakeLists.txt index 6133288866..0087f2019d 100644 --- a/smtk/task/CMakeLists.txt +++ b/smtk/task/CMakeLists.txt @@ -30,10 +30,10 @@ set(taskHeaders Manager.h Registrar.h adaptor/ResourceAndRole.h - json/Adaptor.h json/Configurator.h json/Configurator.txx json/Helper.h + json/jsonAdaptor.h json/jsonManager.h ) -- GitLab From f264b399d0a11bdf6710517f4cd72fe8711ad8c4 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 24 Aug 2021 12:32:39 -0400 Subject: [PATCH 33/52] ci: remove old Windows workaround CI machines are now deployed with the `core.longpaths` Git configuration set to `true`, so this is not an issue anymore. --- .gitlab/os-windows.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index 8250e5c67b..3c111a4858 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml @@ -92,9 +92,4 @@ - *before_script_windows - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1 - ctest --output-on-failure -V -S .gitlab/ci/ctest_test.cmake - after_script: - # Ensure that `git clean` can always remove the directory. But don't - # remove so much that the build cannot be debugged. - # https://github.com/git-for-windows/git/issues/2715 - - cmd /C "rmdir /S /Q build\superbuild\smtk\build\PluginTests" interruptible: true -- GitLab From c8dcfcd886ab7dceb4c38688658427817ec2928c Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 24 Aug 2021 13:15:47 -0400 Subject: [PATCH 34/52] CTestCustom: ignore warnings from sccache that don't cause failures The CI setup on macOS is set up to fail on warnings. This warning comes from `sccache` that the server appears to have disappeared, so compilation is being performed "locally". This is completely fine and not something we need to tell CDash about. --- CMake/CTestCustom.cmake.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMake/CTestCustom.cmake.in b/CMake/CTestCustom.cmake.in index 3aba728952..6bb43e2943 100644 --- a/CMake/CTestCustom.cmake.in +++ b/CMake/CTestCustom.cmake.in @@ -36,6 +36,9 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION # Warnings from the delaunay submodule. "thirdparty/delaunay" + + # Warnings from sccache that we don't care about. + "sccache: warning: The server looks like it shut down unexpectedly, compiling locally instead" ) ##------------------------------------------------------------------------------ -- GitLab From 1ef3d5bac6ab953c6afa215da8e9e09f830c0a9d Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 24 Aug 2021 14:13:35 -0400 Subject: [PATCH 35/52] ResourceAndRole: explicitly handle weak_ptr to shared_ptr casts The C++ standard's `std::shared_ptr` only provides an explicit constructor from `std::weak_ptr` that throws if the `std::weak_ptr` doesn't have any strong references left. GCC 7 refuses to compile the previous code since there is no eligible constructor between the two. Instead, explicitly loop and only add resources which successfully lock. --- smtk/task/adaptor/ResourceAndRole.cxx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/smtk/task/adaptor/ResourceAndRole.cxx b/smtk/task/adaptor/ResourceAndRole.cxx index a53d280a41..449834bfd7 100644 --- a/smtk/task/adaptor/ResourceAndRole.cxx +++ b/smtk/task/adaptor/ResourceAndRole.cxx @@ -50,8 +50,15 @@ bool ResourceAndRole::reconfigureTask() std::map> configs; gather->visitResourceSets( [&configs](const GatherResources::ResourceSet& resourceSet) -> smtk::common::Visit { - configs[resourceSet.m_role].insert( - resourceSet.m_resources.begin(), resourceSet.m_resources.end()); + auto& role_config = configs[resourceSet.m_role]; + for (auto const& resource : resourceSet.m_resources) + { + auto resource_locked = resource.lock(); + if (resource_locked) + { + role_config.insert(resource_locked); + } + } return smtk::common::Visit::Continue; }); if (auto fill = std::dynamic_pointer_cast(dest)) -- GitLab From 26df1e6c8ac146d9766d851ae17f3cbe4f49ba92 Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Thu, 19 Aug 2021 10:34:15 -0400 Subject: [PATCH 36/52] ENH: Making the qtView System Compatible with Tasks In order to make the View System more flexible and to work with the new Task System, the following changes were made: * smtk::view::Information is now derived from TypeContainer and is no longer an abstract class. As a result it can now do the job that ViewInfo and OperationViewInfo does * ViewInfo and OperationViewInfo are no longer needed and have been removed. * qtBaseView's m_viewInfo is now an instance of smtk::view::Information and not ViewInfo * qtViews derived from qtBaseAttributeView now access their attribute resource from their smtk::view::Information instead of from the qtUIManager * qtUIManager::attResource has been deprecated to make it easier to find existing qtView classes that are getting their attribute resource from the UI Manager. Also deleted the qtViewInterface class since it has not been used for a long time and is no longer needed. --- .../depricatingQtUIManagerAttResource.rst | 13 +++++ .../notes/usingTypeContainersForViews.rst | 21 +++++++ smtk/common/TypeContainer.h | 12 ++++ .../appcomponents/pqSMTKResourceBrowser.cxx | 4 +- .../appcomponents/pqSMTKResourcePanel.cxx | 5 +- .../operators/smtkAssignColorsView.cxx | 16 +++--- .../paraview/operators/smtkAssignColorsView.h | 6 +- smtk/extension/qt/CMakeLists.txt | 2 - .../qt/examples/cxx/testItemFactory.cxx | 5 +- smtk/extension/qt/qtAnalysisView.cxx | 14 +++-- smtk/extension/qt/qtAssociationView.cxx | 25 +++++---- smtk/extension/qt/qtAttributeEditorDialog.cxx | 9 ++- smtk/extension/qt/qtAttributeView.cxx | 24 ++++---- smtk/extension/qt/qtBaseAttributeView.cxx | 23 ++++++-- smtk/extension/qt/qtBaseAttributeView.h | 6 ++ smtk/extension/qt/qtBaseView.cxx | 15 +++-- smtk/extension/qt/qtBaseView.h | 56 +++++-------------- smtk/extension/qt/qtCategorySelectorView.cxx | 20 ++++--- smtk/extension/qt/qtDiscreteValueEditor.cxx | 22 +++----- smtk/extension/qt/qtGroupView.cxx | 19 ++++--- smtk/extension/qt/qtInstancedView.cxx | 22 ++++---- smtk/extension/qt/qtInstancedView.h | 9 +-- smtk/extension/qt/qtItem.cxx | 9 ++- smtk/extension/qt/qtItem.h | 3 + .../qt/qtModelEntityAttributeView.cxx | 28 +++++----- smtk/extension/qt/qtOperationView.cxx | 42 ++++++++------ smtk/extension/qt/qtOperationView.h | 35 ++---------- smtk/extension/qt/qtReferenceItemEditor.cxx | 4 +- smtk/extension/qt/qtResourceBrowser.cxx | 21 ++++--- smtk/extension/qt/qtResourceBrowser.h | 2 +- smtk/extension/qt/qtSMTKUtilities.h | 3 +- smtk/extension/qt/qtSelectorView.cxx | 21 ++++--- smtk/extension/qt/qtSimpleExpressionView.cxx | 14 +++-- smtk/extension/qt/qtUIManager.cxx | 51 +++++++++-------- smtk/extension/qt/qtUIManager.h | 20 ++++--- smtk/extension/qt/qtViewInterface.cxx | 16 ------ smtk/extension/qt/qtViewInterface.h | 49 ---------------- smtk/project/plugin/pqSMTKProjectPanel.cxx | 5 +- smtk/view/Information.h | 29 ++++++++-- 39 files changed, 361 insertions(+), 339 deletions(-) create mode 100644 doc/release/notes/depricatingQtUIManagerAttResource.rst create mode 100644 doc/release/notes/usingTypeContainersForViews.rst delete mode 100644 smtk/extension/qt/qtViewInterface.cxx delete mode 100644 smtk/extension/qt/qtViewInterface.h diff --git a/doc/release/notes/depricatingQtUIManagerAttResource.rst b/doc/release/notes/depricatingQtUIManagerAttResource.rst new file mode 100644 index 0000000000..1d62c87bd8 --- /dev/null +++ b/doc/release/notes/depricatingQtUIManagerAttResource.rst @@ -0,0 +1,13 @@ +Changes to qtBaseAttributeView +------------------------------ + +In the past classes derived from qtBaseAttributeView relied on the qtUIManager to get access to the Attribute Resource they depended on. This is no longer the case. The Attribute Resource is now part of the information used to defined the instance. + +Deprecation of qtUIManager::attResource() method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This method is no longer needed for qtBaseAttributeView derived classes. + +Added qtItem::attributeResource() method +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This is a convenience method for accessing the qtItem's underlying Attribute Resource diff --git a/doc/release/notes/usingTypeContainersForViews.rst b/doc/release/notes/usingTypeContainersForViews.rst new file mode 100644 index 0000000000..9d3e7aa4fe --- /dev/null +++ b/doc/release/notes/usingTypeContainersForViews.rst @@ -0,0 +1,21 @@ +Using TypeContainers instead of ViewInfo +---------------------------------------- + +In order to make the View System more flexible and to work with the new Task System, the following changes were made: + +* smtk::view::Information is now derived from TypeContainer and is no longer an abstract class. As a result it can now do the job that ViewInfo and OperationViewInfo does +* ViewInfo and OperationViewInfo are no longer needed. +* qtBaseView's m_viewInfo is now an instance of smtk::view::Information and not ViewInfo + +Developer changes +~~~~~~~~~~~~~~~~~~ + +Unless the qtView is directly accessing m_viewInfo, there should be no required changes. + +When dealing with smtk::view::information, it is important that the type you insert into the container exactly matches the type you use to get information from the container. For example if you insert a QPushButton* into the container and attempt to get a QWidget* back, it will fail and throw an exception. + +So it is recommended you explicitly state the template type instead of having the compiler determine it. In the above example you would need to do an insert(myQtPushButton) in order to get a QWidget* back. + +Removed Data Structures ++++++++++++++++++++++++ +smtk::external::ViewInfo and smtk::external::OperatorViewInfo are no longer needed and have been removed. smtk::view::Information object should be used instead. diff --git a/smtk/common/TypeContainer.h b/smtk/common/TypeContainer.h index 36f8a1ae9f..751e003ccc 100644 --- a/smtk/common/TypeContainer.h +++ b/smtk/common/TypeContainer.h @@ -109,6 +109,7 @@ public: } /// Insert a Type instance into the TypeContainer. + /// Note that if the type already exists in the container, the insertion will fail. template bool insert(const Type& value) { @@ -123,6 +124,17 @@ public: .second; } + /// Insert a Type instance into the TypeContainer if it does not exist already or replace it if it does. + template + bool insert_or_assign(const Type& value) + { + if (this->contains()) + { + this->erase(); + } + return this->insert(value); + } + /// Emplace a Type instance into the TypeContainer. template bool emplace(Args&&... args) diff --git a/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx b/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx index 9cc4c91151..1657243d64 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx +++ b/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx @@ -133,7 +133,7 @@ void pqSMTKResourceBrowser::resourceManagerRemoved(pqSMTKWrapper* mgr, pqServer* void pqSMTKResourceBrowser::initSubphraseGenerator() { - std::string subphraseViewType = smtk::view::SubphraseGenerator::getType(m_viewInfo.m_view); + std::string subphraseViewType = smtk::view::SubphraseGenerator::getType(this->getObject()); auto* smtkSettings = vtkSMTKSettings::GetInstance(); int resourceTreeStyle = smtkSettings->GetResourceTreeStyle(); @@ -159,7 +159,7 @@ void pqSMTKResourceBrowser::initSubphraseGenerator() m_p->m_resourceTreeType.empty() || m_p->m_resourceTreeType != subphraseViewType || (subphraseViewType == "default" && resourceTreeStyle != m_p->m_resourceTreeStyle)) { - smtk::view::ManagerPtr manager = m_viewInfo.m_UIManager->viewManager(); + smtk::view::ManagerPtr manager = this->uiManager()->viewManager(); smtk::view::SubphraseGenerator::Ptr spg = smtk::view::SubphraseGenerator::create( subphraseViewType == "default" ? defaultSubphraseType : subphraseViewType, manager); if (spg) diff --git a/smtk/extension/paraview/appcomponents/pqSMTKResourcePanel.cxx b/smtk/extension/paraview/appcomponents/pqSMTKResourcePanel.cxx index 777a87269f..cd1fa4d1e8 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKResourcePanel.cxx +++ b/smtk/extension/paraview/appcomponents/pqSMTKResourcePanel.cxx @@ -88,7 +88,10 @@ void pqSMTKResourcePanel::resourceManagerAdded(pqSMTKWrapper* wrapper, pqServer* m_viewUIMgr->setSelection(wrapper->smtkSelection()); // m_viewUIMgr->setSelectionBit(1); // ToDo: should be set ? - smtk::extension::ViewInfo resinfo(m_view, this, m_viewUIMgr); + smtk::view::Information resinfo; + resinfo.insert(m_view); + resinfo.insert(this); + resinfo.insert(m_viewUIMgr); // the top-level "Type" in m_view should be pqSMTKResourceBrowser or compatible. auto* baseview = m_viewUIMgr->setSMTKView(resinfo); diff --git a/smtk/extension/paraview/operators/smtkAssignColorsView.cxx b/smtk/extension/paraview/operators/smtkAssignColorsView.cxx index 0bd28a1167..edbffe89f5 100644 --- a/smtk/extension/paraview/operators/smtkAssignColorsView.cxx +++ b/smtk/extension/paraview/operators/smtkAssignColorsView.cxx @@ -129,11 +129,11 @@ public: smtk::shared_ptr CurrentOp; }; -smtkAssignColorsView::smtkAssignColorsView(const OperationViewInfo& info) +smtkAssignColorsView::smtkAssignColorsView(const smtk::view::Information& info) : qtBaseAttributeView(info) { this->Internals = new smtkAssignColorsViewInternals; - this->Internals->CurrentOp = info.m_operator; + this->Internals->CurrentOp = info.get>(); } smtkAssignColorsView::~smtkAssignColorsView() @@ -152,15 +152,13 @@ bool smtkAssignColorsView::displayItem(smtk::attribute::ItemPtr item) const qtBaseView* smtkAssignColorsView::createViewWidget(const smtk::view::Information& info) { - const OperationViewInfo* opinfo = dynamic_cast(&info); - smtkAssignColorsView* view; - if (!opinfo) + if (qtOperationView::validateInformation(info)) { - return nullptr; + auto* view = new smtkAssignColorsView(info); + view->buildUI(); + return view; } - view = new smtkAssignColorsView(*opinfo); - view->buildUI(); - return view; + return nullptr; // Information is not suitable for this View } QIcon smtkAssignColorsView::renderColorSwatch(const QColor& color, int radius) diff --git a/smtk/extension/paraview/operators/smtkAssignColorsView.h b/smtk/extension/paraview/operators/smtkAssignColorsView.h index 13afec5bf5..48a06835a4 100644 --- a/smtk/extension/paraview/operators/smtkAssignColorsView.h +++ b/smtk/extension/paraview/operators/smtkAssignColorsView.h @@ -28,11 +28,7 @@ class SMTKPQOPERATIONVIEWSPLUGIN_EXPORT smtkAssignColorsView public: smtkTypenameMacro(smtkAssignColorsView); - smtkAssignColorsView(const smtk::view::Information& info) - : smtkAssignColorsView(static_cast(info)) - { - } - smtkAssignColorsView(const smtk::extension::OperationViewInfo& info); + smtkAssignColorsView(const smtk::view::Information& info); ~smtkAssignColorsView() override; static smtk::extension::qtBaseView* createViewWidget(const smtk::view::Information& info); diff --git a/smtk/extension/qt/CMakeLists.txt b/smtk/extension/qt/CMakeLists.txt index f933973b75..77ab37de33 100644 --- a/smtk/extension/qt/CMakeLists.txt +++ b/smtk/extension/qt/CMakeLists.txt @@ -54,7 +54,6 @@ set(QAttrLibSrcs qtOverlay.cxx qtTimeZoneRegionModel.cxx qtTimeZoneSelectWidget.cxx - qtViewInterface.cxx qtSMTKUtilities.cxx qtDateTimeItem.cxx @@ -149,7 +148,6 @@ set(QAttrLibHeaders qtSMTKUtilities.h qtStringItem.h qtTypeDeclarations.h - qtViewInterface.h qtViewRegistrar.h ) diff --git a/smtk/extension/qt/examples/cxx/testItemFactory.cxx b/smtk/extension/qt/examples/cxx/testItemFactory.cxx index 7f75f422bb..214392f811 100644 --- a/smtk/extension/qt/examples/cxx/testItemFactory.cxx +++ b/smtk/extension/qt/examples/cxx/testItemFactory.cxx @@ -87,7 +87,10 @@ int testLifecycle() AttributePtr att = createAttribForTest(resource); qtUIManager* mgr = new qtUIManager(resource); QWidget* w = new QWidget; - smtk::extension::ViewInfo vinfo(smtk::view::Configuration::New("base", "test view"), w, mgr); + smtk::view::Information vinfo; + vinfo.insert(smtk::view::Configuration::New("base", "test view")); + vinfo.insert(w); + vinfo.insert(mgr); qtBaseView* v = new qtBaseView(vinfo); qtAttribute* qatt = new qtAttribute(att, w, v); diff --git a/smtk/extension/qt/qtAnalysisView.cxx b/smtk/extension/qt/qtAnalysisView.cxx index eaa25db501..38311eb803 100644 --- a/smtk/extension/qt/qtAnalysisView.cxx +++ b/smtk/extension/qt/qtAnalysisView.cxx @@ -42,9 +42,13 @@ using namespace smtk::extension; qtBaseView* qtAnalysisView::createViewWidget(const smtk::view::Information& info) { - qtAnalysisView* view = new qtAnalysisView(info); - view->buildUI(); - return view; + if (qtBaseAttributeView::validateInformation(info)) + { + auto* view = new qtAnalysisView(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } qtAnalysisView::qtAnalysisView(const smtk::view::Information& info) @@ -77,7 +81,7 @@ void qtAnalysisView::createWidget() layout->setMargin(0); this->Widget->setLayout(layout); - auto attRes = this->uiManager()->attResource(); + auto attRes = this->attributeResource(); std::string attName, defName; view->details().attribute("AnalysisAttributeName", attName); view->details().attribute("AnalysisAttributeType", defName); @@ -140,7 +144,7 @@ void qtAnalysisView::analysisChanged(bool attChanged) // Lets iterate over the items in the analysis attribute and set // the categories accordingly std::set cats; - auto attRes = this->uiManager()->attResource(); + auto attRes = this->attributeResource(); if (attRes == nullptr) { return; // There is nothing we can do diff --git a/smtk/extension/qt/qtAssociationView.cxx b/smtk/extension/qt/qtAssociationView.cxx index eb5876f62f..941809a739 100644 --- a/smtk/extension/qt/qtAssociationView.cxx +++ b/smtk/extension/qt/qtAssociationView.cxx @@ -39,10 +39,8 @@ using namespace smtk::extension; class qtAssociationViewInternals : public Ui::qtAssociationView { public: - QList getCurrentDefs( - smtk::extension::qtUIManager* uiManager) const + QList getCurrentDefs(const ResourcePtr& attResource) const { - auto attResource = uiManager->attResource(); if (!(attResource && attResource->activeCategoriesEnabled())) { // There is no filtering - return everything @@ -69,9 +67,8 @@ public: return defs; } - bool currentDefsIsEmpty(smtk::extension::qtUIManager* uiManager) const + bool currentDefsIsEmpty(const ResourcePtr& attResource) const { - auto attResource = uiManager->attResource(); if (attResource && attResource->activeCategoriesEnabled()) { if (attResource->activeCategories().empty()) @@ -114,9 +111,13 @@ public: qtBaseView* qtAssociationView::createViewWidget(const smtk::view::Information& info) { - qtAssociationView* view = new qtAssociationView(info); - view->buildUI(); - return view; + if (qtBaseAttributeView::validateInformation(info)) + { + auto* view = new qtAssociationView(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } qtAssociationView::qtAssociationView(const smtk::view::Information& info) @@ -149,7 +150,7 @@ void qtAssociationView::createWidget() // since the getAllDefinitions() call needs the categories list in AttDefMap // Create a map for all catagories so we can cluster the definitions this->Internals->AttDefMap.clear(); - const ResourcePtr attResource = this->uiManager()->attResource(); + const ResourcePtr attResource = this->attributeResource(); std::set::const_iterator it; const std::set& cats = attResource->categories(); @@ -217,7 +218,7 @@ void qtAssociationView::updateUI() } QList currentDefs = - this->Internals->getCurrentDefs(this->uiManager()); + this->Internals->getCurrentDefs(this->attributeResource()); std::set atts; // Get all of the attributes that match the list of definitions foreach (attribute::DefinitionPtr attDef, currentDefs) @@ -257,7 +258,7 @@ void qtAssociationView::getAllDefinitions() return; } - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); std::string attName, defName, val; smtk::attribute::AttributePtr att; @@ -339,7 +340,7 @@ void qtAssociationView::getAllDefinitions() // hidden at the same time. bool qtAssociationView::isEmpty() const { - return this->Internals->currentDefsIsEmpty(this->uiManager()); + return this->Internals->currentDefsIsEmpty(this->attributeResource()); } void qtAssociationView::associationsChanged() diff --git a/smtk/extension/qt/qtAttributeEditorDialog.cxx b/smtk/extension/qt/qtAttributeEditorDialog.cxx index 33189de835..8c6189f453 100644 --- a/smtk/extension/qt/qtAttributeEditorDialog.cxx +++ b/smtk/extension/qt/qtAttributeEditorDialog.cxx @@ -48,9 +48,12 @@ qtAttributeEditorDialog::qtAttributeEditorDialog( m_instancedViewDef->details().addChild("InstancedAttributes").addChild("Att"); child.setAttribute("Name", m_attribute->name()).setAttribute("Type", m_attribute->type()); - ViewInfo v(m_instancedViewDef, this->m_widget->attributeFrame, m_uiManager); - qtInstancedView* iview = dynamic_cast( - qtInstancedView::createViewWidget(v, m_attribute->attributeResource())); + smtk::view::Information v; + v.insert(m_instancedViewDef); + v.insert(this->m_widget->attributeFrame); + v.insert(m_uiManager); + v.insert>(m_attribute->attributeResource()); + qtInstancedView* iview = dynamic_cast(qtInstancedView::createViewWidget(v)); m_instancedView.reset(iview); m_widget->attributeName->setText(m_attribute->name().c_str()); diff --git a/smtk/extension/qt/qtAttributeView.cxx b/smtk/extension/qt/qtAttributeView.cxx index 428557e136..b774b2b11e 100644 --- a/smtk/extension/qt/qtAttributeView.cxx +++ b/smtk/extension/qt/qtAttributeView.cxx @@ -104,6 +104,7 @@ public: // of the UI Manager and whether the View is to ignore // categories const QList getCurrentDefs( + const ResourcePtr& attResource, smtk::extension::qtUIManager* uiManager, bool ignoreCategories) const { @@ -112,7 +113,6 @@ public: return removeAdvancedDefs(uiManager, this->AllDefs); } - auto attResource = uiManager->attResource(); if (!(attResource && attResource->activeCategoriesEnabled())) { // There are no active categories - return everything @@ -197,9 +197,13 @@ public: qtBaseView* qtAttributeView::createViewWidget(const smtk::view::Information& info) { - qtAttributeView* view = new qtAttributeView(info); - view->buildUI(); - return view; + if (qtBaseAttributeView::validateInformation(info)) + { + auto* view = new qtAttributeView(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } qtAttributeView::qtAttributeView(const smtk::view::Information& info) @@ -269,7 +273,7 @@ void qtAttributeView::createWidget() // since the getAllDefinitions() call needs the categories list in AttDefMap // Create a map for all categories so we can cluster the definitions m_internals->AttDefMap.clear(); - const ResourcePtr attResource = this->uiManager()->attResource(); + const ResourcePtr attResource = this->attributeResource(); std::set::const_iterator it; const std::set& cats = attResource->categories(); @@ -835,7 +839,7 @@ smtk::attribute::DefinitionPtr qtAttributeView::getCurrentDef() const foreach ( attribute::DefinitionPtr attDef, - m_internals->getCurrentDefs(this->uiManager(), m_ignoreCategories)) + m_internals->getCurrentDefs(this->attributeResource(), this->uiManager(), m_ignoreCategories)) { std::string txtDef = attDef->displayedTypeName(); if (strDef == QString::fromUtf8(txtDef.c_str())) @@ -1001,7 +1005,7 @@ void qtAttributeView::onViewBy() } QList currentDefs = - m_internals->getCurrentDefs(this->uiManager(), m_ignoreCategories); + m_internals->getCurrentDefs(this->attributeResource(), this->uiManager(), m_ignoreCategories); m_internals->AddAction->setEnabled(currentDefs.count() > 0); @@ -1073,7 +1077,7 @@ void qtAttributeView::onViewBy() this->configuration()->details().attribute( m_internals->m_activeAttributeViewAttName, activeAttUuid); smtk::attribute::ConstAttributePtr activeAtt = - this->uiManager()->attResource()->findAttribute(smtk::common::UUID(activeAttUuid)); + this->attributeResource()->findAttribute(smtk::common::UUID(activeAttUuid)); if (activeAtt) { @@ -1248,7 +1252,7 @@ void qtAttributeView::getAllDefinitions() return; } - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); std::string attName, defName, val, styleName; smtk::attribute::AttributePtr att; @@ -1396,7 +1400,7 @@ void qtAttributeView::showAdvanceLevelOverlay(bool show) bool qtAttributeView::isEmpty() const { QList currentDefs = - m_internals->getCurrentDefs(this->uiManager(), m_ignoreCategories); + m_internals->getCurrentDefs(this->attributeResource(), this->uiManager(), m_ignoreCategories); return currentDefs.isEmpty(); } diff --git a/smtk/extension/qt/qtBaseAttributeView.cxx b/smtk/extension/qt/qtBaseAttributeView.cxx index 06648ef4fc..aca3da8f9c 100644 --- a/smtk/extension/qt/qtBaseAttributeView.cxx +++ b/smtk/extension/qt/qtBaseAttributeView.cxx @@ -75,15 +75,21 @@ public: QPointer m_configurationLabel; }; +bool qtBaseAttributeView::validateInformation(const smtk::view::Information& info) +{ + return qtBaseView::validateInformation(info) && + info.contains>(); +} + qtBaseAttributeView::qtBaseAttributeView(const smtk::view::Information& info) : qtBaseView(info) , m_topLevelCanCreateConfigurations(false) { this->Internals = new qtBaseAttributeViewInternals; m_ScrollArea = nullptr; - m_fixedLabelWidth = m_viewInfo.m_UIManager->maxValueLabelLength(); + m_fixedLabelWidth = this->uiManager()->maxValueLabelLength(); m_topLevelInitialized = false; - m_ignoreCategories = m_viewInfo.m_view->details().attributeAsBool("IgnoreCategories"); + m_ignoreCategories = this->getObject()->details().attributeAsBool("IgnoreCategories"); // We need to be able to determine within the a Signal Operation, which View caused // the change in order to avoid infinite loops. To do this, each View will have an addressString // set to its address. This string is then passed to the signalAttribute function when needed. @@ -97,6 +103,11 @@ qtBaseAttributeView::~qtBaseAttributeView() delete this->Internals; } +smtk::attribute::ResourcePtr qtBaseAttributeView::attributeResource() const +{ + return m_viewInfo.get>().lock(); +} + void qtBaseAttributeView::getDefinitions( smtk::attribute::DefinitionPtr attDef, QList& defs) @@ -139,7 +150,7 @@ bool qtBaseAttributeView::displayItemDefinition( { return false; } - auto attResoure = this->uiManager()->attResource(); + auto attResoure = this->attributeResource(); return attResoure->passActiveCategoryCheck(idef->categories()); } @@ -552,7 +563,7 @@ void qtBaseAttributeView::makeTopLevel() smtk::view::ConfigurationPtr view = this->configuration(); this->Internals->clearWidgets(); - const attribute::ResourcePtr attResource = this->uiManager()->attResource(); + const attribute::ResourcePtr attResource = this->attributeResource(); this->topLevelPrepAdvanceLevels(view); this->topLevelPrepConfigurations(view, attResource); @@ -723,7 +734,7 @@ bool qtBaseAttributeView::isEmpty() const void qtBaseAttributeView::onConfigurationChanged(int index) { std::set cats; - smtk::attribute::ResourcePtr attRes = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr attRes = this->attributeResource(); smtk::attribute::AttributePtr att; std::string attName; @@ -783,7 +794,7 @@ void qtBaseAttributeView::onConfigurationChanged(int index) void qtBaseAttributeView::prepConfigurationComboBox(const std::string& newConfigurationName) { std::set cats; - smtk::attribute::ResourcePtr attRes = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr attRes = this->attributeResource(); smtk::attribute::DefinitionPtr def = m_topLevelConfigurationDef.lock(); if (def == nullptr) { diff --git a/smtk/extension/qt/qtBaseAttributeView.h b/smtk/extension/qt/qtBaseAttributeView.h index e45ff38513..4a745062f1 100644 --- a/smtk/extension/qt/qtBaseAttributeView.h +++ b/smtk/extension/qt/qtBaseAttributeView.h @@ -64,6 +64,12 @@ public: void setIgnoreCategories(bool mode); bool ignoreCategories() const { return m_ignoreCategories; } + /// Return the attribute resource used by this View + smtk::attribute::ResourcePtr attributeResource() const; + + // Validates the view information to see if it is suitable for creating a qtAttributeBaseView instance + static bool validateInformation(const smtk::view::Information& info); + signals: void modified(smtk::attribute::ItemPtr); diff --git a/smtk/extension/qt/qtBaseView.cxx b/smtk/extension/qt/qtBaseView.cxx index 5b37a3c3e3..4a48fdc57e 100644 --- a/smtk/extension/qt/qtBaseView.cxx +++ b/smtk/extension/qt/qtBaseView.cxx @@ -28,17 +28,24 @@ using namespace smtk::extension; -qtBaseView::qtBaseView(const ViewInfo& info) +bool qtBaseView::validateInformation(const smtk::view::Information& info) +{ + return info.contains() && info.contains() && + info.contains(); +} + +qtBaseView::qtBaseView(const smtk::view::Information& info) { m_viewInfo = info; this->Widget = nullptr; m_advOverlayVisible = false; m_isTopLevel = false; m_useSelectionManager = false; - if (m_viewInfo.m_view) + const auto& view = this->getObject(); + if (view) { - m_isTopLevel = m_viewInfo.m_view->details().attributeAsBool("TopLevel"); - m_useSelectionManager = m_viewInfo.m_view->details().attributeAsBool("UseSelectionManager"); + m_isTopLevel = view->details().attributeAsBool("TopLevel"); + m_useSelectionManager = view->details().attributeAsBool("UseSelectionManager"); } } diff --git a/smtk/extension/qt/qtBaseView.h b/smtk/extension/qt/qtBaseView.h index 60198bde57..1c21e1b304 100644 --- a/smtk/extension/qt/qtBaseView.h +++ b/smtk/extension/qt/qtBaseView.h @@ -34,37 +34,6 @@ class qtUIManager; class qtItem; class qtViewInfoDialog; -// This struct is used to initialize qtView-based classes -class SMTKQTEXT_EXPORT ViewInfo : public smtk::view::Information -{ -public: - ViewInfo(smtk::view::ConfigurationPtr view, QWidget* parent, qtUIManager* uiman) - : m_view(view) - , m_parent(parent) - , m_UIManager(uiman) - { - } - - // ViewInfo(smtk::view::ConfigurationPtr view, QWidget* parent, qtUIManager* uiman, - // const std::map& layoutDict) - // : m_view(view) - // , m_parent(parent) - // , m_UIManager(uiman) - // , m_layoutDict(layoutDict) - // { - // } - - ViewInfo() = default; - ~ViewInfo() override = default; - - const smtk::view::Configuration* configuration() const override { return m_view.get(); } - - smtk::view::ConfigurationPtr m_view; // View Definition - QWidget* m_parent; // Parent Widget of the View - qtUIManager* m_UIManager; // UI Manager - // std::map m_layoutDict; // Widget Layout Dictionary -}; - ///\brief A base class for all view types implemented using Qt class SMTKQTEXT_EXPORT qtBaseView : public QObject @@ -82,20 +51,22 @@ public: smtkTypenameMacro(qtBaseView); - qtBaseView(const ViewInfo& info); - qtBaseView(const smtk::view::Information& info) - : qtBaseView(dynamic_cast(info)) - { - } + qtBaseView(const smtk::view::Information& info); ~qtBaseView() override; SMTK_DEPRECATED_IN_21_09("Method has been replaced by qtBaseView::configuration") - smtk::view::ConfigurationPtr getObject() const { return m_viewInfo.m_view; } - const smtk::view::ConfigurationPtr& configuration() const { return m_viewInfo.m_view; } + smtk::view::ConfigurationPtr getObject() const { return this->configuration(); } + const smtk::view::ConfigurationPtr& configuration() const + { + return m_viewInfo.get(); + } + QWidget* widget() const { return this->Widget; } - QWidget* parentWidget() const { return m_viewInfo.m_parent; } - qtUIManager* uiManager() const { return m_viewInfo.m_UIManager; } + + QWidget* parentWidget() const { return m_viewInfo.get(); } + + qtUIManager* uiManager() const { return m_viewInfo.get(); } virtual int advanceLevel() const { return 0; } virtual bool categoryEnabled() const { return false; } @@ -116,6 +87,9 @@ public: ///\brief Return true if the view's contents are valid. virtual bool isValid() const { return true; } + // Validates the view information to see if it is suitable for creating a qtBaseView instance + static bool validateInformation(const smtk::view::Information& info); + signals: void aboutToDestroy(); void modified(); @@ -153,7 +127,7 @@ protected: QWidget* Widget; bool m_isTopLevel; bool m_useSelectionManager; - ViewInfo m_viewInfo; + smtk::view::Information m_viewInfo; QPointer m_infoDialog; bool m_advOverlayVisible; }; // class diff --git a/smtk/extension/qt/qtCategorySelectorView.cxx b/smtk/extension/qt/qtCategorySelectorView.cxx index 61b2e50e02..5831ae501c 100644 --- a/smtk/extension/qt/qtCategorySelectorView.cxx +++ b/smtk/extension/qt/qtCategorySelectorView.cxx @@ -48,9 +48,13 @@ public: qtBaseView* qtCategorySelectorView::createViewWidget(const smtk::view::Information& info) { - qtCategorySelectorView* view = new qtCategorySelectorView(info); - view->buildUI(); - return view; + if (qtBaseAttributeView::validateInformation(info)) + { + auto* view = new qtCategorySelectorView(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } qtCategorySelectorView::qtCategorySelectorView(const smtk::view::Information& info) @@ -84,7 +88,7 @@ void qtCategorySelectorView::createWidget() bool qtCategorySelectorView::createChildren() { smtk::view::ConfigurationPtr view = this->configuration(); - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); int viewsIndex; viewsIndex = view->details().findChild("Views"); @@ -125,10 +129,10 @@ bool qtCategorySelectorView::createChildren() continue; } // Setup the information for the new child view based off of - // this one - smtk::extension::ViewInfo vinfo = m_viewInfo; - vinfo.m_view = v; - vinfo.m_parent = this->Widget; + // this one but with a different view configuration and (parent) widget + auto vinfo = m_viewInfo; + vinfo.insert_or_assign(v); + vinfo.insert_or_assign(this->Widget); qtView = this->uiManager()->createView(vinfo); if (qtView) { diff --git a/smtk/extension/qt/qtDiscreteValueEditor.cxx b/smtk/extension/qt/qtDiscreteValueEditor.cxx index b4df9d3b66..ceb7f3412e 100644 --- a/smtk/extension/qt/qtDiscreteValueEditor.cxx +++ b/smtk/extension/qt/qtDiscreteValueEditor.cxx @@ -86,12 +86,8 @@ qtDiscreteValueEditor::~qtDiscreteValueEditor() void qtDiscreteValueEditor::createWidget() { + smtk::attribute::ResourcePtr attResource = this->Internals->m_inputItem->attributeResource(); auto* uiManager = this->Internals->m_inputItem->uiManager(); - smtk::attribute::ResourcePtr attResource; - if (uiManager) - { - attResource = uiManager->attResource(); - } smtk::attribute::ValueItemPtr item = this->Internals->m_inputItem->itemAs(); if (!item) @@ -321,9 +317,11 @@ void qtDiscreteValueEditor::updateContents() { auto* uiManager = this->Internals->m_inputItem->uiManager(); if (uiManager == nullptr) + { return; + } - smtk::attribute::ResourcePtr attResource = uiManager->attResource(); + smtk::attribute::ResourcePtr attResource = this->Internals->m_inputItem->attributeResource(); this->Internals->clearChildItems(); @@ -373,14 +371,10 @@ void qtDiscreteValueEditor::updateContents() auto* iiview = this->Internals->m_inputItem->m_itemInfo.baseView(); int currentLen = iiview ? iiview->fixedLabelWidth() : 0; - if (this->Internals->m_inputItem->uiManager()) + int tmpLen = uiManager->getWidthOfItemsMaxLabel(activeChildDefs, uiManager->advancedFont()); + if (iiview) { - int tmpLen = this->Internals->m_inputItem->uiManager()->getWidthOfItemsMaxLabel( - activeChildDefs, this->Internals->m_inputItem->uiManager()->advancedFont()); - if (iiview) - { - iiview->setFixedLabelWidth(tmpLen); - } + iiview->setFixedLabelWidth(tmpLen); } bool hasVisibleChildren = false; for (i = 0; i < m; i++) @@ -407,7 +401,7 @@ void qtDiscreteValueEditor::updateContents() comp, this->Internals->m_childrenFrame.data(), this->Internals->m_inputItem->m_itemInfo.baseView()); - childItem = this->Internals->m_inputItem->uiManager()->createItem(info); + childItem = uiManager->createItem(info); } if (childItem) { diff --git a/smtk/extension/qt/qtGroupView.cxx b/smtk/extension/qt/qtGroupView.cxx index 5508e51784..078122f060 100644 --- a/smtk/extension/qt/qtGroupView.cxx +++ b/smtk/extension/qt/qtGroupView.cxx @@ -152,9 +152,13 @@ void qtGroupViewInternals::updateChildren(qtGroupView* gview, qtBaseViewMemFn mf qtBaseView* qtGroupView::createViewWidget(const smtk::view::Information& info) { - qtGroupView* view = new qtGroupView(info); - view->buildUI(); - return view; + if (qtBaseAttributeView::validateInformation(info)) + { + auto* view = new qtGroupView(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } qtGroupView::qtGroupView(const smtk::view::Information& info) @@ -280,7 +284,7 @@ void qtGroupView::createWidget() smtk::view::Configuration::Component& viewsComp = view->details().child(viewsIndex); std::size_t i, n = viewsComp.numberOfChildren(); smtk::view::ConfigurationPtr v; - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); qtBaseView* qtView; for (i = 0; i < n; i++) @@ -304,9 +308,10 @@ void qtGroupView::createWidget() } // Setup the information for the new child view based off of // this one - smtk::extension::ViewInfo vinfo = m_viewInfo; - vinfo.m_view = v; - vinfo.m_parent = this->Widget; + auto vinfo = m_viewInfo; + vinfo.insert_or_assign(v); + vinfo.insert_or_assign(this->Widget); + vinfo.insert_or_assign>(resource); qtView = this->uiManager()->createView(vinfo); if (qtView) { diff --git a/smtk/extension/qt/qtInstancedView.cxx b/smtk/extension/qt/qtInstancedView.cxx index d33693f493..c29f0669cb 100644 --- a/smtk/extension/qt/qtInstancedView.cxx +++ b/smtk/extension/qt/qtInstancedView.cxx @@ -53,20 +53,19 @@ public: smtk::operation::Observers::Key m_observerKey; }; -qtBaseView* qtInstancedView::createViewWidget( - const smtk::view::Information& info, - smtk::attribute::ResourcePtr overrideResource) +qtBaseView* qtInstancedView::createViewWidget(const smtk::view::Information& info) { - qtInstancedView* view = new qtInstancedView(info, overrideResource); - view->buildUI(); - return view; + if (qtBaseAttributeView::validateInformation(info)) + { + auto* view = new qtInstancedView(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } -qtInstancedView::qtInstancedView( - const smtk::view::Information& info, - smtk::attribute::ResourcePtr overrideResource) +qtInstancedView::qtInstancedView(const smtk::view::Information& info) : qtBaseAttributeView(info) - , m_overrideResource(overrideResource) { this->Internals = new qtInstancedViewInternals; } @@ -141,8 +140,7 @@ void qtInstancedView::updateUI() return; } - smtk::attribute::ResourcePtr resource = - (m_overrideResource ? m_overrideResource : this->uiManager()->attResource()); + smtk::attribute::ResourcePtr resource = this->attributeResource(); std::string attName, defName; smtk::attribute::AttributePtr att; smtk::attribute::DefinitionPtr attDef; diff --git a/smtk/extension/qt/qtInstancedView.h b/smtk/extension/qt/qtInstancedView.h index 97ec1b43c2..d14034b72a 100644 --- a/smtk/extension/qt/qtInstancedView.h +++ b/smtk/extension/qt/qtInstancedView.h @@ -38,13 +38,9 @@ public: ///\brief Create an instance view using an optionally specified attribute resource instead of the one /// associated with the UI Manager - static qtBaseView* createViewWidget( - const smtk::view::Information& info, - smtk::attribute::ResourcePtr overrideResource = nullptr); + static qtBaseView* createViewWidget(const smtk::view::Information& info); - qtInstancedView( - const smtk::view::Information& info, - smtk::attribute::ResourcePtr overrideResource = nullptr); + qtInstancedView(const smtk::view::Information& info); ~qtInstancedView() override; // Returns true if all attributes in the view are valid bool isValid() const override; @@ -71,7 +67,6 @@ protected: private: qtInstancedViewInternals* Internals; - smtk::attribute::ResourcePtr m_overrideResource; }; // class }; // namespace extension diff --git a/smtk/extension/qt/qtItem.cxx b/smtk/extension/qt/qtItem.cxx index fff67ed079..1866ab9bab 100644 --- a/smtk/extension/qt/qtItem.cxx +++ b/smtk/extension/qt/qtItem.cxx @@ -52,6 +52,11 @@ qtItem::~qtItem() delete this->Internals; } +smtk::attribute::ResourcePtr qtItem::attributeResource() const +{ + return m_itemInfo.baseView()->attributeResource(); +} + void qtItem::markForDeletion() { // Disconnect this object's signals @@ -135,7 +140,7 @@ void qtItem::showAdvanceLevelOverlay(bool show) int idx = std::distance(levels.begin(), it) - 1; this->Internals->AdvLevelCombo->setCurrentIndex(idx); } - const double* rgba = m_itemInfo.uiManager()->attResource()->advanceLevelColor(mylevel); + const double* rgba = this->attributeResource()->advanceLevelColor(mylevel); if (rgba) { this->Internals->advOverlay->overlay()->setColor( @@ -172,7 +177,7 @@ void qtItem::setLocalAdvanceLevel(unsigned int l) item->setLocalAdvanceLevel(1, l); if (this->Internals->advOverlay) { - const double* rgba = m_itemInfo.uiManager()->attResource()->advanceLevelColor(l); + const double* rgba = this->attributeResource()->advanceLevelColor(l); if (rgba) { this->Internals->advOverlay->overlay()->setColor( diff --git a/smtk/extension/qt/qtItem.h b/smtk/extension/qt/qtItem.h index a604612799..ec6b6bcb9c 100644 --- a/smtk/extension/qt/qtItem.h +++ b/smtk/extension/qt/qtItem.h @@ -68,6 +68,9 @@ public: qtUIManager* uiManager() const { return m_itemInfo.uiManager(); } + /// Return the underlying Attribute Resource + smtk::attribute::ResourcePtr attributeResource() const; + QPointer widget() { return m_widget; } QPointer parentWidget() { return m_itemInfo.parentWidget(); } diff --git a/smtk/extension/qt/qtModelEntityAttributeView.cxx b/smtk/extension/qt/qtModelEntityAttributeView.cxx index e6fea02d4a..7b92fd51a0 100644 --- a/smtk/extension/qt/qtModelEntityAttributeView.cxx +++ b/smtk/extension/qt/qtModelEntityAttributeView.cxx @@ -120,10 +120,8 @@ class qtModelEntityAttributeViewInternals public: ~qtModelEntityAttributeViewInternals() { delete this->CurrentAtt; } - const QList getCurrentDefs( - smtk::extension::qtUIManager* uiManager) const + const QList getCurrentDefs(const ResourcePtr& attResource) const { - auto attResource = uiManager->attResource(); if (!(attResource && attResource->activeCategoriesEnabled())) { // There are no active categories - return everything @@ -196,9 +194,13 @@ public: qtBaseView* qtModelEntityAttributeView::createViewWidget(const smtk::view::Information& info) { // TO DO Need to deal with Selections - qtModelEntityAttributeView* view = new qtModelEntityAttributeView(info); - view->buildUI(); - return view; + if (qtBaseAttributeView::validateInformation(info)) + { + auto* view = new qtModelEntityAttributeView(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } qtModelEntityAttributeView::qtModelEntityAttributeView(const smtk::view::Information& info) @@ -264,7 +266,7 @@ void qtModelEntityAttributeView::createWidget() } this->Internals->AttDefMap.clear(); - const ResourcePtr attResource = this->uiManager()->attResource(); + const ResourcePtr attResource = this->attributeResource(); std::set::const_iterator it; const std::set& cats = attResource->categories(); @@ -367,7 +369,7 @@ std::set qtModelEntityAttributeView::associ std::set result; // First we need to determine if the attribute resource has resources associated with it // if not we need to go to resource manager to get the information - auto attResource = this->uiManager()->attResource(); + auto attResource = this->attributeResource(); auto resources = attResource->associations(); if (!resources.empty()) { @@ -444,7 +446,7 @@ void qtModelEntityAttributeView::updateModelEntities() } QList currentDefs = - this->Internals->getCurrentDefs(this->uiManager()); + this->Internals->getCurrentDefs(this->attributeResource()); // Create an initial string list for the combo boxes QStringList slist; @@ -530,7 +532,7 @@ void qtModelEntityAttributeView::cellChanged(int row, int column) // Get selected type std::string tname = this->Internals->ListTable->item(row, 1)->text().toStdString(); - auto attRes = this->uiManager()->attResource(); + auto attRes = this->attributeResource(); auto resManager = this->uiManager()->resourceManager(); if (resManager == nullptr) { @@ -538,7 +540,7 @@ void qtModelEntityAttributeView::cellChanged(int row, int column) } QList currentDefs = - this->Internals->getCurrentDefs(this->uiManager()); + this->Internals->getCurrentDefs(this->attributeResource()); // Get the component of the item auto entity = this->object(this->Internals->ListTable->item(row, 0)); if (entity == nullptr) @@ -705,7 +707,7 @@ void qtModelEntityAttributeView::getAllDefinitions() return; } - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); std::string attName, defName, val; smtk::attribute::AttributePtr att; @@ -802,6 +804,6 @@ void qtModelEntityAttributeView::selectionMade() bool qtModelEntityAttributeView::isEmpty() const { QList currentDefs = - this->Internals->getCurrentDefs(this->uiManager()); + this->Internals->getCurrentDefs(this->attributeResource()); return currentDefs.isEmpty(); } diff --git a/smtk/extension/qt/qtOperationView.cxx b/smtk/extension/qt/qtOperationView.cxx index c87b979d6a..ed0bd84a11 100644 --- a/smtk/extension/qt/qtOperationView.cxx +++ b/smtk/extension/qt/qtOperationView.cxx @@ -42,7 +42,6 @@ public: , m_activeOperations(0) { } - smtk::operation::OperationPtr m_operator; std::unique_ptr m_instancedView; smtk::view::ConfigurationPtr m_instancedViewDef; QPointer m_applyButton; @@ -52,25 +51,28 @@ public: std::atomic m_activeOperations; }; +bool qtOperationView::validateInformation(const smtk::view::Information& info) +{ + return qtBaseAttributeView::validateInformation(info) && + info.contains(); +} + qtBaseView* qtOperationView::createViewWidget(const smtk::view::Information& info) { - const OperationViewInfo* opinfo = dynamic_cast(&info); - qtOperationView* view; - if (!opinfo) + if (qtOperationView::validateInformation(info)) { - return nullptr; + auto* view = new qtOperationView(info); + view->buildUI(); + return view; } - view = new qtOperationView(*opinfo); - view->buildUI(); - return view; + return nullptr; // Information is not suitable for this View } -qtOperationView::qtOperationView(const OperationViewInfo& info) +qtOperationView::qtOperationView(const smtk::view::Information& info) : qtBaseAttributeView(info) , m_applied(false) { this->Internals = new qtOperationViewInternals; - this->Internals->m_operator = info.m_operator; // We need to create a new View for the internal instanced View this->Internals->m_instancedViewDef = smtk::view::Configuration::New("Instanced", "Parameters"); smtk::view::ConfigurationPtr view = this->configuration(); @@ -90,7 +92,7 @@ qtOperationView::qtOperationView(const OperationViewInfo& info) view->details().setAttribute("FilterByCategory", "false"); } } - if (auto manager = this->Internals->m_operator->manager()) + if (auto manager = this->operation()->manager()) { auto* launcher = manager->launchers()[qtOperationLauncher::type_name].target(); if (launcher == nullptr) @@ -115,9 +117,9 @@ QPointer qtOperationView::applyButton() const return this->Internals->m_applyButton; } -smtk::operation::OperationPtr qtOperationView::operation() const +const smtk::operation::OperationPtr& qtOperationView::operation() const { - return this->Internals->m_operator; + return m_viewInfo.get(); } void qtOperationView::showInfoButton(bool visible) @@ -183,7 +185,12 @@ void qtOperationView::createWidget() QVBoxLayout* layout = new QVBoxLayout(this->Widget); layout->setMargin(0); this->Widget->setLayout(layout); - ViewInfo v(this->Internals->m_instancedViewDef, this->Widget, this->uiManager()); + + // Create the information to create an Instance View + smtk::view::Information v = m_viewInfo; + v.insert_or_assign(this->Internals->m_instancedViewDef); + v.insert_or_assign(this->Widget); + qtInstancedView* iview = dynamic_cast(qtInstancedView::createViewWidget(v)); this->Internals->m_instancedView.reset(iview); @@ -270,15 +277,16 @@ void qtOperationView::requestModelEntityAssociation() void qtOperationView::setInfoToBeDisplayed() { - m_infoDialog->displayInfo(this->Internals->m_operator->parameters()); + m_infoDialog->displayInfo(this->operation()->parameters()); } void qtOperationView::onOperate() { if ((!m_applied) && this->Internals->m_instancedView->isValid()) { + const auto& myOperation = this->operation(); shared_ptr handler = - (*this->Internals->m_launcher)(this->Internals->m_operator); + (*this->Internals->m_launcher)(myOperation); connect( handler.get(), @@ -286,7 +294,7 @@ void qtOperationView::onOperate() this, &qtOperationView::operationExecuted); - emit this->operationRequested(this->Internals->m_operator); + emit this->operationRequested(myOperation); if (this->Internals->m_applyButton) { // The button may disappear when a session is closed by an operator. this->Internals->m_applyButton->setEnabled(false); diff --git a/smtk/extension/qt/qtOperationView.h b/smtk/extension/qt/qtOperationView.h index 4c72f5c959..2903eb8446 100644 --- a/smtk/extension/qt/qtOperationView.h +++ b/smtk/extension/qt/qtOperationView.h @@ -30,30 +30,6 @@ namespace smtk { namespace extension { -class SMTKQTEXT_EXPORT OperationViewInfo : public ViewInfo -{ -public: - OperationViewInfo( - smtk::view::ConfigurationPtr view, - smtk::operation::OperationPtr targetOperation, - QWidget* parent, - qtUIManager* uiman) - : ViewInfo(view, parent, uiman) - , m_operator(targetOperation) - { - } - - // OperationViewInfo(smtk::view::ConfigurationPtr view, - // smtk::operation::OperationPtr targetOperation, QWidget* parent, qtUIManager* uiman, - // const std::map& layoutDict) - // : ViewInfo(view, parent, uiman, layoutDict) - // , m_operator(targetOperation) - // { - // } - - OperationViewInfo() = default; - smtk::operation::OperationPtr m_operator; -}; class SMTKQTEXT_EXPORT qtOperationView : public qtBaseAttributeView { @@ -64,16 +40,12 @@ public: static qtBaseView* createViewWidget(const smtk::view::Information& info); - qtOperationView(const smtk::view::Information& info) - : qtOperationView(static_cast(info)) - { - } + qtOperationView(const smtk::view::Information& info); - qtOperationView(const OperationViewInfo& info); ~qtOperationView() override; QPointer applyButton() const; - smtk::operation::OperationPtr operation() const; + const smtk::operation::OperationPtr& operation() const; void showInfoButton(bool visible = true); // Replaces default buttons, for embedding operation view in other widgets. @@ -83,6 +55,9 @@ public: QPointer infoButton, QPointer doneButton); + // Validates the view information to see if it is suitable for creating a qtOperationView instance + static bool validateInformation(const smtk::view::Information& info); + public slots: void updateUI() override; void showAdvanceLevelOverlay(bool show) override; diff --git a/smtk/extension/qt/qtReferenceItemEditor.cxx b/smtk/extension/qt/qtReferenceItemEditor.cxx index 1ba8b8e6fd..977aecc881 100644 --- a/smtk/extension/qt/qtReferenceItemEditor.cxx +++ b/smtk/extension/qt/qtReferenceItemEditor.cxx @@ -743,9 +743,11 @@ void qtReferenceItemEditor::updateContents() { auto* uiManager = this->uiManager(); if (uiManager == nullptr) + { return; + } - smtk::attribute::ResourcePtr attResource = uiManager->attResource(); + smtk::attribute::ResourcePtr attResource = this->attributeResource(); // First clear all of the current children items being displayed m_internals->clearChildItems(); diff --git a/smtk/extension/qt/qtResourceBrowser.cxx b/smtk/extension/qt/qtResourceBrowser.cxx index 3e47af0f4c..bd433bb2be 100644 --- a/smtk/extension/qt/qtResourceBrowser.cxx +++ b/smtk/extension/qt/qtResourceBrowser.cxx @@ -57,9 +57,13 @@ std::string qtResourceBrowser::s_configurationJSON = ResourcePanelConfiguration_ qtBaseView* qtResourceBrowser::createViewWidget(const smtk::view::Information& info) { - qtResourceBrowser* view = new qtResourceBrowser(info); - view->buildUI(); - return view; + if (qtBaseView::validateInformation(info)) + { + auto* view = new qtResourceBrowser(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } qtResourceBrowser::qtResourceBrowser(const smtk::view::Information& info) @@ -69,15 +73,16 @@ qtResourceBrowser::qtResourceBrowser(const smtk::view::Information& info) smtk::view::PhraseModelPtr phraseModel; std::string modelViewType; QAbstractItemModel* qtPhraseModel = nullptr; - if (m_viewInfo.m_view) + const auto& view = this->getObject(); + if (view) { // empty Widget attribute is OK, will use default. - m_viewInfo.m_view->details().attribute("Widget", modelViewType); - smtk::view::ManagerPtr manager = m_viewInfo.m_UIManager->viewManager(); - phraseModel = manager->phraseModelFactory().createFromConfiguration(m_viewInfo.configuration()); + view->details().attribute("Widget", modelViewType); + smtk::view::ManagerPtr manager = this->uiManager()->viewManager(); + phraseModel = manager->phraseModelFactory().createFromConfiguration(view.get()); qtPhraseModel = new smtk::extension::qtDescriptivePhraseModel; } - m_p->setup(this, phraseModel, modelViewType, qtPhraseModel, m_viewInfo.m_parent); + m_p->setup(this, phraseModel, modelViewType, qtPhraseModel, this->parentWidget()); this->Widget = m_p->m_container; } diff --git a/smtk/extension/qt/qtResourceBrowser.h b/smtk/extension/qt/qtResourceBrowser.h index 4aeeb493a9..64b201d412 100644 --- a/smtk/extension/qt/qtResourceBrowser.h +++ b/smtk/extension/qt/qtResourceBrowser.h @@ -35,7 +35,7 @@ class qtDescriptivePhraseModel; * This contains Qt widget that displays a tree or list view holding an SMTK * descriptive phrase model. * - * Its ViewInfo should be initialized with json/xml that contains: + * Its Information should be initialized with json/xml that contains: * (1) an smtk::view::PhraseModel that you have configured, * (2) the string name registered to a QAbstractItemView subclass constructor, * (3) a QAbstactItemModel implementing qtDescriptivePhraseModel model index queries, and diff --git a/smtk/extension/qt/qtSMTKUtilities.h b/smtk/extension/qt/qtSMTKUtilities.h index 01ea4c6ba0..a57298a885 100644 --- a/smtk/extension/qt/qtSMTKUtilities.h +++ b/smtk/extension/qt/qtSMTKUtilities.h @@ -11,8 +11,7 @@ #define __smtk_attribute_qtSMTKUtilities_h #include "smtk/extension/qt/Exports.h" -#include "smtk/extension/qt/qtUIManager.h" // for qtItemConstructor definition -#include "smtk/extension/qt/qtViewInterface.h" // for qtSMTKViewConstructor definition +#include "smtk/extension/qt/qtUIManager.h" // for qtItemConstructor definition #include diff --git a/smtk/extension/qt/qtSelectorView.cxx b/smtk/extension/qt/qtSelectorView.cxx index 2e4cbf265f..58f8bce7c6 100644 --- a/smtk/extension/qt/qtSelectorView.cxx +++ b/smtk/extension/qt/qtSelectorView.cxx @@ -52,9 +52,13 @@ public: qtBaseView* qtSelectorView::createViewWidget(const smtk::view::Information& info) { - qtSelectorView* view = new qtSelectorView(info); - view->buildUI(); - return view; + if (qtBaseAttributeView::validateInformation(info)) + { + auto* view = new qtSelectorView(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } qtSelectorView::qtSelectorView(const smtk::view::Information& info) @@ -100,7 +104,7 @@ bool qtSelectorView::createSelector() this->Widget->setLayout(layout); // Get the Selector Attribute - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); std::string attName, defName; view->details().attribute("SelectorName", attName); view->details().attribute("SelectorType", defName); @@ -173,7 +177,7 @@ bool qtSelectorView::isEmpty() const bool qtSelectorView::createChildren() { smtk::view::ConfigurationPtr view = this->configuration(); - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); // We need the selector item's definition in order to get the enumeration info auto selItemDef = @@ -225,9 +229,10 @@ bool qtSelectorView::createChildren() } // Setup the information for the new child view based off of // this one - smtk::extension::ViewInfo vinfo = m_viewInfo; - vinfo.m_view = v; - vinfo.m_parent = this->Widget; + auto vinfo = m_viewInfo; + vinfo.insert_or_assign(v); + vinfo.insert_or_assign(this->Widget); + qtView = this->uiManager()->createView(vinfo); if (qtView) { diff --git a/smtk/extension/qt/qtSimpleExpressionView.cxx b/smtk/extension/qt/qtSimpleExpressionView.cxx index 3ece5da647..2f7dc902bc 100644 --- a/smtk/extension/qt/qtSimpleExpressionView.cxx +++ b/smtk/extension/qt/qtSimpleExpressionView.cxx @@ -81,9 +81,13 @@ const char* qtSimpleExpressionView::qtSimpleExpressionViewInternals::getFunction qtBaseView* qtSimpleExpressionView::createViewWidget(const smtk::view::Information& info) { - qtSimpleExpressionView* view = new qtSimpleExpressionView(info); - view->buildUI(); - return view; + if (qtBaseAttributeView::validateInformation(info)) + { + auto* view = new qtSimpleExpressionView(info); + view->buildUI(); + return view; + } + return nullptr; // Information is not suitable for this View } qtSimpleExpressionView::qtSimpleExpressionView(const smtk::view::Information& info) @@ -605,7 +609,7 @@ void qtSimpleExpressionView::onDeleteSelected() return; } - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); resource->removeAttribute(this->getFunctionFromItem(selItem)); this->Internals->FuncList->takeItem(this->Internals->FuncList->row(selItem)); @@ -779,7 +783,7 @@ void qtSimpleExpressionView::updateUI() { return; } - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); // There should be only 1 child component called Type if ((view->details().numberOfChildren() != 1) || (view->details().child(0).name() != "Att")) { diff --git a/smtk/extension/qt/qtUIManager.cxx b/smtk/extension/qt/qtUIManager.cxx index f4cb7a524f..117a8bec0d 100644 --- a/smtk/extension/qt/qtUIManager.cxx +++ b/smtk/extension/qt/qtUIManager.cxx @@ -13,6 +13,7 @@ #include "smtk/extension/qt/qtAnalysisView.h" #include "smtk/extension/qt/qtAssociationView.h" #include "smtk/extension/qt/qtAttributeView.h" +#include "smtk/extension/qt/qtBaseView.h" #include "smtk/extension/qt/qtCategorySelectorView.h" #include "smtk/extension/qt/qtComponentItem.h" #include "smtk/extension/qt/qtDateTimeItem.h" @@ -220,16 +221,22 @@ void qtUIManager::initializeUI(QWidget* pWidget, bool useInternalFileBrowser) } this->internalInitialize(); + smtk::view::Information vinfo; + vinfo.insert(m_smtkView); + vinfo.insert(pWidget); + vinfo.insert(this); if (!m_operation) { - smtk::extension::ViewInfo vinfo(m_smtkView, pWidget, this); - m_topView = this->createView(vinfo); + vinfo.insert>(m_attResource); } else { - smtk::extension::OperationViewInfo vinfo(m_smtkView, m_operation, pWidget, this); - m_topView = this->createView(vinfo); + vinfo.insert(m_operation); + vinfo.insert>(m_operation->specification()); } + + m_topView = this->createView(vinfo); + if (m_topView) { if (m_topView->configuration()->details().attribute(m_activeAdvLevelXmlAttName)) @@ -252,12 +259,11 @@ void qtUIManager::initializeUI(QWidget* pWidget, bool useInternalFileBrowser) } } -void qtUIManager::initializeUI( - const smtk::extension::ViewInfo& viewInfo, - bool useInternalFileBrowser) +void qtUIManager::initializeUI(const smtk::view::Information& viewInfo, bool useInternalFileBrowser) { m_useInternalFileBrowser = useInternalFileBrowser; - m_parentWidget = viewInfo.m_parent; + m_parentWidget = viewInfo.get(); + m_smtkView = viewInfo.get(); if (m_topView) { delete m_topView; @@ -373,21 +379,21 @@ smtk::view::ConfigurationPtr qtUIManager::findOrCreateOperationView() const } qtBaseView* qtUIManager::setSMTKView( - const smtk::extension::ViewInfo& viewInfo, + const smtk::view::Information& viewInfo, bool useInternalFileBrowser) { if ( - (m_smtkView == viewInfo.m_view) && (m_parentWidget == viewInfo.m_parent) && + (m_smtkView == viewInfo.get()) && + (m_parentWidget == viewInfo.get()) && (m_useInternalFileBrowser == useInternalFileBrowser)) { return m_topView; } - m_smtkView = viewInfo.m_view; this->initializeUI(viewInfo, m_useInternalFileBrowser); return m_topView; } -qtBaseView* qtUIManager::setSMTKView(smtk::view::ConfigurationPtr v) +qtBaseView* qtUIManager::setSMTKView(const smtk::view::ConfigurationPtr& v) { if (m_smtkView != v) { @@ -398,7 +404,7 @@ qtBaseView* qtUIManager::setSMTKView(smtk::view::ConfigurationPtr v) } qtBaseView* qtUIManager::setSMTKView( - smtk::view::ConfigurationPtr v, + const smtk::view::ConfigurationPtr& v, QWidget* pWidget, bool useInternalFileBrowser) { @@ -889,39 +895,40 @@ void qtUIManager::registerItemConstructor(const std::string& itype, qtItemConstr m_itemConstructors[itype] = f; } -qtBaseView* qtUIManager::createView(const ViewInfo& info) +qtBaseView* qtUIManager::createView(const smtk::view::Information& info) { - if (info.m_UIManager != this) + if (info.get() != this) { - // The view being constructed is not refering to this manager! + // The view being constructed is not referring to this manager! return nullptr; } auto& viewManager = m_managers.get(); + std::string viewType = info.get()->type(); if (!viewManager) { - std::cerr << "No viewManager for View Type: " << info.m_view->type() << " skipping view!\n"; + std::cerr << "No viewManager for View Type: " << viewType << " skipping view!\n"; return nullptr; } qtBaseView* qtView = nullptr; - if (viewManager->viewWidgetFactory().contains(info.m_view->type())) + if (viewManager->viewWidgetFactory().contains(viewType)) { qtView = dynamic_cast( viewManager->viewWidgetFactory() - .createFromName(info.m_view->type(), static_cast(info)) + .createFromName(viewType, static_cast(info)) .release()); } - else if (viewManager->viewWidgetFactory().containsAlias(info.m_view->type())) + else if (viewManager->viewWidgetFactory().containsAlias(viewType)) { qtView = dynamic_cast( viewManager->viewWidgetFactory() - .createFromAlias(info.m_view->type(), static_cast(info)) + .createFromAlias(viewType, static_cast(info)) .release()); } if (!qtView) { // Constructor for that type could not be found) - std::cerr << "Could not find View Type: " << info.m_view->type() << " skipping view!\n"; + std::cerr << "Could not find View Type: " << viewType << " skipping view!\n"; } else { diff --git a/smtk/extension/qt/qtUIManager.h b/smtk/extension/qt/qtUIManager.h index 137d7098b0..861003e8e6 100644 --- a/smtk/extension/qt/qtUIManager.h +++ b/smtk/extension/qt/qtUIManager.h @@ -13,6 +13,7 @@ #include "smtk/attribute/Categories.h" #include "smtk/attribute/Resource.h" +#include "smtk/common/Deprecation.h" #include "smtk/common/TypeContainer.h" #include "smtk/operation/Manager.h" @@ -22,7 +23,6 @@ #include "smtk/view/Selection.h" #include "smtk/extension/qt/Exports.h" -#include "smtk/extension/qt/qtBaseView.h" // Needed for ViewInfo definition #include "smtk/extension/qt/qtItem.h" #include @@ -47,7 +47,6 @@ class qtFileItem; class qtModelEntityItem; class qtBaseView; -typedef qtBaseView* (*widgetConstructor)(const ViewInfo& info); typedef qtItem* (*qtItemConstructor)(const qtAttributeItemInfo& info); /**\brief Container for managers whose content is presented via Qt widgets. @@ -79,7 +78,7 @@ public: ~qtUIManager() override; void initializeUI(QWidget* pWidget, bool useInternalFileBrowser = false); - void initializeUI(const smtk::extension::ViewInfo& v, bool useInternalFileBrowser = false); + void initializeUI(const smtk::view::Information& v, bool useInternalFileBrowser = false); /// If this instance was constructed with an operation, return an appropriate view for it. smtk::view::ConfigurationPtr findOrCreateOperationView() const; @@ -89,10 +88,12 @@ public: ///@{ /// Use the given smtk::view::Configuration to construct widgets matching the specification. - qtBaseView* setSMTKView(smtk::view::ConfigurationPtr v); - qtBaseView* - setSMTKView(smtk::view::ConfigurationPtr v, QWidget* pWidget, bool useInternalFileBrowser = true); - qtBaseView* setSMTKView(const smtk::extension::ViewInfo& v, bool useInternalFileBrowser = true); + qtBaseView* setSMTKView(const smtk::view::ConfigurationPtr& v); + qtBaseView* setSMTKView( + const smtk::view::ConfigurationPtr& v, + QWidget* pWidget, + bool useInternalFileBrowser = true); + qtBaseView* setSMTKView(const smtk::view::Information& v, bool useInternalFileBrowser = true); smtk::view::ConfigurationPtr smtkView() const { return m_smtkView; } const smtk::view::Configuration::Component& findStyle( const smtk::attribute::DefinitionPtr& def, @@ -129,8 +130,9 @@ public: const smtk::common::TypeContainer& managers() const { return m_managers; } smtk::common::TypeContainer& managers() { return m_managers; } + SMTK_DEPRECATED_IN_21_08("Since the attribute resource is now passed into qtBaseAttributeViews, " + "there is no longer a need to access the resource from the qtUIManager") smtk::attribute::ResourcePtr attResource() const { return m_attResource.lock(); } - ///@{ /// Set/Get the color used for indicating items with default values void setDefaultValueColor(const QColor& color); @@ -237,7 +239,7 @@ public: virtual int getWidthOfText(const std::string& text, const QFont& font); ///Mechanism for creating new GUI view based on registered factory functions - qtBaseView* createView(const ViewInfo& info); + qtBaseView* createView(const smtk::view::Information& info); ///Mechanism for creating new GUI item based on registered factory functions qtItem* createItem(const qtAttributeItemInfo& info); diff --git a/smtk/extension/qt/qtViewInterface.cxx b/smtk/extension/qt/qtViewInterface.cxx deleted file mode 100644 index 0c9d6365aa..0000000000 --- a/smtk/extension/qt/qtViewInterface.cxx +++ /dev/null @@ -1,16 +0,0 @@ -//========================================================================= -// Copyright (c) Kitware, Inc. -// All rights reserved. -// See LICENSE.txt for details. -// -// This software is distributed WITHOUT ANY WARRANTY; without even -// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -// PURPOSE. See the above copyright notice for more information. -//========================================================================= -#include "smtk/extension/qt/qtViewInterface.h" - -using namespace smtk::extension; - -qtViewInterface::qtViewInterface() = default; - -qtViewInterface::~qtViewInterface() = default; diff --git a/smtk/extension/qt/qtViewInterface.h b/smtk/extension/qt/qtViewInterface.h deleted file mode 100644 index b4892d4e17..0000000000 --- a/smtk/extension/qt/qtViewInterface.h +++ /dev/null @@ -1,49 +0,0 @@ -//========================================================================= -// Copyright (c) Kitware, Inc. -// All rights reserved. -// See LICENSE.txt for details. -// -// This software is distributed WITHOUT ANY WARRANTY; without even -// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -// PURPOSE. See the above copyright notice for more information. -//========================================================================= - -#ifndef __smtk_attribute_qtViewInterface_h -#define __smtk_attribute_qtViewInterface_h - -#include "smtk/extension/qt/Exports.h" -#include "smtk/extension/qt/qtBaseView.h" - -#include -#include - -typedef smtk::extension::qtBaseView* (*qtSMTKViewConstructor)( - const smtk::extension::ViewInfo& info); - -namespace smtk -{ -namespace extension -{ - -/// interface class for plugins that add a QDockWindow -class SMTKQTEXT_EXPORT qtViewInterface -{ -public: - qtViewInterface(); - virtual ~qtViewInterface(); - - virtual QString viewName() const = 0; - - /// return a static constructor for derived class of qtBaseView - virtual qtSMTKViewConstructor viewConstructor() = 0; - -private: - Q_DISABLE_COPY(qtViewInterface) -}; - -}; // namespace extension -}; // namespace smtk - -Q_DECLARE_INTERFACE(smtk::extension::qtViewInterface, "com.kitware/paraview/smtkview") - -#endif diff --git a/smtk/project/plugin/pqSMTKProjectPanel.cxx b/smtk/project/plugin/pqSMTKProjectPanel.cxx index c9203d80a1..08b13b41b1 100644 --- a/smtk/project/plugin/pqSMTKProjectPanel.cxx +++ b/smtk/project/plugin/pqSMTKProjectPanel.cxx @@ -88,7 +88,10 @@ void pqSMTKProjectPanel::sourceAdded(pqSMTKWrapper* wrapper, pqServer* server) m_viewUIMgr->setSelection(wrapper->smtkSelection()); // m_viewUIMgr->setSelectionBit(1); // ToDo: should be set ? - smtk::extension::ViewInfo resinfo(m_view, this, m_viewUIMgr); + smtk::view::Information resinfo; + resinfo.insert(m_view); + resinfo.insert(this); + resinfo.insert(m_viewUIMgr); // the top-level "Type" in m_view should be pqSMTKProjectBrowser or compatible. auto* baseview = m_viewUIMgr->setSMTKView(resinfo); diff --git a/smtk/view/Information.h b/smtk/view/Information.h index 3a0b142803..3d82d68c99 100644 --- a/smtk/view/Information.h +++ b/smtk/view/Information.h @@ -11,28 +11,45 @@ #ifndef smtk_view_Information_h #define smtk_view_Information_h +#include "smtk/common/TypeContainer.h" + #include "smtk/CoreExports.h" +#include "smtk/SharedFromThis.h" +#include "smtk/view/Configuration.h" namespace smtk { namespace view { -class Configuration; -/**\brief A base class for information passed to views during initialization. +/**\brief A class for information passed to views during initialization. * * View information must include configuration information, but usually * also includes information specific to the GUI system of the view - * being constructed. Hence, this class is usually dynamically cast to - * a type appropriate to the view. + * being constructed. Hence, this class is based off of TypeContainer so it + * can hold arbitrary information. */ class SMTKCORE_EXPORT Information + : public smtk::common::TypeContainer + , public std::enable_shared_from_this { public: - virtual ~Information() = 0; + typedef TypeContainer Container; + + smtkTypeMacroBase(smtk::view::Information); + smtkCreateMacro(Information); + + Information() = default; + ~Information() override; - virtual const Configuration* configuration() const = 0; + virtual const Configuration* configuration() const + { + return this->get().get(); + } + +protected: }; + } // namespace view } // namespace smtk -- GitLab From 77743f9544a2fa2c8fc3d7bc7716a3c1273c6f46 Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Tue, 24 Aug 2021 20:11:10 -0400 Subject: [PATCH 37/52] ENH: Added the concept of markedForRemoval to Resources Resources can now be markedForRemoval indicating that the resource will be removed from memory (as apposed to deletion which also means it is being deleted from storage as well). This can be used in the UI to determine if a View needs to worry about keeping its contents up to date if the reason it is using is going to be removed. This also gets around a current issue with the Resource Links system which will cause a resource to be pulled back into memory even though the resources that its associated with is going to be removed. Another benefit is to potentially optimize the removal of components when its resource is targeted for removal. --- doc/release/notes/supportingMarkedForRemoval.rst | 15 +++++++++++++++ smtk/extension/qt/qtAssociation2ColumnWidget.cxx | 7 +++++++ smtk/extension/qt/qtAttributeView.cxx | 5 +++-- smtk/extension/qt/qtInstancedView.cxx | 6 +++++- smtk/extension/qt/qtReferenceItemEditor.cxx | 8 ++++++++ smtk/resource/PersistentObject.h | 2 +- smtk/resource/Resource.h | 7 +++++++ 7 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 doc/release/notes/supportingMarkedForRemoval.rst diff --git a/doc/release/notes/supportingMarkedForRemoval.rst b/doc/release/notes/supportingMarkedForRemoval.rst new file mode 100644 index 0000000000..ab794ff0f5 --- /dev/null +++ b/doc/release/notes/supportingMarkedForRemoval.rst @@ -0,0 +1,15 @@ +Supporting MarkedForRemoval +-------------------------- + +Resources can now be markedForRemoval indicating that the resource will be removed from memory (as apposed to deletion which also means it is being deleted from storage as well). This can be used in the UI to determine if a View needs to worry about keeping its contents up to date if the reason it is using is going to be removed. This also gets around a current issue with the Resource Links system which will cause a resource to be pulled back into memory even though the resources that its associated with is going to be removed. + +Another benefit is to potentially optimize the removal of components when its resource is targeted for removal. + +Developer changes +~~~~~~~~~~~~~~~~~~ + +* Added the following API to smtk::resource::Resource + * setMarkedForRemoval - sets the resource's marked for removal state + * isMarkedForRemoval - returns the resource's marked for removal state +* UI class changes + * All Attribute Related Qt classes that handle operations will now check to see if the resource is marked for removal. diff --git a/smtk/extension/qt/qtAssociation2ColumnWidget.cxx b/smtk/extension/qt/qtAssociation2ColumnWidget.cxx index 7adedea172..1fb01e193e 100644 --- a/smtk/extension/qt/qtAssociation2ColumnWidget.cxx +++ b/smtk/extension/qt/qtAssociation2ColumnWidget.cxx @@ -355,6 +355,13 @@ void qtAssociation2ColumnWidget::refreshAssociations(const smtk::common::UUID& i } ResourcePtr attResource = attDef->resource(); + // If this resource is marked for removal there is nothing to be done + if (attResource->isMarkedForRemoval()) + { + m_internals->CurrentList->blockSignals(false); + m_internals->AvailableList->blockSignals(false); + return; + } auto resManager = m_view->uiManager()->resourceManager(); // Lets get the objects that can possibly be associated with the attribute/definition if (theAttribute) diff --git a/smtk/extension/qt/qtAttributeView.cxx b/smtk/extension/qt/qtAttributeView.cxx index b774b2b11e..aa81937078 100644 --- a/smtk/extension/qt/qtAttributeView.cxx +++ b/smtk/extension/qt/qtAttributeView.cxx @@ -1509,9 +1509,10 @@ int qtAttributeView::handleOperationEvent( std::size_t i, n; smtk::attribute::DefinitionPtr currentDef = this->getCurrentDef(); - if (currentDef == nullptr) + // If there is no definition or it's attribute resource is mark for removal + // then we don't need to update anything + if ((currentDef == nullptr) || currentDef->resource()->isMarkedForRemoval()) { - // There is nothing being displayed so nothing needs to be updated return 0; } diff --git a/smtk/extension/qt/qtInstancedView.cxx b/smtk/extension/qt/qtInstancedView.cxx index c29f0669cb..06a4e40a0b 100644 --- a/smtk/extension/qt/qtInstancedView.cxx +++ b/smtk/extension/qt/qtInstancedView.cxx @@ -291,7 +291,11 @@ int qtInstancedView::handleOperationEvent( smtk::operation::EventType event, smtk::operation::Operation::Result result) { - if (event != smtk::operation::EventType::DID_OPERATE) + // If the operation did not execute or if the view's + // attribute resource is marked for removal, just return + if ( + (event != smtk::operation::EventType::DID_OPERATE) || + this->attributeResource()->isMarkedForRemoval()) { return 0; } diff --git a/smtk/extension/qt/qtReferenceItemEditor.cxx b/smtk/extension/qt/qtReferenceItemEditor.cxx index 977aecc881..b33cd1e1e0 100644 --- a/smtk/extension/qt/qtReferenceItemEditor.cxx +++ b/smtk/extension/qt/qtReferenceItemEditor.cxx @@ -665,6 +665,14 @@ void qtReferenceItemEditor::handleResourceEvent( auto theAttribute = item->attribute(); auto attResource = theAttribute->attributeResource(); + // If this resource is marked for removal, then we don't need to update this widget since + // it should be deleted shortly - this will also prevent the resource's links system from + // pulling in associated resources unnecessarily + if (attResource->isMarkedForRemoval()) + { + return; + } + if ((event == smtk::resource::EventType::REMOVED) && (attResource->id() != resource.id())) { // The simplest solution is just to refresh the widget diff --git a/smtk/resource/PersistentObject.h b/smtk/resource/PersistentObject.h index bd368eee73..2fd5be8c66 100644 --- a/smtk/resource/PersistentObject.h +++ b/smtk/resource/PersistentObject.h @@ -49,7 +49,7 @@ public: /// connect to this object (see Resource::setId and its treatment of /// manager registration for reference). virtual bool setId(const common::UUID& myID) = 0; - /// Return the name of the object - by default it will return the UUID but that can be overriden + /// Return the name of the object - by default it will return the UUID but that can be overridden virtual std::string name() const; /// Attempt to cast this object to a subclass. diff --git a/smtk/resource/Resource.h b/smtk/resource/Resource.h index f8e2359e29..7b39a65001 100644 --- a/smtk/resource/Resource.h +++ b/smtk/resource/Resource.h @@ -126,6 +126,12 @@ public: virtual bool clean() const { return m_clean; } void setClean(bool state = true); + /// Mark the resource to indicate it is about to removed (meaning it is being removed from memory + /// not necessarily for deletion) + void setMarkedForRemoval(bool val) { m_markedForRemoval = val; } + + /// Return whether the object is marked for removal + virtual bool isMarkedForRemoval() const { return m_markedForRemoval; } /// Resources that are managed have a non-null pointer to their manager. ManagerPtr manager() const { return m_manager.lock(); } @@ -223,6 +229,7 @@ private: Links m_links; Properties m_properties; Queries m_queries; + bool m_markedForRemoval = false; mutable Lock m_lock; }; -- GitLab From 15ff06720971a6b1b3bb587779e471bc9f387f62 Mon Sep 17 00:00:00 2001 From: Ryan Krattiger Date: Wed, 14 Jul 2021 15:48:52 -0500 Subject: [PATCH 38/52] CI: Add XMS Mesher to fedora33-paraview container --- .gitlab/ci/docker/fedora33-paraview/install_superbuild.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/ci/docker/fedora33-paraview/install_superbuild.sh b/.gitlab/ci/docker/fedora33-paraview/install_superbuild.sh index bd3df49bcd..96f9a77836 100755 --- a/.gitlab/ci/docker/fedora33-paraview/install_superbuild.sh +++ b/.gitlab/ci/docker/fedora33-paraview/install_superbuild.sh @@ -27,12 +27,12 @@ cmake -GNinja \ -DDEVELOPER_MODE_smtk:BOOL=ON \ -DENABLE_cmb:BOOL=OFF \ -DENABLE_cmbusersguide:BOOL=OFF \ - -DENABLE_smtkprojectmanager:BOOL=OFF \ -DENABLE_smtkresourcemanagerstate:BOOL=OFF \ -DENABLE_paraview:BOOL=ON \ -DENABLE_python3:BOOL=ON \ -DSUPPRESS_szip_OUTPUT:BOOL=OFF \ -DUSE_SYSTEM_qt5:BOOL=ON \ + -DENABLE_xmsmesher:BOOL=ON \ $sccache_settings \ "-D__BUILDBOT_INSTALL_LOCATION:PATH=$SUPERBUILD_PREFIX" \ "$workdir" -- GitLab From c2b43234535fa3bd7960cb4d50ca9f98190b448b Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 19 Aug 2021 17:39:31 -0400 Subject: [PATCH 39/52] Fix warning (reorder member initializers). --- smtk/attribute/Item.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smtk/attribute/Item.cxx b/smtk/attribute/Item.cxx index f690a35cd1..c97e427497 100644 --- a/smtk/attribute/Item.cxx +++ b/smtk/attribute/Item.cxx @@ -23,8 +23,8 @@ Item::Item(Attribute* owningAttribute, int itemPosition) , m_owningItem(nullptr) , m_position(itemPosition) , m_isEnabled(true) - , m_forceRequired(false) , m_isIgnored(false) + , m_forceRequired(false) { m_hasLocalAdvanceLevelInfo[0] = false; m_hasLocalAdvanceLevelInfo[1] = false; @@ -37,8 +37,8 @@ Item::Item(Item* inOwningItem, int itemPosition, int inSubGroupPosition) , m_position(itemPosition) , m_subGroupPosition(inSubGroupPosition) , m_isEnabled(true) - , m_forceRequired(false) , m_isIgnored(false) + , m_forceRequired(false) { m_hasLocalAdvanceLevelInfo[0] = false; m_hasLocalAdvanceLevelInfo[1] = false; -- GitLab From ee78cfd82aaa9bf36b38db21588f297094d9d03d Mon Sep 17 00:00:00 2001 From: David Thompson Date: Tue, 17 Aug 2021 11:25:38 -0400 Subject: [PATCH 40/52] Add a task::Group (plus many changes to Task). --- doc/CMakeLists.txt | 7 +- doc/userguide/figures/task-terminology.png | 3 + doc/userguide/figures/task-terminology.svg | 570 +++++++++++++++++++ doc/userguide/index.rst | 3 +- doc/userguide/task/adaptors.rst | 30 +- doc/userguide/task/classes.rst | 184 +++--- doc/userguide/task/concepts.rst | 47 ++ doc/userguide/task/index.rst | 19 +- doc/userguide/workflow/concepts.rst | 15 - doc/userguide/workflow/index.rst | 22 - smtk/task/Adaptor.cxx | 5 +- smtk/task/Adaptor.h | 10 +- smtk/task/CMakeLists.txt | 1 + smtk/task/ConfigureOperation.cxx | 296 ++++++++++ smtk/task/ConfigureOperation.h | 119 ++++ smtk/task/FillOutAttributes.cxx | 69 +++ smtk/task/FillOutAttributes.h | 6 + smtk/task/GatherResources.cxx | 126 ++-- smtk/task/GatherResources.h | 17 + smtk/task/Group.cxx | 151 ++++- smtk/task/Group.h | 18 + smtk/task/Instances.cxx | 74 +++ smtk/task/Instances.h | 71 ++- smtk/task/Task.cxx | 146 ++++- smtk/task/Task.h | 84 ++- smtk/task/adaptor/Instances.h | 4 +- smtk/task/adaptor/ResourceAndRole.cxx | 178 ++++-- smtk/task/adaptor/ResourceAndRole.h | 8 +- smtk/task/json/Configurator.h | 34 +- smtk/task/json/Configurator.txx | 44 +- smtk/task/json/Helper.cxx | 61 +- smtk/task/json/Helper.h | 54 +- smtk/task/json/jsonAdaptor.cxx | 4 +- smtk/task/json/jsonFillOutAttributes.cxx | 3 +- smtk/task/json/jsonGatherResources.cxx | 137 ++++- smtk/task/json/jsonGatherResources.h | 6 +- smtk/task/json/jsonGroup.cxx | 22 +- smtk/task/json/jsonManager.cxx | 82 ++- smtk/task/json/jsonManager.h | 9 + smtk/task/json/jsonTask.cxx | 4 + smtk/task/pybind11/PybindFillOutAttributes.h | 4 +- smtk/task/testing/cxx/CMakeLists.txt | 1 + smtk/task/testing/cxx/TestActiveTask.cxx | 1 + smtk/task/testing/cxx/TestTaskBasics.cxx | 22 + smtk/task/testing/cxx/TestTaskGroup.cxx | 240 +++++--- smtk/task/testing/cxx/TestTaskJSON.cxx | 56 +- 46 files changed, 2618 insertions(+), 449 deletions(-) create mode 100644 doc/userguide/figures/task-terminology.png create mode 100644 doc/userguide/figures/task-terminology.svg delete mode 100644 doc/userguide/workflow/concepts.rst delete mode 100644 doc/userguide/workflow/index.rst create mode 100644 smtk/task/ConfigureOperation.cxx create mode 100644 smtk/task/ConfigureOperation.h create mode 100644 smtk/task/Instances.cxx diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 3cab56ba9a..4ce8765629 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -119,8 +119,11 @@ if (SPHINX_FOUND) userguide/obtain-build-install.rst userguide/simulation/index.rst userguide/overview.rst - userguide/workflow/index.rst - userguide/workflow/concepts.rst + userguide/task/adaptors.rst + userguide/task/concepts.rst + userguide/task/classes.rst + userguide/task/index.rst + userguide/task/io.rst userguide/model/index.rst userguide/model/property-names.rst userguide/model/sessions.rst diff --git a/doc/userguide/figures/task-terminology.png b/doc/userguide/figures/task-terminology.png new file mode 100644 index 0000000000..8e6215ee3c --- /dev/null +++ b/doc/userguide/figures/task-terminology.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b58b1f8705ff125a2f97bec04250687633da5e8f0a8f40039554a85c9eb5a4bc +size 107702 diff --git a/doc/userguide/figures/task-terminology.svg b/doc/userguide/figures/task-terminology.svg new file mode 100644 index 0000000000..857f6a0530 --- /dev/null +++ b/doc/userguide/figures/task-terminology.svg @@ -0,0 +1,570 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + Task + dependencies + dependents + + Task1 + Task M + + + Task 1 + Task N + + Head Task + Workflow + + + + + + + ... + ... + ... + + + Parent Task + + + + + children + Task 1 + Task N + ... + + diff --git a/doc/userguide/index.rst b/doc/userguide/index.rst index bcd785c261..f8e411fc98 100644 --- a/doc/userguide/index.rst +++ b/doc/userguide/index.rst @@ -44,11 +44,10 @@ simulation domain. graph/index.rst mesh/index.rst project/index.rst + task/index.rst simulation/index.rst view/index.rst extension/index.rst - task/index.rst - workflow/index.rst bindings/index.rst plugin/index.rst administration.rst diff --git a/doc/userguide/task/adaptors.rst b/doc/userguide/task/adaptors.rst index 7da005b452..313c936ebb 100644 --- a/doc/userguide/task/adaptors.rst +++ b/doc/userguide/task/adaptors.rst @@ -42,12 +42,31 @@ ResourceAndRole --------------- The :smtk:`ResourceAndRole ` adaptor -exists to pass information from GatherResources to FillOutAttributes. +exists to pass information from GatherResources to FillOutAttributes, +possibly passing this information "through" Groups that have these tasks +as children. When configured, only attribute resources chosen by the user will be examined for validity. -The ResourceAndRole adaptor accepts no additional JSON configuration beyond the base -Adaptor class. +The ResourceAndRole adaptor accepts two additional JSON configuration entries +beyond the base Adaptor class: + +* ``from-tag``: this is a string used *only* when the "from" task is a Group. + It specifies a key in the Group's interal configuration object from which + to fetch data to apply to the destination task. + The default value is an empty string. +* ``to-tag``: this is a string used *only* when the "to" task is a Group. + It specifies a key in the Group's interal configuration object to which + to write data that will be applied to the destination task. + The default value is an empty string. + +The Group from- and to-tag strings exist because a Group task may need +to aggregate state from multiple child tasks or configure multiple +child tasks separately. +By providing the key, authors can control which child tasks share or +do not share configuration information. +If the default empty string is used, then all tasks will share +the same configuration information. Example """"""" @@ -57,6 +76,7 @@ Example { "id": 1, "type": "smtk::task::adaptor::ResourceAndRole", - "from": 1, /* must be a GatherResources task */ - "to": 2 /* must be a FillOutAttributes task */ + "from": 1, /* must be a GatherResources or Group task */ + "to": 2, /* must be a FillOutAttributes or Group task */ + "to-tag": "foo" /* if "to" is a Group, "foo" is a key for resource+role data. */ } diff --git a/doc/userguide/task/classes.rst b/doc/userguide/task/classes.rst index 9e3737491f..19d483313b 100644 --- a/doc/userguide/task/classes.rst +++ b/doc/userguide/task/classes.rst @@ -16,6 +16,8 @@ It is Completable by default. The following JSON can be used to configure it: * ``title``: an optional string value holding instructions to users. +* ``style``: an optional array of strings holding presentation + style-class names for the task. * ``completed``: an optional boolean value indicating whether the user has marked the task complete or not. @@ -27,17 +29,99 @@ Example { "type": "smtk::task::Task", "title": "Instructions to users.", + "style": [ "unique-component-colors", "fancy-menu" ], "completed": false } +ConfigureOperation +------------------ + +:smtk:`ConfigureOperation ` exists to +create and validate inputs to an operation. + +You can use a :smtk:`MapComponents ` +adaptor to configure the operation's associated objects. + +Example +""""""" + +.. code:: json + + { + "type": "smtk::task::ConfigureOperation", + "title": "Export simulation", + "operation": "smtk::session::oscillator::Export" + } + +FillOutAttributes +----------------- + +The :smtk:`FillOutAttributes task ` +monitors operations for attribute resources with particular roles. +When an operation creates or modifies a matching resource, the +task checks whether all the attributes with matching definitions +are valid. If so, the task is Completable. If not, it is Incomplete. +It is Completable by default (i.e., if no matching resources +or attributes exist). + +This task accepts all the JSON configuration that the base Task class does, plus: + +* ``attribute-sets``: a JSON array of required attributes, organized by role. + Each array entry must be a JSON object holding: + + * ``role``: an optional string holding an attribute-resource role name. + If omitted, any role is allowed. + * ``definitions``: a set of :smtk:`smtk::attribute::Definition` type-names + specifying which types of attributes to validate before allowing completion. + * ``auto-configure``: either true or false (the default), depending on + whether resources with matching roles should automatically be added. + The default is false since a task-adaptor, such as + :smtk:`ResourceAndRole `, will + normally configure only those resources identified by a user as + relevant in a dependent task. + +Example +""""""" + +.. code:: json + + { + "type": "smtk::task::FillOutAttributes", + "title": "Assign materials and mesh sizing.", + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": ["SolidMaterial", "FluidMaterial"] + }, + { + "role": "meshing attribute", + "definitions": [ + "GlobalSizingParameters", + "FaceSize", + "EdgeSize" + ] + } + ] + } + +In the example above, you can see that two different attribute resources +(one for the simulation and one for a mesh generator) are specified with +different roles and the definitions that should be checked for resources +in those roles are different. + Group ----- -A task :smtk:`Group ` exists to organize a set of tasks. +A task :smtk:`Group ` exists to collect similar or related +child tasks together in order to organize the workflow and reduce clutter. +The Group's state and output are dependent on its children. The Group instance is responsible for configuring its children, including -creating dependencies among them. The Group's state and output are -dependent on its children. +creating dependencies among them; this is accomplished by accepting +adaptors that link the Group to its child task and vice-versa. +The Group provides adaptors with an "adaptor data" object where they +can store configuration information and isolate the children from +external tasks. The Group has a "mode," which describes how children are related to one another: when the mode is parallel, children have no dependency on @@ -67,8 +151,13 @@ The task Group class accepts all the JSON configuration that the base Task class Each child task may have an integer ``id`` whose value may be referenced by ``adaptors`` below. * ``adaptors``: an array of task-adaptor specifications that inform - the group task how to configure children. The reserved ``id`` of 0 - refers to the Group itself. + the group task how to configure children. The reserved ``id`` of 1 + refers to the Group itself. Child tasks are numbered 2 and above. +* ``adaptor-data``: a dictionary of key-value pairs. The keys are arbitrary strings + provided by adaptors and the values are serializations of configuration information + to be passed to child tasks from the parent or vice-versa. + This is not typically specified when authoring a workflow but is saved and loaded + when saving task state. Example """"""" @@ -81,12 +170,12 @@ Example "mode": "serial", "children": [ { - "id": 1, + "id": 2, "type": "smtk::task::Task", "title": "Step 1." }, { - "id": 2, + "id": 3, "type": "smtk::task::Task", "title": "Step 2." } @@ -94,28 +183,30 @@ Example "adaptors": [ { "//": "How the parent configures its child." - "type": "smtk::task::adaptor::PassResources", - "from": 0, - "to": 1 + "type": "smtk::task::adaptor::ResourceAndRole", + "from-tag": "simulation", + "from": 1, + "to": 2 }, { "//": "How the parent configures its child." - "type": "smtk::task::adaptor::PassResources", - "from": 0, - "to": 2 + "type": "smtk::task::adaptor::ResourceAndRole", + "from-tag": "model", + "from": 1, + "to": 3 }, { "//": "How the serial task configures its successor." "type": "smtk::task::adaptor::PassComponents", - "from": 1, - "to": 2 + "from": 2, + "to": 3 }, { "//": "How a child task configures its parent's" "//": "output. Be careful to avoid loops." "type": "smtk::task::adaptor::PassComponents", - "from": 2, - "to": 0 + "from": 3, + "to": 1 } ] } @@ -130,6 +221,9 @@ resources is acceptable, at which time it transitions to completable. It is Incomplete by default unless unconfigured (in which case it is Completable). It accepts all the JSON configuration that the base Task class does, plus: +* ``auto-configure``: either true or false (the default), depending on whether + resources should be automatically pulled from the resource manager based on + their roles (true) or whether a user must explicitly assign resources (false). * ``resources``: a JSON array of required resources, organized by role. Each array entry must be a JSON object holding: @@ -164,59 +258,3 @@ Example } ] } - -FillOutAttributes ------------------ - -The :smtk:`FillOutAttributes task ` -monitors operations for attribute resources with particular roles. -When an operation creates or modifies a matching resource, the -task checks whether all the attributes with matching definitions -are valid. If so, the task is Completable. If not, it is Incomplete. -It is Completable by default (i.e., if no matching resources -or attributes exist). - -This task accepts all the JSON configuration that the base Task class does, plus: - -* ``attribute-sets``: a JSON array of required attributes, organized by role. - Each array entry must be a JSON object holding: - - * ``role``: an optional string holding an attribute-resource role name. - If omitted, any role is allowed. - * ``definitions``: a set of :smtk:`smtk::attribute::Definition` type-names - specifying which types of attributes to validate before allowing completion. - * ``auto-configure``: either true or false (the default), depending on - whether resources with matching roles should automatically be added. - The default is false since a task-adaptor, such as - :smtk:`ResourceAndRole `, will - normally configure only those resources identified by a user as - relevant in a dependent task. - -Example -""""""" - -.. code:: json - - { - "type": "smtk::task::FillOutAttributes", - "title": "Assign materials and mesh sizing.", - "attribute-sets": [ - { - "role": "simulation attribute", - "definitions": ["SolidMaterial", "FluidMaterial"] - }, - { - "role": "meshing attribute", - "definitions": [ - "GlobalSizingParameters", - "FaceSize", - "EdgeSize" - ] - } - ] - } - -In the example above, you can see that two different attribute resources -(one for the simulation and one for a mesh generator) are specified with -different roles and the definitions that should be checked for resources -in those roles are different. diff --git a/doc/userguide/task/concepts.rst b/doc/userguide/task/concepts.rst index 786b9ede74..c23393fdbf 100644 --- a/doc/userguide/task/concepts.rst +++ b/doc/userguide/task/concepts.rst @@ -1,15 +1,49 @@ Key Concepts ============ +A *task* is some activity that a user must complete to accomplish +a simulation pre- or post-processing objective (e.g., generating +a geometric model of a simulation domain, associating attributes +to model geometry, meshing a model, exporting a simulation input +deck, submitting a simulation job, post-processing simulation results). +Each task has *state* that indicates how complete it is. +Tasks may reference other tasks as *dependencies*, +which means the referenced tasks must be completed before +their *dependent* tasks may be undertaken by the user. + The graph of tasks (with dependencies as arcs) indicates what tasks a user may work on, what tasks are incomplete, and what tasks cannot be performed because of unmet dependencies. +.. findfigure:: task-terminology.* + :align: center + :width: 90% + +It is possible to have multiple, disjoint graphs of tasks. +Each connected component is called a *workflow* or *pipeline*. + A task becomes active when its dependencies are met and the user chooses to focus on the task. An application can compute the set of tasks which users are allowed to focus on (make active) by cutting the graph along arcs that connect completed tasks to incomplete tasks. +When a task becomes active, the application will generally change +its appearance and functionality to aid the user in performing +the task. The visual *style* adopted by the application should be +guided by the style class-names (arbitrary, author-defined strings) +assigned (by the task author) to the task. + +Tasks are allowed to have children. +When a task has children, the children form a workflow with one or more +head tasks and may not have dependencies on external tasks (i.e., on +tasks that are not children of the same parent). +The parent task should configure its children and its internal state +should be some function of the state of its children. +To work on a child task, the user must first make the parent task +active and may then make one of the children head-tasks active. + +Note that a workflow may have multiple "head" tasks (i.e., tasks without +dependencies whose dependents reference all of the head tasks). :smtk:`State ` is an enumeration of the states a task may take on. @@ -60,3 +94,16 @@ that connect completed tasks to incomplete tasks. is an object applications can create to hold a task factory and the set of task instances the factory has created. It also holds the active task tracker. + +Pipelines + are tasks that form a directed acyclic graph of dependencies. + There is no explicit class representing pipelines since they + can be produced by visiting related (dependent) Task instances given + the task(s) at the "head" of the pipeline (i.e., tasks with no + dependencies). + + Instead of providing an explicit representation of pipelines, + SMTK provides observers for changes to the set of pipeline head tasks. + The task :smtk:`Instances ` class has + a ``workflowObservers()`` method that you may use to be informed + of :smtk:`workflow events `. diff --git a/doc/userguide/task/index.rst b/doc/userguide/task/index.rst index 1667d2a5be..638b3081a9 100644 --- a/doc/userguide/task/index.rst +++ b/doc/userguide/task/index.rst @@ -1,12 +1,19 @@ .. _smtk-task-sys: --------------- -Workflow tasks --------------- +------------------ +SMTK's Task System +------------------ -SMTK provides tools to guide users through the process of preparing a simulation description. -A workflow consists of a series of tasks that may have dependencies. -These tasks form a graph with dependencies as arcs. +Simulations can require input from many people with different skill sets. +People with backgrounds in engineering; physical modeling; geometric discretization; +numerical analysis; visualization and data analysis; statistics; and project management +may all be involved, each with different tasks to complete. +SMTK accommodates this range of interests and tasks by providing applications +with ways to prompt users – especially infrequent or inexpert users – with +suggested tasks required to complete the portion of the design process. + +These tasks, together with their inter-dependencies, form a +a graph with tasks as nodes and dependencies as arcs. .. toctree:: :maxdepth: 3 diff --git a/doc/userguide/workflow/concepts.rst b/doc/userguide/workflow/concepts.rst deleted file mode 100644 index 3aa7917805..0000000000 --- a/doc/userguide/workflow/concepts.rst +++ /dev/null @@ -1,15 +0,0 @@ -Key Concepts -============ - -Internally, SMTK represents a workflow as a directed acyclic graph. -Nodes in the graph may be - -+ actions to take; -+ conditions which must be met; or -+ choices between acceptable actions or conditions. - -Edges in the graph indicate dependencies between nodes. - -Presenting a complete task DAG would be confusing to many users. -Instead, we use SMTK's view presentation model to allow applications -to tailor what is shown based on the current state. diff --git a/doc/userguide/workflow/index.rst b/doc/userguide/workflow/index.rst deleted file mode 100644 index 1143ad6f95..0000000000 --- a/doc/userguide/workflow/index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _smtk-workflow-sys: - ----------------------- -SMTK's Workflow System ----------------------- - -SMTK's workflow system is aimed at providing guidance to -users on tasks required to complete the simulation preparation -process. - -Simulations can require input from many people with different skill sets. -People with backgrounds in engineering; physical modeling; geometric discretization; -numerical analysis; visualization and data analysis; statistics; and project management -may all be involved, each with different tasks to complete. -SMTK accommodates this range of interests and tasks by providing applications -with ways to prompt users, especially infrequent or inexpert users, with -suggested actions. - -.. toctree:: - :maxdepth: 3 - - concepts.rst diff --git a/smtk/task/Adaptor.cxx b/smtk/task/Adaptor.cxx index 19c73b6237..7606000c1f 100644 --- a/smtk/task/Adaptor.cxx +++ b/smtk/task/Adaptor.cxx @@ -21,10 +21,7 @@ Adaptor::Adaptor(const Configuration& config) (void)config; // subclasses may use this } -Adaptor::Adaptor( - const Configuration& config, - std::shared_ptr& from, - std::shared_ptr& to) +Adaptor::Adaptor(const Configuration& config, Task* from, Task* to) : m_from(from) , m_to(to) { diff --git a/smtk/task/Adaptor.h b/smtk/task/Adaptor.h index e894ee0424..4df51a3f1e 100644 --- a/smtk/task/Adaptor.h +++ b/smtk/task/Adaptor.h @@ -31,7 +31,7 @@ public: /// Construct an unconfigured adaptor. Adaptor(); Adaptor(const Configuration& config); - Adaptor(const Configuration& config, std::shared_ptr& from, std::shared_ptr& to); + Adaptor(const Configuration& config, Task* from, Task* to); /// Destructor must be virtual. virtual ~Adaptor() = default; @@ -42,13 +42,13 @@ public: virtual bool reconfigureTask() = 0; /// The task this adaptor uses to fetch configuration parameters. - std::shared_ptr from() const { return m_from.lock(); } + Task* from() const { return m_from; } /// The task to which this adaptor applies configuration parameters. - std::shared_ptr to() const { return m_to.lock(); } + Task* to() const { return m_to; } protected: - std::weak_ptr m_from; - std::weak_ptr m_to; + Task* m_from; + Task* m_to; smtk::task::Task::Observers::Key m_observer; }; } // namespace task diff --git a/smtk/task/CMakeLists.txt b/smtk/task/CMakeLists.txt index 0087f2019d..93b8bb9c4a 100644 --- a/smtk/task/CMakeLists.txt +++ b/smtk/task/CMakeLists.txt @@ -14,6 +14,7 @@ set(adaptors set(taskSrcs Active.cxx Adaptor.cxx + Instances.cxx Manager.cxx Registrar.cxx adaptor/ResourceAndRole.cxx diff --git a/smtk/task/ConfigureOperation.cxx b/smtk/task/ConfigureOperation.cxx new file mode 100644 index 0000000000..b90c188dde --- /dev/null +++ b/smtk/task/ConfigureOperation.cxx @@ -0,0 +1,296 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/FillOutAttributes.h" + +#include "smtk/project/ResourceContainer.h" + +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonFillOutAttributes.h" + +#include "smtk/operation/Manager.h" +#include "smtk/operation/SpecificationOps.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/Resource.h" + +#include "smtk/resource/Manager.h" + +#include + +namespace smtk +{ +namespace task +{ + +FillOutAttributes::FillOutAttributes() = default; + +FillOutAttributes::FillOutAttributes( + const Configuration& config, + const smtk::common::Managers::Ptr& managers) + : Task(config, managers) + , m_managers(managers) +{ + this->configure(config); +} + +FillOutAttributes::FillOutAttributes( + const Configuration& config, + const PassedDependencies& dependencies, + const smtk::common::Managers::Ptr& managers) + : Task(config, dependencies, managers) + , m_managers(managers) +{ + this->configure(config); +} + +void FillOutAttributes::configure(const Configuration& config) +{ + // The predicate from_json method needs the resource manager: + auto& helper = json::Helper::instance(); + helper.setManagers(m_managers); + + if (config.contains("attribute-sets")) + { + config.at("attribute-sets").get_to(m_attributeSets); + } + if (m_managers) + { + if (auto operationManager = m_managers->get()) + { + m_observer = operationManager->observers().insert( + [this]( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result) { return this->update(op, event, result); }, + /* priority */ 0, + /* initialize */ true, + "FillOutAttributes monitors operations for updates."); + } + } + if (!m_attributeSets.empty()) + { + this->initializeResources(); + this->internalStateChanged(this->computeInternalState()); + } +} + +smtk::common::Visit FillOutAttributes::visitAttributeSets(AttributeSetVisitor visitor) +{ + if (!visitor) + { + return smtk::common::Visit::Halt; + } + for (auto& entry : m_attributeSets) + { + if (visitor(entry) == smtk::common::Visit::Halt) + { + return smtk::common::Visit::Halt; + } + } + return smtk::common::Visit::Continue; +} + +bool FillOutAttributes::initializeResources() +{ + bool foundResource = false; + if (m_attributeSets.empty()) + { + return foundResource; + } + if (auto resourceManager = m_managers->get()) + { + auto resources = resourceManager->find(); + for (const auto& resource : resources) + { + const std::string& role = smtk::project::detail::role(resource); + for (auto& attributeSet : m_attributeSets) + { + if ( + attributeSet.m_role.empty() || attributeSet.m_role == "*" || attributeSet.m_role == role) + { + if (attributeSet.m_autoconfigure) + { + foundResource = true; + auto it = attributeSet.m_resources.insert({ resource->id(), { {}, {} } }).first; + this->updateResourceEntry(*resource, attributeSet, it->second); + } + } + } + } + } + return foundResource; +} + +bool FillOutAttributes::updateResourceEntry( + smtk::attribute::Resource& resource, + const AttributeSet& predicate, + ResourceAttributes& entry) +{ + bool changesMade = false; + // I. Remove invalid entries for attributes that are valid or deleted. + std::set expunged; + std::set validated; + std::set invalidated; + for (const auto& invalidId : entry.m_invalid) + { + auto att = resource.findAttribute(invalidId); + if (att) + { + if (att->isValid()) // TODO: accept predicate override for categories? + { + validated.insert(invalidId); + } + } + else + { + expunged.insert(invalidId); + } + } + // II. Check valid attributes to see if they have been invalidated or expunged. + for (const auto& validId : entry.m_valid) + { + auto att = resource.findAttribute(validId); + if (att) + { + if (!att->isValid()) // TODO: accept predicate override for categories? + { + invalidated.insert(validId); + } + } + else + { + expunged.insert(validId); + } + } + // If the set of invalid attributes was changed, we need to re-run computeInternalState(). + changesMade |= !expunged.empty() || !validated.empty() || !invalidated.empty(); + for (const auto& id : validated) + { + entry.m_invalid.erase(id); + entry.m_valid.insert(id); + } + for (const auto& id : expunged) + { + entry.m_invalid.erase(id); + } + for (const auto& id : invalidated) + { + entry.m_invalid.insert(id); + entry.m_valid.erase(id); + } + // II. Check for newly-created attributes + std::vector attributes; + for (const auto& definition : predicate.m_definitions) + { + resource.findAttributes(definition, attributes); + for (const auto& attribute : attributes) + { + auto uid = attribute->id(); + if ( + (entry.m_invalid.find(uid) == entry.m_invalid.end()) && + (entry.m_valid.find(uid) == entry.m_valid.end())) + { + // We've found a new attribute. Classify it. + changesMade = true; + if (attribute->isValid()) // TODO: accept predicate override for categories? + { + entry.m_valid.insert(uid); + } + else + { + entry.m_invalid.insert(uid); + } + } + } + } + return changesMade; +} + +int FillOutAttributes::update( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result) +{ + (void)op; + bool predicatesUpdated = false; + switch (event) + { + case smtk::operation::EventType::DID_OPERATE: + { + auto mentionedResources = smtk::operation::extractResources(result); + + for (const auto& weakResource : mentionedResources) + { + auto resource = std::dynamic_pointer_cast(weakResource.lock()); + if (resource) + { + const std::string& role = smtk::project::detail::role(resource); + // Do we care about this resource? + for (auto& predicate : m_attributeSets) + { + auto it = predicate.m_resources.find(resource->id()); + bool doUpdate = false; + if (it != predicate.m_resources.end()) + { + doUpdate = true; + } + else if ( + predicate.m_role == role || predicate.m_role == "*" || predicate.m_role.empty()) + { + if (predicate.m_autoconfigure) + { + it = predicate.m_resources.insert({ resource->id(), { {}, {} } }).first; + doUpdate = true; + } + } + if (doUpdate) + { + predicatesUpdated |= this->updateResourceEntry(*resource, predicate, it->second); + } + } + } + } + } + break; + case smtk::operation::EventType::WILL_OPERATE: + break; + } + if (predicatesUpdated) + { + this->internalStateChanged(this->computeInternalState()); + } + return 0; +} + +State FillOutAttributes::computeInternalState() const +{ + State s = State::Completable; + bool empty = true; + for (const auto& predicate : m_attributeSets) + { + for (const auto& resourceEntry : predicate.m_resources) + { + empty &= resourceEntry.second.m_valid.empty() && resourceEntry.second.m_invalid.empty(); + if (!resourceEntry.second.m_invalid.empty()) + { + s = State::Incomplete; + return s; + } + } + } + if (empty) + { + s = State::Irrelevant; + } + return s; +} + +} // namespace task +} // namespace smtk diff --git a/smtk/task/ConfigureOperation.h b/smtk/task/ConfigureOperation.h new file mode 100644 index 0000000000..385e7d6e13 --- /dev/null +++ b/smtk/task/ConfigureOperation.h @@ -0,0 +1,119 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_task_FillOutAttributes_h +#define smtk_task_FillOutAttributes_h + +#include "smtk/operation/Manager.h" +#include "smtk/operation/Observer.h" +#include "smtk/operation/Operation.h" +#include "smtk/resource/Resource.h" +#include "smtk/task/Task.h" + +#include "smtk/common/Visit.h" + +namespace smtk +{ +namespace task +{ +// Forward declaration +namespace adaptor +{ +class ResourceAndRole; +} + +/**\brief FillOutAttributes is a task that is incomplete until specified + * attributes are valid. + * + * This task accepts an input attribute resource (configured by a predecessor + * task or specified via a role) and observe an operation manager for operations. + * After each operation, attributes with a definition are validated. + * If all attributes identify are valid, the task becomes completable. + * Otherwise, the task will remain (or become) incomplete. + */ +class SMTKCORE_EXPORT FillOutAttributes : public Task +{ +public: + smtkTypeMacro(smtk::task::FillOutAttributes); + smtkSuperclassMacro(smtk::task::Task); + smtkCreateMacro(smtk::task::Task); + + /// Per-resource sets of validated attributes + /// + /// We need to track attributes so incremental updates + /// can decide whether to change state. + struct ResourceAttributes + { + /// Attributes matching a definition that are validated. + std::set m_valid; + /// Attributes matching a definition that need attention. + std::set m_invalid; + }; + /// A predicate used to collect resources that fit a given role. + struct AttributeSet + { + /// The required role. If empty, any role is allowed. + std::string m_role; + /// The definitions in matching resources whose attributes should be valid. + std::set m_definitions; + /// Should all resources with a matching role be added? + /// + /// If false (default), then resources must be explicitly configured by UUID + /// or configured by a task adaptor. + /// If true, then all resources with a matching role will have attributes + /// matching m_definitions checked. + bool m_autoconfigure = false; + /// The set of resources being managed that are selected by the validator. + std::map m_resources; + }; + /// Signatures of functors that visit resources-by-role predicates. + using AttributeSetVisitor = std::function; + + FillOutAttributes(); + FillOutAttributes( + const Configuration& config, + const smtk::common::Managers::Ptr& managers = nullptr); + FillOutAttributes( + const Configuration& config, + const PassedDependencies& dependencies, + const smtk::common::Managers::Ptr& managers = nullptr); + + ~FillOutAttributes() override = default; + + void configure(const Configuration& config); + + smtk::common::Visit visitAttributeSets(AttributeSetVisitor visitor); + +protected: + friend class adaptor::ResourceAndRole; + + /// Initialize with a list of resources from manager in m_managers. + bool initializeResources(); + /// Update a single resource in a predicate + bool updateResourceEntry( + smtk::attribute::Resource& resource, + const AttributeSet& predicate, + ResourceAttributes& entry); + /// Respond to operations that may change task state. + int update( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result); + + /// Check m_resourcesByRole to see if all requirements are met. + State computeInternalState() const; + + smtk::common::Managers::Ptr m_managers; + smtk::operation::Observers::Key m_observer; + std::vector m_attributeSets; +}; +} // namespace task +} // namespace smtk + +#endif // smtk_task_FillOutAttributes_h diff --git a/smtk/task/FillOutAttributes.cxx b/smtk/task/FillOutAttributes.cxx index b90c188dde..31c7152d12 100644 --- a/smtk/task/FillOutAttributes.cxx +++ b/smtk/task/FillOutAttributes.cxx @@ -81,6 +81,75 @@ void FillOutAttributes::configure(const Configuration& config) } } +bool FillOutAttributes::getViewData(smtk::common::TypeContainer& configuration) const +{ + using ResourceSet = std:: + set>; + + bool didChange = false; + auto resourceManager = m_managers->get(); + if (!resourceManager) + { + return didChange; + } + + std::set previous; + if (configuration.contains()) + { + for (const auto& weakResource : configuration.get()) + { + if (auto resource = weakResource.lock()) + { + previous.insert(resource->id()); + } + } + } + + ResourceSet viewData; + this->visitAttributeSets( + [&didChange, &resourceManager, &previous, &viewData](const AttributeSet& attributeSet) { + for (const auto& entry : attributeSet.m_resources) + { + if ( + auto resource = + std::dynamic_pointer_cast(resourceManager->get(entry.first))) + { + viewData.insert(resource); + if (previous.find(entry.first) == previous.end()) + { + didChange = true; + } + } + } + return smtk::common::Visit::Continue; + }); + if (!didChange && previous.size() != viewData.size()) + { + didChange = true; // previous entries were removed. + } + if (didChange) + { + configuration.insert_or_assign(viewData); + } + return didChange; +} + +smtk::common::Visit FillOutAttributes::visitAttributeSets(ConstAttributeSetVisitor visitor) const +{ + if (!visitor) + { + return smtk::common::Visit::Halt; + } + for (const auto& entry : m_attributeSets) + { + if (visitor(entry) == smtk::common::Visit::Halt) + { + return smtk::common::Visit::Halt; + } + } + return smtk::common::Visit::Continue; +} + smtk::common::Visit FillOutAttributes::visitAttributeSets(AttributeSetVisitor visitor) { if (!visitor) diff --git a/smtk/task/FillOutAttributes.h b/smtk/task/FillOutAttributes.h index 385e7d6e13..b16ffef094 100644 --- a/smtk/task/FillOutAttributes.h +++ b/smtk/task/FillOutAttributes.h @@ -74,6 +74,7 @@ public: }; /// Signatures of functors that visit resources-by-role predicates. using AttributeSetVisitor = std::function; + using ConstAttributeSetVisitor = std::function; FillOutAttributes(); FillOutAttributes( @@ -86,8 +87,13 @@ public: ~FillOutAttributes() override = default; + /// Parse configuration information to initialize this instance. void configure(const Configuration& config); + /// Provide the attribute resource(s) that the user should edit. + bool getViewData(smtk::common::TypeContainer& configuration) const override; + + smtk::common::Visit visitAttributeSets(ConstAttributeSetVisitor visitor) const; smtk::common::Visit visitAttributeSets(AttributeSetVisitor visitor); protected: diff --git a/smtk/task/GatherResources.cxx b/smtk/task/GatherResources.cxx index cab6616125..4203ecefb5 100644 --- a/smtk/task/GatherResources.cxx +++ b/smtk/task/GatherResources.cxx @@ -10,9 +10,15 @@ #include "smtk/task/GatherResources.h" +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonGatherResources.h" + +#include "smtk/project/ResourceContainer.h" + #include "smtk/operation/Manager.h" #include "smtk/operation/SpecificationOps.h" -#include "smtk/project/ResourceContainer.h" + +#include "smtk/io/Logger.h" #include @@ -21,65 +27,6 @@ namespace smtk namespace task { -void to_json(json& j, const GatherResources::ResourceSet& p) -{ - j = json{ { "role", p.m_role }, { "type", p.m_type } }; - if (p.m_minimumCount == 0 && p.m_maximumCount < 0) - { - // skip counts; any number of resources are allowed. - } - else - { - j["min"] = p.m_minimumCount; - j["max"] = p.m_maximumCount; - } - if (p.m_validator) - { - j["validator"] = "Cannot serialize validators yet."; - } -} - -void from_json(const json& j, GatherResources::ResourceSet& p) -{ - if (j.contains("role")) - { - j.at("role").get_to(p.m_role); - } - if (j.contains("type")) - { - j.at("type").get_to(p.m_type); - } - if (j.contains("min")) - { - j.at("min").get_to(p.m_minimumCount); - } - else - { - p.m_minimumCount = 1; - } - if (j.contains("max")) - { - j.at("max").get_to(p.m_maximumCount); - } - else - { - p.m_maximumCount = -1; - } - if (j.contains("validator")) - { - throw std::logic_error("todo"); // TODO - } - else - { - // Accept any resource - p.m_validator = nullptr; - /* - [](const smtk::resource::Resource&, const TaskNeedsResource::ResourceSet&) - { return true; }; - */ - } -} - GatherResources::GatherResources() = default; GatherResources::GatherResources( @@ -103,6 +50,12 @@ GatherResources::GatherResources( void GatherResources::configure(const Configuration& config) { + if (m_managers) + { + json::Helper::instance().setManagers(m_managers); + } + m_autoconfigure = + (config.contains("auto-configure") ? config.at("auto-configure").get() : false); if (config.contains("resources")) { auto rsrcSpecs = config.at("resources"); @@ -131,7 +84,7 @@ void GatherResources::configure(const Configuration& config) }, /* priority */ 0, /* initialize */ true, - "GatherResources monitors results for resources and their roles."); + "GatherResources monitors resources and their roles."); } } if (!m_resourcesByRole.empty()) @@ -140,6 +93,38 @@ void GatherResources::configure(const Configuration& config) } } +bool GatherResources::addResourceInRole( + const std::shared_ptr& resource, + const std::string& role) +{ + if (!resource) + { + return false; + } + auto it = m_resourcesByRole.find(role); + if (it == m_resourcesByRole.end()) + { + smtkWarningMacro( + smtk::io::Logger::instance(), + "GatherResources is not configured for the role \"" << role << "\"."); + return false; + } + if (it->second.m_type == "*" || resource->isOfType(it->second.m_type)) + { + if (it->second.m_resources.insert(resource).second) + { + // TODO: Should we set the role: + // resource->properties().get()[smtk::project::ResourceContainer::role_name] = role + // so that a resource-manager event that drops the resource removes + // it from the proper ResourceSet? Otherwise, it can be dropped but + // we won't transition from completable to incomplete if we need to... + this->internalStateChanged(this->computeInternalState()); + return true; + } + } + return false; +} + smtk::common::Visit GatherResources::visitResourceSets(ResourceSetVisitor visitor) { if (!visitor) @@ -166,15 +151,18 @@ void GatherResources::updateResources( { case smtk::resource::EventType::ADDED: { - // Add the resource to the appropriate entry: - const std::string& role = smtk::project::detail::role(resourcePtr); - auto it = m_resourcesByRole.find(role); - if (it != m_resourcesByRole.end()) + if (m_autoconfigure) { - if (!it->second.m_validator || it->second.m_validator(resource, it->second)) + // Add the resource to the appropriate entry: + const std::string& role = smtk::project::detail::role(resourcePtr); + auto it = m_resourcesByRole.find(role); + if (it != m_resourcesByRole.end()) { - it->second.m_resources.insert(resourcePtr); - resourceSetsUpdated = true; + if (!it->second.m_validator || it->second.m_validator(resource, it->second)) + { + it->second.m_resources.insert(resourcePtr); + resourceSetsUpdated = true; + } } } } @@ -191,7 +179,7 @@ void GatherResources::updateResources( } break; case smtk::resource::EventType::MODIFIED: - // TODO + // TODO: Maybe a role was removed? break; } if (resourceSetsUpdated) diff --git a/smtk/task/GatherResources.h b/smtk/task/GatherResources.h index 1d8163ec9b..cd5a80d2be 100644 --- a/smtk/task/GatherResources.h +++ b/smtk/task/GatherResources.h @@ -74,6 +74,22 @@ public: void configure(const Configuration& config); + /// Explicitly add a \a resource in the given \a role. + /// + /// If \a autoconfigure is disabled, calling this method is + /// the only way to complete this task. + /// + /// The \a role must be one this task is configured to accept. + /// This method will add the \a resource as long as it is of + /// the correct type (regardless of whether it is marked with + /// the \a role passed to this method). + /// + /// If the resource was added (i.e., not already present in + /// the given \a role), then this method returns true. + bool addResourceInRole( + const std::shared_ptr& resource, + const std::string& role); + smtk::common::Visit visitResourceSets(ResourceSetVisitor visitor); protected: @@ -83,6 +99,7 @@ protected: /// Check m_resourcesByRole to see if all requirements are met. State computeInternalState() const; + bool m_autoconfigure = false; smtk::common::Managers::Ptr m_managers; smtk::resource::Observers::Key m_observer; std::map m_resourcesByRole; diff --git a/smtk/task/Group.cxx b/smtk/task/Group.cxx index a7eb96f87c..d998926c26 100644 --- a/smtk/task/Group.cxx +++ b/smtk/task/Group.cxx @@ -13,6 +13,7 @@ #include "smtk/task/json/Helper.h" #include "smtk/task/json/jsonGroup.h" +#include "smtk/task/json/jsonManager.h" #include "smtk/operation/Manager.h" #include "smtk/operation/SpecificationOps.h" @@ -50,7 +51,46 @@ Group::Group( void Group::configure(const Configuration& config) { + if (config.contains("adaptor-data")) + { + m_adaptorData = config.at("adaptor-data"); + } + // Push a new helper onto the stack (so task/adaptor numbers + // refer to children, not exernal objects): + auto& helper = smtk::task::json::Helper::pushInstance(this); // Instantiate children and configure them + if (config.contains("children")) + { + std::vector> tasks; + if (!smtk::task::json::jsonManager::deserialize( + tasks, m_adaptors, helper.managers(), config.at("children"))) + { + smtkErrorMacro(smtk::io::Logger::instance(), "Could not deserialize child tasks."); + } + for (const auto& weakChild : tasks) + { + if (auto child = weakChild.lock()) + { + m_children.emplace(std::make_pair( + child, + child->observers().insert( + [this](Task& child, State prev, State next) { + this->childStateChanged(child, prev, next); + }, + /* priority */ 0, + /* initialize */ true, + "Group child observer."))); + } + } + } + else + { + smtkErrorMacro(smtk::io::Logger::instance(), "No children tasks, group will be trivial."); + } + // Revert helper instance to external objects: + smtk::task::json::Helper::popInstance(); + + this->internalStateChanged(this->computeInternalState()); } std::vector Group::children() const @@ -64,19 +104,128 @@ std::vector Group::children() const return result; } +smtk::common::Visit Group::visit(RelatedTasks relation, Visitor visitor) const +{ + switch (relation) + { + case RelatedTasks::Depend: + return this->Superclass::visit(relation, visitor); + break; + case RelatedTasks::Child: + for (const auto& entry : m_children) + { + auto dep = entry.first; + if (dep) + { + if (visitor(*dep) == smtk::common::Visit::Halt) + { + return smtk::common::Visit::Halt; + } + } + } + break; + } + return smtk::common::Visit::Continue; +} + +void Group::setAdaptorData(const std::string& tagName, Task::Configuration& config) +{ + auto it = m_adaptorData.find(tagName); + if (it != m_adaptorData.end()) + { + if (m_adaptorData[tagName] == config) + { // Data is identical to prior data. + return; + } + } + // Now we know we have new data. + // Assign it and reconfigure children. + m_adaptorData[tagName] = config; + for (const auto& weakAdaptor : m_adaptors) + { + if (auto adaptor = weakAdaptor.lock()) + { + if (adaptor->from() == this) + { + adaptor->reconfigureTask(); + } + } + } +} + +Task::Configuration Group::adaptorData(const std::string& key) const +{ + if (m_adaptorData.contains(key)) + { + return m_adaptorData.at(key); + } + return {}; +} + State Group::computeInternalState() const { + bool allIrrelevant = true; + bool allUnavailable = true; State s = State::Completable; for (const auto& entry : m_children) { auto childState = entry.first->state(); - if (childState < s) + if (childState != State::Irrelevant) + { + allIrrelevant = false; + } + if (childState != State::Unavailable) + { + allUnavailable = false; + } + if (childState > State::Unavailable && childState < s) { s = childState; } } + if (!m_children.empty()) + { + if (allIrrelevant) + { + s = State::Irrelevant; + } + else if (allUnavailable) + { + s = State::Unavailable; + } + } + // If the internal state is about to change to completable, + // ensure any output adaptor-data is updated. + if (s > this->internalState() && s >= State::Completable) + { + for (const auto& weakAdaptor : m_adaptors) + { + if (auto adaptor = weakAdaptor.lock()) + { + if (adaptor->to() == this) + { + adaptor->reconfigureTask(); + } + } + } + } return s; } +void Group::childStateChanged(Task& child, State prev, State next) +{ + if (prev == next) + { + return; + } + // For now, bludgeon internal state into submission by recomputing + // from scratch. In the future we should do this incrementally. + // TODO + this->internalStateChanged(this->computeInternalState()); + (void)child; + // (void)prev; + // (void)next; +} + } // namespace task } // namespace smtk diff --git a/smtk/task/Group.h b/smtk/task/Group.h index e5d2f1dc5a..62849bb3cc 100644 --- a/smtk/task/Group.h +++ b/smtk/task/Group.h @@ -22,6 +22,7 @@ namespace smtk { namespace task { +class Adaptor; /**\brief Group is a task that owns children and draws its state from them. * @@ -58,13 +59,30 @@ public: void configure(const Configuration& config); std::vector children() const; + bool hasChildren() const override { return !m_children.empty(); } + smtk::common::Visit visit(RelatedTasks relation, Visitor visitor) const override; + + const std::vector>& adaptors() const { return m_adaptors; } + + /// Set/get adaptor configuration data passed to/from child tasks. + void setAdaptorData(const std::string& tagName, Task::Configuration& config); + const Task::Configuration& adaptorData() const { return m_adaptorData; } + Task::Configuration& adaptorData() { return m_adaptorData; } + Task::Configuration adaptorData(const std::string& key) const; + + /// Return the managers used to configure this Group. + smtk::common::Managers::Ptr managers() const { return m_managers; } protected: /// Check m_resourcesByRole to see if all requirements are met. State computeInternalState() const; + void childStateChanged(Task& child, State prev, State next); + smtk::common::Managers::Ptr m_managers; std::map m_children; + std::vector> m_adaptors; + Task::Configuration m_adaptorData; }; } // namespace task } // namespace smtk diff --git a/smtk/task/Instances.cxx b/smtk/task/Instances.cxx new file mode 100644 index 0000000000..4d60fdae59 --- /dev/null +++ b/smtk/task/Instances.cxx @@ -0,0 +1,74 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#include "smtk/task/Instances.h" + +namespace smtk +{ +namespace task +{ + +Instances::Instances() + : m_workflowObservers([this](WorkflowObserver& observer) { + // Initialize an observer with all the extant workflow head-tasks. + // Basically, iterate over all tasks computing the set of all workflow heads + // and signal them with "WorkflowEvent::Resume". + std::set workflows; + std::set visited; + this->visit([&workflows, &visited](const std::shared_ptr& task) { + smtk::task::workflowsOfTask(task.get(), workflows, visited); + return smtk::common::Visit::Continue; + }); + observer(workflows, WorkflowEvent::Resuming, nullptr); + }) +{ +} + +bool Instances::pauseWorkflowNotifications(bool doPause) +{ + if (doPause == m_workflowNotificationsPaused) + { + return false; + } + m_workflowNotificationsPaused = doPause; + if (!m_workflowNotificationsPaused) + { + // Notify of changes at end of pause... + // Basically, iterate over all tasks computing the set of all workflow heads + // and signal them with "WorkflowEvent::Resume". + // This avoids having to keep and collapse diffs of all changes during a pause. + std::set workflows; + std::set visited; + this->visit([&workflows, &visited](const std::shared_ptr& task) { + smtk::task::workflowsOfTask(task.get(), workflows, visited); + return smtk::common::Visit::Continue; + }); + this->workflowEvent(workflows, WorkflowEvent::Resuming, nullptr); + } + else + { + m_needNotification = true; + } + return true; +} + +bool Instances::workflowEvent(const std::set& workflows, WorkflowEvent event, Task* subject) +{ + if (m_workflowNotificationsPaused) + { + m_needNotification = true; + return false; + } + m_needNotification = false; + m_workflowObservers(workflows, event, subject); + return true; +} + +} // namespace task +} // namespace smtk diff --git a/smtk/task/Instances.h b/smtk/task/Instances.h index 5f362cd949..8b10b69f14 100644 --- a/smtk/task/Instances.h +++ b/smtk/task/Instances.h @@ -22,15 +22,70 @@ namespace smtk namespace task { +/// An enum for events in the lifecycle of a workflow (tree of tasks). +enum class WorkflowEvent +{ + Created, //!< A workflow has been created. + TaskAdded, //!< A task has been added to the workflow. + TaskRemoved, //!< A task has been removed from the workflow. + Destroyed, //!< The tasks in the workflow have become unmanaged or dependent on another workflow. + Resuming //!< Notifications had been paused and now are not. + //!< The reported workflow heads are all extant workflows current. + //!< This event is also used to initialize observers added after tasks have been created. +}; + /// Track smtk::task::Task objects with smtk::common::Instances. -using Instances = smtk::common::Instances< - smtk::task::Task, - void, - std::tuple>, - std::tuple< - smtk::task::Task::Configuration&, - smtk::task::Task::PassedDependencies, - std::shared_ptr>>; +/// +/// This class adds an API for observing vectors of tasks that +/// form workflows as instances are managed/unmanaged and +/// dependencies are added/removed. +class SMTKCORE_EXPORT Instances + : public smtk::common::Instances< + smtk::task::Task, + void, + std::tuple>, + std::tuple< + smtk::task::Task::Configuration&, + smtk::task::Task::PassedDependencies, + std::shared_ptr>> +{ +public: + /// The signature for observing workflow construction/destruction/modification. + /// + /// The first parameter is the head task of the workflow (i.e., the one on which + /// all others depend). The second parameter is the type of event and the third + /// parameter is a task that is non-null only for WorkflowEvent::TaskAdded and + /// WorkflowEvent::TaskRemoved events: + /// + For WorkflowEvent::TaskAdded, the third parameter is the added task. + /// + For WorkflowEvent::TaskRemoved, the third parameter is the removed task. + using WorkflowObserver = std::function&, WorkflowEvent, Task*)>; + /// Observers that are invoked when the workflow structure changes. + using WorkflowObservers = smtk::common::Observers; + + Instances(); + + /// Return the set of workflow-event observers (so you can add yourself to it). + const WorkflowObservers& workflowObservers() const { return m_workflowObservers; } + WorkflowObservers& workflowObservers() { return m_workflowObservers; } + + /// Pause or resume workflow-event notifications. + /// + /// This exists so that deserialization does not generate many + /// redundant events (e.g., creating a new head for each task + /// then destroying each as dependencies are added). + bool pauseWorkflowNotifications(bool doPause); + + /// Notify observers of a change. + /// Use this method to invoke observers since it can be "paused" + /// (i.e., no observers will be invoked if \a m_workflowNotificationsPaused + /// is true). + bool workflowEvent(const std::set& workflows, WorkflowEvent event, Task* subject); + +protected: + WorkflowObservers m_workflowObservers; + bool m_workflowNotificationsPaused = false; + bool m_needNotification = false; +}; } // namespace task } // namespace smtk diff --git a/smtk/task/Task.cxx b/smtk/task/Task.cxx index ad32624606..a8f3301d60 100644 --- a/smtk/task/Task.cxx +++ b/smtk/task/Task.cxx @@ -8,6 +8,9 @@ // PURPOSE. See the above copyright notice for more information. //========================================================================= #include "smtk/task/Task.h" +#include "smtk/task/Manager.h" + +#include "smtk/task/json/Helper.h" #include @@ -16,11 +19,48 @@ namespace smtk namespace task { +void workflowsOfTask( + Task* task, + std::set& workflows, + std::set& visited) +{ + if (visited.find(task) != visited.end()) + { + return; + } + visited.insert(task); + if (task->m_dependencies.empty()) + { + workflows.insert(task); + } + else + { + for (const auto& weakDependency : task->m_dependencies) + { + if (auto dependency = weakDependency.first.lock()) + { + workflowsOfTask(dependency.get(), workflows, visited); + } + } + } +} + +std::set workflowsOfTask(Task& task) +{ + std::set result; + std::set visited; + workflowsOfTask(&task, result, visited); + return result; +} + Task::Task() = default; Task::Task(const Configuration& config, const std::shared_ptr& managers) { - (void)managers; + if (managers->contains()) + { + m_manager = managers->get(); + } this->configure(config); } @@ -29,7 +69,10 @@ Task::Task( const PassedDependencies& dependencies, const std::shared_ptr& managers) { - (void)managers; + if (managers->contains()) + { + m_manager = managers->get(); + } this->configure(config); for (const auto& dependency : dependencies) { @@ -52,10 +95,19 @@ void Task::configure(const Configuration& config) { m_title = config.at("title").get(); } + if (config.contains("style")) + { + m_style = config.at("style").get>(); + } if (config.contains("completed")) { m_completed = config.at("completed").get(); } + auto& helper = smtk::task::json::Helper::instance(); + if (!helper.topLevel()) + { + m_parent = helper.tasks().unswizzle(1); + } } void Task::setTitle(const std::string& title) @@ -63,6 +115,33 @@ void Task::setTitle(const std::string& title) m_title = title; } +bool Task::addStyle(const std::string& styleClass) +{ + if (styleClass.empty()) + { + return false; + } + return m_style.insert(styleClass).second; +} + +bool Task::removeStyle(const std::string& styleClass) +{ + return m_style.erase(styleClass) > 0; +} + +bool Task::clearStyle() +{ + bool didModify = !m_style.empty(); + m_style.clear(); + return didModify; +} + +bool Task::getViewData(smtk::common::TypeContainer& configuration) const +{ + (void)configuration; + return false; +} + State Task::state() const { State s = m_internalState; @@ -141,6 +220,18 @@ bool Task::addDependency(const std::shared_ptr& dependency) { return false; } + // Was this task previously without dependencies? If so, + // it used to be a workflow head and we must notify + // the task-manager's instances object that a head task + // is being removed. + bool wasHead = m_dependencies.empty(); + if (wasHead) + { + if (auto taskManager = m_manager.lock()) + { + taskManager->taskInstances().workflowEvent({ this }, WorkflowEvent::Destroyed, nullptr); + } + } State prev = this->state(); m_dependencies.insert(std::make_pair( (const std::weak_ptr)(dependency), @@ -148,6 +239,15 @@ bool Task::addDependency(const std::shared_ptr& dependency) bool didChange = this->updateState(dependency, prev, next); (void)didChange; }))); + dependency->m_dependents.insert(this->shared_from_this()); + if (wasHead) + { + if (auto taskManager = m_manager.lock()) + { + taskManager->taskInstances().workflowEvent( + smtk::task::workflowsOfTask(*this), WorkflowEvent::TaskAdded, this); + } + } // Now determine if this dependency changed the state. State next = this->state(); if (prev != next) @@ -161,9 +261,26 @@ bool Task::addDependency(const std::shared_ptr& dependency) bool Task::removeDependency(const std::shared_ptr& dependency) { State prev = this->state(); + bool willBeHead = + m_dependencies.size() == 1 && m_dependencies.begin()->first.lock() == dependency; + if (willBeHead) + { + if (auto taskManager = m_manager.lock()) + { + taskManager->taskInstances().workflowEvent( + smtk::task::workflowsOfTask(*this), WorkflowEvent::TaskRemoved, nullptr); + } + } bool didRemove = m_dependencies.erase(dependency) > 0; if (didRemove) { + if (willBeHead) + { + if (auto taskManager = m_manager.lock()) + { + taskManager->taskInstances().workflowEvent({ this }, WorkflowEvent::Created, nullptr); + } + } State next = this->state(); if (prev != next) { @@ -174,6 +291,31 @@ bool Task::removeDependency(const std::shared_ptr& dependency) return false; } +smtk::common::Visit Task::visit(RelatedTasks relation, Visitor visitor) const +{ + smtk::common::Visit status = smtk::common::Visit::Continue; + switch (relation) + { + case RelatedTasks::Depend: + for (const auto& entry : m_dependencies) + { + auto dep = entry.first.lock(); + if (dep) + { + if (visitor(*dep) == smtk::common::Visit::Halt) + { + status = smtk::common::Visit::Halt; + break; + } + } + } + break; + case RelatedTasks::Child: + break; + } + return status; +} + bool Task::changeState(State previous, State next) { if (previous == next) diff --git a/smtk/task/Task.h b/smtk/task/Task.h index 5f31dd6ad7..65973a3071 100644 --- a/smtk/task/Task.h +++ b/smtk/task/Task.h @@ -15,6 +15,7 @@ #include "smtk/SystemConfig.h" #include "smtk/common/Managers.h" #include "smtk/common/Observers.h" +#include "smtk/common/Visit.h" #include "smtk/task/State.h" #include "nlohmann/json.hpp" @@ -29,6 +30,20 @@ namespace smtk namespace task { +class Manager; +class Task; +/// Free functions that populate a set of workflow "head" tasks for an input \a task +/// (this is the set of tasks that \a task ultimately depends on but are not themselves +/// dependent on any task). +/// +/// The variant that accepts \a visited can be used to efficiently accumulate workflows +/// across several tasks (by pruning visited nodes from its traversal). +SMTKCORE_EXPORT void workflowsOfTask( + Task* task, + std::set& workflows, + std::set& visited); +SMTKCORE_EXPORT std::set workflowsOfTask(const Task& task); + /**\brief Task is a base class for all SMTK tasks. * * SMTK tasks are nodes in a graph whose arcs connect them to dependencies. @@ -67,6 +82,16 @@ public: using PassedDependencies = std::set; /// Tasks are configured with arbitrary JSON objects, though this may change. using Configuration = nlohmann::json; + /// Signature of functions invoked when visiting dependencies or children while + /// allowing early termination. + using Visitor = std::function; + + /// The types of related tasks to visit. + enum class RelatedTasks + { + Depend, //!< Visit tasks this task depends upon. + Child //!< Visit child tasks. + }; Task(); Task( @@ -96,6 +121,27 @@ public: /// This is not intended to be a unique identifier. void setTitle(const std::string& title); + /// Set/get style classes for the task. + /// A style class specifies how applications should present the task + /// (e.g., what type of view to provide the user, what rendering mode + /// to use, what objects to list or exclude). + const std::set& style() const { return m_style; } + bool addStyle(const std::string& styleClass); + bool removeStyle(const std::string& styleClass); + bool clearStyle(); + + /// Populate a type-container with view-related data for configuration. + /// + /// Subclasses should override this method. + /// Generally, views will want access to a resource and potentially + /// components in the resource that are the subject of the view. + /// Other view configuration will come from view style() (see above) + /// or smtk::common::Managers. + /// + /// This method will return true when the \a configuration + /// was modified and false otherwise. + virtual bool getViewData(smtk::common::TypeContainer& configuration) const; + /// Get the state. /// /// This state is a composition of \a m_internalState – updated by subclasses as @@ -111,11 +157,13 @@ public: /// /// /// - /// + /// + /// /// /// /// - /// + /// + /// /// /// /// @@ -173,6 +221,21 @@ public: template bool removeDependencies(const Container& tasks); + /// Return a parent task if one exists; null otherwise. + Task* parent() const { return m_parent; } + + /// Return whether or not the task has children. + /// By default, tasks do not support children. + virtual bool hasChildren() const { return false; } + + /// Visit children. If hasChildren returns false, this will return immediately. + /// + /// For the signature taking a Visitor, this method returns + /// smtk::common::Visit::Halt if iteration was terminated. + /// + /// Subclasses that override hasChildren should override these methods. + virtual smtk::common::Visit visit(RelatedTasks relation, Visitor visitor) const; + /// Return this object's observers so other classes may insert themselves. Observers& observers() { return m_observers; } @@ -185,6 +248,9 @@ public: State internalState() const { return m_internalState; } protected: + friend SMTKCORE_EXPORT void + workflowsOfTask(Task*, std::set&, std::set&); + /// Indicate the state has changed. /// This method invokes observers if and only if \a previous and \a next are different. /// It will also update m_completed to match the new state. @@ -208,13 +274,27 @@ protected: /// A task name to present to the user. std::string m_title; + /// The set of style classes for this task. + std::set m_style; /// Whether the user has marked the task completed or not. bool m_completed = false; /// A set of dependent tasks and the keys used to observe their /// state so that this task can update its state in response. std::map> m_dependencies; + /// Tasks upon which this task depends. + /// + /// This set is maintained by other Task instances when + /// addDependency/removeDependency is called. + std::set> m_dependents; /// The set of observers of *this* task's state. Observers m_observers; + /// If this task is the child of another task, this pointer references its parent. + /// The parent is responsible for updating this pointer as needed. + /// m_parent is not a weak pointer because it must be initialized in the child + /// during the parent's construction (when no shared pointer may exist). + Task* m_parent = nullptr; + /// If this task is being managed, this will refer to its manager. + std::weak_ptr m_manager; private: /// The internal state of the task as provided by subclasses. diff --git a/smtk/task/adaptor/Instances.h b/smtk/task/adaptor/Instances.h index 802eca0b5d..92b2eeb98c 100644 --- a/smtk/task/adaptor/Instances.h +++ b/smtk/task/adaptor/Instances.h @@ -34,8 +34,8 @@ using Instances = smtk::common::Instances< // Constructor variant: configuration and tasks. std::tuple< smtk::task::Adaptor::Configuration&, // JSON configuration information - smtk::task::Task::Ptr, // Source task (from) - smtk::task::Task::Ptr // Task to be configured (to) + smtk::task::Task*, // Source task (from) + smtk::task::Task* // Task to be configured (to) >>; } // namespace adaptor diff --git a/smtk/task/adaptor/ResourceAndRole.cxx b/smtk/task/adaptor/ResourceAndRole.cxx index 449834bfd7..7ce4a8547f 100644 --- a/smtk/task/adaptor/ResourceAndRole.cxx +++ b/smtk/task/adaptor/ResourceAndRole.cxx @@ -12,6 +12,10 @@ #include "smtk/task/FillOutAttributes.h" #include "smtk/task/GatherResources.h" +#include "smtk/task/Group.h" +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonFillOutAttributes.h" +#include "smtk/task/json/jsonGatherResources.h" #include "smtk/attribute/Resource.h" @@ -30,10 +34,7 @@ ResourceAndRole::ResourceAndRole(const Configuration& config) this->configureSelf(config); } -ResourceAndRole::ResourceAndRole( - const Configuration& config, - std::shared_ptr& from, - std::shared_ptr& to) +ResourceAndRole::ResourceAndRole(const Configuration& config, Task* from, Task* to) : Superclass(config, from, to) { this->configureSelf(config); @@ -42,68 +43,119 @@ ResourceAndRole::ResourceAndRole( bool ResourceAndRole::reconfigureTask() { bool didChange = false; - if (auto source = this->from()) + std::map> configs; + FillOutAttributes* fill = nullptr; + auto updateFillOutAttributes = + [&configs, &fill, &didChange](FillOutAttributes::AttributeSet& attributeSet) { + auto it = configs.find(attributeSet.m_role); + if (it != configs.end()) + { + std::set unused; + // Fill "unused" with all resource IDs being tracked. + for (const auto& entry : attributeSet.m_resources) + { + unused.insert(entry.first); + } + // Add entries from GatherResource's output and remove from unused. + for (const auto& resource : it->second) + { + auto asit = attributeSet.m_resources.find(resource->id()); + if (asit != attributeSet.m_resources.end()) + { + unused.erase(resource->id()); + } + else + { + didChange = true; + asit = attributeSet.m_resources.insert({ resource->id(), { {}, {} } }).first; + fill->updateResourceEntry( + *(dynamic_cast(resource.get())), + attributeSet, + asit->second); + } + } + // Remove unused (no longer relevant resources) + for (const auto& uid : unused) + { + attributeSet.m_resources.erase(uid); + didChange = true; + } + } + return smtk::common::Visit::Continue; + }; + + if (auto* source = this->from()) { - if (auto gather = std::dynamic_pointer_cast(source)) + if (auto* gather = dynamic_cast(source)) { - auto dest = this->to(); - std::map> configs; - gather->visitResourceSets( - [&configs](const GatherResources::ResourceSet& resourceSet) -> smtk::common::Visit { - auto& role_config = configs[resourceSet.m_role]; - for (auto const& resource : resourceSet.m_resources) - { - auto resource_locked = resource.lock(); - if (resource_locked) + auto* dest = this->to(); + if ((fill = dynamic_cast(dest))) + { + gather->visitResourceSets( + [&configs](const GatherResources::ResourceSet& resourceSet) -> smtk::common::Visit { + auto& role_config = configs[resourceSet.m_role]; + for (auto const& resource : resourceSet.m_resources) { - role_config.insert(resource_locked); + auto resource_locked = resource.lock(); + if (resource_locked) + { + role_config.insert(resource_locked); + } } - } - return smtk::common::Visit::Continue; - }); - if (auto fill = std::dynamic_pointer_cast(dest)) - { + return smtk::common::Visit::Continue; + }); // Look for matching roles in attribute sets and populate with // the gathered resources. - auto visitor = - [&configs, &fill, &didChange](FillOutAttributes::AttributeSet& attributeSet) { - auto it = configs.find(attributeSet.m_role); - if (it != configs.end()) + fill->visitAttributeSets(updateFillOutAttributes); + if (didChange) + { + fill->internalStateChanged(fill->computeInternalState()); + } + } + else if (auto* group = dynamic_cast(dest)) + { + Task::Configuration configs; + gather->visitResourceSets( + [&configs](const GatherResources::ResourceSet& resourceSet) -> smtk::common::Visit { + nlohmann::json jsonResourceSet = resourceSet; + configs.push_back(jsonResourceSet); + return smtk::common::Visit::Continue; + }); + group->setAdaptorData(m_fromTag, configs); + } + else + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Cannot configure resource and role on a \"" << (dest ? dest->typeName() : "null") + << "\" task."); + } + } + else if (auto* group = dynamic_cast(source)) + { + auto* dest = this->to(); + if ((fill = dynamic_cast(dest))) + { + if (group->adaptorData().contains(m_toTag)) + { + json::Helper::instance().setManagers(group->managers()); + for (const auto& jsonResourceSet : group->adaptorData()[m_toTag]) + { + auto resourceSet = jsonResourceSet.get(); + auto& role_config = configs[resourceSet.m_role]; + for (auto const& weakResource : resourceSet.m_resources) { - std::set unused; - // Fill "unused" with all resource IDs being tracked. - for (const auto& entry : attributeSet.m_resources) - { - unused.insert(entry.first); - } - // Add entries from GatherResource's output and remove from unused. - for (const auto& resource : it->second) + auto resource = weakResource.lock(); + if (resource) { - auto asit = attributeSet.m_resources.find(resource->id()); - if (asit != attributeSet.m_resources.end()) - { - unused.erase(resource->id()); - } - else - { - didChange = true; - asit = attributeSet.m_resources.insert({ resource->id(), { {}, {} } }).first; - fill->updateResourceEntry( - *(dynamic_cast(resource.get())), - attributeSet, - asit->second); - } - } - // Remove unused (no longer relevant resources) - for (const auto& uid : unused) - { - attributeSet.m_resources.erase(uid); - didChange = true; + role_config.insert(resource); } } - return smtk::common::Visit::Continue; - }; - fill->visitAttributeSets(visitor); + } + } + // Look for matching roles in attribute sets and populate with + // the gathered resources. + fill->visitAttributeSets(updateFillOutAttributes); if (didChange) { fill->internalStateChanged(fill->computeInternalState()); @@ -114,7 +166,7 @@ bool ResourceAndRole::reconfigureTask() smtkErrorMacro( smtk::io::Logger::instance(), "Cannot configure resource and role on a \"" << (dest ? dest->typeName() : "null") - << "\" task."); + << "."); } } else @@ -127,7 +179,17 @@ bool ResourceAndRole::reconfigureTask() return didChange; } -void ResourceAndRole::configureSelf(const Configuration& config) {} +void ResourceAndRole::configureSelf(const Configuration& config) +{ + if (config.contains("from-tag")) + { + config.at("from-tag").get_to(m_fromTag); + } + if (config.contains("to-tag")) + { + config.at("to-tag").get_to(m_toTag); + } +} } // namespace adaptor } // namespace task diff --git a/smtk/task/adaptor/ResourceAndRole.h b/smtk/task/adaptor/ResourceAndRole.h index 892e5c1657..6c0f3d4e0f 100644 --- a/smtk/task/adaptor/ResourceAndRole.h +++ b/smtk/task/adaptor/ResourceAndRole.h @@ -31,10 +31,7 @@ public: /// Construct an unconfigured adaptor. ResourceAndRole(); ResourceAndRole(const Configuration& config); - ResourceAndRole( - const Configuration& config, - std::shared_ptr& from, - std::shared_ptr& to); + ResourceAndRole(const Configuration& config, Task* from, Task* to); /// Reconfigure the "to()" task. /// @@ -44,6 +41,9 @@ public: protected: void configureSelf(const Configuration& config); + + std::string m_fromTag; + std::string m_toTag; }; } // namespace adaptor } // namespace task diff --git a/smtk/task/json/Configurator.h b/smtk/task/json/Configurator.h index b802370230..bd90e735c3 100644 --- a/smtk/task/json/Configurator.h +++ b/smtk/task/json/Configurator.h @@ -51,6 +51,11 @@ public: /// Keys are task class-names; values are functors that produce a JSON /// object given a task of that type. using HelperTypeMap = std::unordered_map; + /// Swizzle IDs are serializable substitutes for pointers. + /// + /// This type must be signed since negative IDs are used for children of + /// a Group task. + using SwizzleId = int; /// JSON data type using json = nlohmann::json; @@ -98,19 +103,35 @@ public: /// Return json configuration for the given object using registered helpers. typename ObjectType::Configuration configuration(const ObjectType* object); - /// Reset the helper's state. + /// Reset the configurator's state. /// /// This should be called before beginning serialization or deserialization. /// Additionally, calling it after each of these tasks is recommended since /// it will free memory. void clear(); + /// Reset just the portion of the configurator's state related to negative SwizzleIds. + /// + /// Sometimes serializers need to process nested objects. + /// While a more general solution is possible (wherein each nested + /// call to the serializer creates a new helper and pops it once + /// complete, it is simpler to constrain nested swizzlers to never + /// overlap one another. Then, we can simply re-use the namespace + /// of negative swizzle IDs for each nested call. + /// This method clears any negative swizzle IDs and should be called + /// at the start of each nested deserialization. + void clearNestedSwizzles(); + + /// Return the ID of an object as computed by the swizzler. + /// This will allocate a new, negative ID if none exists. + SwizzleId nestedSwizzleId(const ObjectType* object); + /// Return the ID of an object as computed by the swizzler. /// This will allocate a new ID if none exists. - std::size_t swizzleId(const ObjectType* object); + SwizzleId swizzleId(const ObjectType* object); /// Return the pointer to an object given its swizzled ID (or null). - ObjectType* unswizzle(std::size_t objectId) const; + ObjectType* unswizzle(SwizzleId objectId) const; /// Return a serialization of task-references that is consistent within /// the scope of serializing a set of tasks. @@ -121,9 +142,10 @@ public: protected: Helper* m_helper; - std::unordered_map m_swizzleFwd; - std::unordered_map m_swizzleBck; - std::size_t m_nextSwizzle = 1; + std::unordered_map m_swizzleFwd; + std::map m_swizzleBck; + SwizzleId m_nextSwizzle = 1; + SwizzleId m_nextNested = -1; static HelperTypeMap s_types; }; diff --git a/smtk/task/json/Configurator.txx b/smtk/task/json/Configurator.txx index 9b686b5e9b..1d9ec1e671 100644 --- a/smtk/task/json/Configurator.txx +++ b/smtk/task/json/Configurator.txx @@ -136,10 +136,48 @@ void Configurator::clear() m_nextSwizzle = 1; } +template +void Configurator::clearNestedSwizzles() +{ + std::unordered_set removals; + auto end = m_swizzleBck.lower_bound(SwizzleId(0)); + for (auto it = m_swizzleBck.begin(); it != end; ++it) + { + removals.insert(it->second); + } + m_swizzleBck.erase(m_swizzleBck.begin(), end); + for (const auto& object : removals) + { + m_swizzleFwd.erase(object); + } + m_nextNested = -1; +} + +template +typename Configurator::SwizzleId Configurator::nestedSwizzleId( + const ObjectType* object) +{ + if (!object) + { + return 0; + } + auto* ncobject = const_cast(object); // Need a non-const ObjectType in some cases. + const auto& it = m_swizzleFwd.find(ncobject); + if (it != m_swizzleFwd.end()) + { + return it->second; + } + SwizzleId id = m_nextNested--; + m_swizzleFwd[ncobject] = id; + m_swizzleBck[id] = ncobject; + return id; +} + /// Return the ID of an object as computed by the swizzler. /// This will allocate a new ID if none exists. template -std::size_t Configurator::swizzleId(const ObjectType* object) +typename Configurator::SwizzleId Configurator::swizzleId( + const ObjectType* object) { if (!object) { @@ -151,7 +189,7 @@ std::size_t Configurator::swizzleId(const ObjectType* object) { return it->second; } - std::size_t id = m_nextSwizzle++; + SwizzleId id = m_nextSwizzle++; m_swizzleFwd[ncobject] = id; m_swizzleBck[id] = ncobject; return id; @@ -159,7 +197,7 @@ std::size_t Configurator::swizzleId(const ObjectType* object) /// Return the pointer to an object given its swizzled ID (or null). template -ObjectType* Configurator::unswizzle(std::size_t objectId) const +ObjectType* Configurator::unswizzle(SwizzleId objectId) const { auto it = m_swizzleBck.find(objectId); if (it == m_swizzleBck.end()) diff --git a/smtk/task/json/Helper.cxx b/smtk/task/json/Helper.cxx index bb9c1c9d9f..a2d9295471 100644 --- a/smtk/task/json/Helper.cxx +++ b/smtk/task/json/Helper.cxx @@ -13,11 +13,12 @@ #include "smtk/io/Logger.h" #include +#include namespace { std::mutex g_types; -thread_local std::unique_ptr g_instance; +thread_local std::vector> g_instanceStack; } // anonymous namespace namespace smtk @@ -37,11 +38,38 @@ Helper::~Helper() = default; Helper& Helper::instance() { - if (!g_instance) + if (g_instanceStack.empty()) { - g_instance = std::unique_ptr(new Helper); + g_instanceStack.emplace_back(new Helper); } - return *g_instance; + return *(g_instanceStack.back()); +} + +Helper& Helper::pushInstance(smtk::task::Task* parent) +{ + std::shared_ptr managers; + if (!g_instanceStack.empty()) + { + managers = g_instanceStack.back()->managers(); + } + g_instanceStack.emplace_back(new Helper); + g_instanceStack.back()->setManagers(managers); + g_instanceStack.back()->tasks().swizzleId(parent); + g_instanceStack.back()->m_topLevel = false; + return *(g_instanceStack.back()); +} + +void Helper::popInstance() +{ + if (!g_instanceStack.empty()) + { + g_instanceStack.pop_back(); + } +} + +std::size_t Helper::nestingDepth() +{ + return g_instanceStack.size(); } Configurator& Helper::tasks() @@ -77,7 +105,7 @@ Helper::json Helper::swizzleDependencies(const Task::PassedDependencies& deps) { if (dep) { - std::size_t id = m_tasks.swizzleId(const_cast(dep.get())); + SwizzleId id = m_tasks.swizzleId(const_cast(dep.get())); if (id) { ids.push_back(id); @@ -92,7 +120,7 @@ Task::PassedDependencies Helper::unswizzleDependencies(const json& ids) const Task::PassedDependencies deps; for (const auto& id : ids) { - auto taskId = id.get(); + auto taskId = id.get(); auto* ptr = m_tasks.unswizzle(id); if (!ptr) { @@ -104,7 +132,7 @@ Task::PassedDependencies Helper::unswizzleDependencies(const json& ids) const return deps; } -void Helper::setAdaptorTaskIds(std::size_t fromId, std::size_t toId) +void Helper::setAdaptorTaskIds(SwizzleId fromId, SwizzleId toId) { m_adaptorFromId = fromId; m_adaptorToId = toId; @@ -112,27 +140,16 @@ void Helper::setAdaptorTaskIds(std::size_t fromId, std::size_t toId) void Helper::clearAdaptorTaskIds() { - m_adaptorFromId = ~0; - m_adaptorToId = ~0; + m_adaptorFromId = ~static_cast(0); + m_adaptorToId = ~static_cast(0); } -std::pair Helper::getAdaptorTasks() +std::pair Helper::getAdaptorTasks() { Task::Ptr fromPtr; auto* from = m_tasks.unswizzle(m_adaptorFromId); - if (from) - { - fromPtr = from->shared_from_this(); - } - - Task::Ptr toPtr; auto* to = m_tasks.unswizzle(m_adaptorToId); - if (to) - { - toPtr = to->shared_from_this(); - } - - return std::make_pair(fromPtr, toPtr); + return std::make_pair(from, to); } } // namespace json diff --git a/smtk/task/json/Helper.h b/smtk/task/json/Helper.h index 998045823d..b8bd188db7 100644 --- a/smtk/task/json/Helper.h +++ b/smtk/task/json/Helper.h @@ -35,15 +35,39 @@ namespace json class SMTKCORE_EXPORT Helper { public: + /// Swizzle IDs are serializable substitutes for pointers. + using SwizzleId = Configurator::SwizzleId; /// JSON data type using json = nlohmann::json; /// Destructor is public, but you shouldn't use it. ~Helper(); - /// Return the helper singleton. + /// Return the helper "singleton". + /// + /// The object returned is a per-thread instance + /// at the top of a stack that may be altered using + /// the pushInstance() and popInstance() methods. + /// This allows nested deserializers to each have + /// their own context that appears to be globally + /// available. static Helper& instance(); + /// Push a new helper instance on the local thread's stack. + /// + /// The returned \a Helper will have: + /// + The same managers as the previous (if any) helper. + /// + The \a parent task is assigned the ID 0. + static Helper& pushInstance(smtk::task::Task* parent); + + /// Pop a helper instance off the local thread's stack. + static void popInstance(); + + /// Return the nesting level (i.e., the number of helper instances in the stack). + /// + /// The outermost helper will return 1 (assuming you have called instance() first). + static std::size_t nestingDepth(); + /// Return an object for registering task classes and serialization helpers. Configurator& tasks(); @@ -58,6 +82,13 @@ public: void setManagers(const smtk::common::Managers::Ptr& managers); smtk::common::Managers::Ptr managers(); + /// Reset only negative task IDs. + /// + /// Negative task IDs are used to when deserializing a Group task's + /// children. Because only a single Group is deserialized at a time + /// (per thread), there are no namespace collisions. + void clearGroupTaskIds(); + /// Reset the helper's state. /// /// This should be called before beginning serialization or deserialization. @@ -75,17 +106,30 @@ public: /// Return a deserialization of de-swizzled task-references. Task::PassedDependencies unswizzleDependencies(const json& ids) const; - void setAdaptorTaskIds(std::size_t fromId, std::size_t toId); + /// Returns true if the helper is for deserializing top-level or child tasks. + bool topLevel() const { return m_topLevel; } + + /// Set/clear a pair of task IDs used when deserializing a single adaptor. + /// + /// Storing these IDs as state in the helper allows `from_json()` to + /// be "stateless" (i.e., taking no additional parameters and using + /// the Helper as a side effect). + void setAdaptorTaskIds(SwizzleId fromId, SwizzleId toId); void clearAdaptorTaskIds(); - std::pair getAdaptorTasks(); + /// Get task-pointers based on the IDs set earlier. + std::pair getAdaptorTasks(); protected: Helper(); Configurator m_tasks; Configurator m_adaptors; smtk::common::Managers::Ptr m_managers; - std::size_t m_adaptorFromId = ~0; - std::size_t m_adaptorToId = ~0; + SwizzleId m_adaptorFromId = ~static_cast(0); + SwizzleId m_adaptorToId = ~static_cast(0); + /// m_topLevel indicates whether pushInstance() (false) or instance() (true) + /// was used to create this helper. If m_topLevel is false, the parent task + /// is assigned swizzle ID 1. + bool m_topLevel = true; }; } // namespace json diff --git a/smtk/task/json/jsonAdaptor.cxx b/smtk/task/json/jsonAdaptor.cxx index b4617eecf4..84fdc2a161 100644 --- a/smtk/task/json/jsonAdaptor.cxx +++ b/smtk/task/json/jsonAdaptor.cxx @@ -28,8 +28,8 @@ Adaptor::Configuration jsonAdaptor::operator()(const Adaptor* adaptor, Helper& h { config = { { "id", helper.adaptors().swizzleId(adaptor) }, { "type", adaptor->typeName() }, - { "from", helper.tasks().swizzleId(adaptor->from().get()) }, - { "to", helper.tasks().swizzleId(adaptor->to().get()) } }; + { "from", helper.tasks().swizzleId(adaptor->from()) }, + { "to", helper.tasks().swizzleId(adaptor->to()) } }; } return config; } diff --git a/smtk/task/json/jsonFillOutAttributes.cxx b/smtk/task/json/jsonFillOutAttributes.cxx index 78f2c1d7da..bb5421d883 100644 --- a/smtk/task/json/jsonFillOutAttributes.cxx +++ b/smtk/task/json/jsonFillOutAttributes.cxx @@ -75,8 +75,7 @@ Task::Configuration jsonFillOutAttributes::operator()(const Task* task, Helper& config = superclass(fillOutAttributes, helper); nlohmann::json::array_t attributeSets; fillOutAttributes->visitAttributeSets( - [&attributeSets, fillOutAttributes]( - const FillOutAttributes::AttributeSet& attributeSet) -> smtk::common::Visit { + [&attributeSets](const FillOutAttributes::AttributeSet& attributeSet) -> smtk::common::Visit { nlohmann::json jsonAttributeSet = attributeSet; attributeSets.push_back(jsonAttributeSet); return smtk::common::Visit::Continue; diff --git a/smtk/task/json/jsonGatherResources.cxx b/smtk/task/json/jsonGatherResources.cxx index 07d2d04bc9..f6af8f7296 100644 --- a/smtk/task/json/jsonGatherResources.cxx +++ b/smtk/task/json/jsonGatherResources.cxx @@ -13,10 +13,118 @@ #include "smtk/task/GatherResources.h" +#include "smtk/io/Logger.h" + namespace smtk { namespace task { + +void from_json(const nlohmann::json& j, GatherResources::ResourceSet& resourceSet) +{ + if (j.contains("role")) + { + j.at("role").get_to(resourceSet.m_role); + } + if (j.contains("type")) + { + j.at("type").get_to(resourceSet.m_type); + } + if (j.contains("min")) + { + j.at("min").get_to(resourceSet.m_minimumCount); + } + else + { + resourceSet.m_minimumCount = 1; + } + if (j.contains("max")) + { + j.at("max").get_to(resourceSet.m_maximumCount); + } + else + { + resourceSet.m_maximumCount = -1; + } + if (j.contains("validator")) + { + throw std::logic_error("todo"); // TODO + } + else + { + // Accept any resource + resourceSet.m_validator = nullptr; + /* + [](const smtk::resource::Resource&, const TaskNeedsResource::ResourceSet&) + { return true; }; + */ + } + if (j.contains("resource-ids")) + { + auto resourceIds = j.at("resource-ids"); + bool warnOnIds = !resourceIds.is_array() || !resourceIds.empty(); + auto managers = json::Helper::instance().managers(); + if (managers) + { + if (auto resourceManager = managers->get()) + { + for (const auto& jsonId : j.at("resource-ids")) + { + auto resource = resourceManager->get(jsonId.get()); + if (resource) + { + resourceSet.m_resources.insert(resource); + } + else + { + smtkWarningMacro( + smtk::io::Logger::instance(), "Resource \"" << jsonId << "\" not found."); + } + } + } + else + { + warnOnIds = true; + } + } + if (warnOnIds) + { + smtkWarningMacro( + smtk::io::Logger::instance(), + "Resource IDs were provided but either were in the wrong form " + "or a resource manager was unavailable."); + } + } +} + +void to_json(nlohmann::json& j, const GatherResources::ResourceSet& resourceSet) +{ + j = { + { "role", resourceSet.m_role }, + { "type", resourceSet.m_type }, + }; + if (resourceSet.m_minimumCount != 1) + { + j["min"] = resourceSet.m_minimumCount; + } + if (resourceSet.m_maximumCount != -1) + { + j["max"] = resourceSet.m_maximumCount; + } + nlohmann::json::array_t resourceIds; + for (const auto& weakResource : resourceSet.m_resources) + { + if (auto resource = weakResource.lock()) + { + resourceIds.push_back(resource->id()); + } + } + if (!resourceIds.empty()) + { + j["resource-ids"] = resourceIds; + } +} + namespace json { @@ -31,34 +139,9 @@ Task::Configuration jsonGatherResources::operator()(const Task* task, Helper& he config = superclass(gatherResources, helper); nlohmann::json::array_t resourceSets; gatherResources->visitResourceSets( - [&resourceSets, - gatherResources](const GatherResources::ResourceSet& resourceSet) -> smtk::common::Visit { - nlohmann::json jsonResourceSet = { - { "role", resourceSet.m_role }, - { "type", resourceSet.m_type }, - }; - if (resourceSet.m_minimumCount != 1) - { - jsonResourceSet["min"] = resourceSet.m_minimumCount; - } - if (resourceSet.m_maximumCount != -1) - { - jsonResourceSet["max"] = resourceSet.m_maximumCount; - } + [&resourceSets](const GatherResources::ResourceSet& resourceSet) -> smtk::common::Visit { + nlohmann::json jsonResourceSet = resourceSet; resourceSets.push_back(jsonResourceSet); - - nlohmann::json jsonOutput = { - { "role", resourceSet.m_role }, - }; - nlohmann::json::array_t jsonResources; - for (const auto& weakResource : resourceSet.m_resources) - { - if (auto resource = weakResource.lock()) - { - jsonResources.push_back(resource->id().toString()); - } - } - jsonOutput["resources"] = jsonResources; return smtk::common::Visit::Continue; }); config["resources"] = resourceSets; diff --git a/smtk/task/json/jsonGatherResources.h b/smtk/task/json/jsonGatherResources.h index f0b67f13fa..d83cfd96be 100644 --- a/smtk/task/json/jsonGatherResources.h +++ b/smtk/task/json/jsonGatherResources.h @@ -10,7 +10,7 @@ #ifndef smtk_task_json_GatherResources_h #define smtk_task_json_GatherResources_h -#include "smtk/task/Task.h" +#include "smtk/task/GatherResources.h" #include #include @@ -19,6 +19,10 @@ namespace smtk { namespace task { + +void from_json(const nlohmann::json& j, GatherResources::ResourceSet& resourceSet); +void to_json(nlohmann::json& j, const GatherResources::ResourceSet& resourceSet); + namespace json { diff --git a/smtk/task/json/jsonGroup.cxx b/smtk/task/json/jsonGroup.cxx index 84691d5c97..beff672a5a 100644 --- a/smtk/task/json/jsonGroup.cxx +++ b/smtk/task/json/jsonGroup.cxx @@ -9,6 +9,7 @@ //========================================================================= #include "smtk/task/json/jsonGroup.h" #include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonAdaptor.h" #include "smtk/task/json/jsonTask.h" #include "smtk/task/Group.h" @@ -29,13 +30,30 @@ Task::Configuration jsonGroup::operator()(const Task* task, Helper& helper) cons { jsonTask superclass; config = superclass(group, helper); + config["adaptor-data"] = group->adaptorData(); + // Now that we've serialized the parent task, + // push a helper on the stack to serialize children. + auto& childHelper = smtk::task::json::Helper::pushInstance(group); nlohmann::json::array_t children; for (const auto& child : group->children()) { + childHelper.tasks().swizzleId(child.get()); nlohmann::json jsonChild = child; - children.push_back(jsonChild); + children.emplace_back(jsonChild); } - config["children"] = children; + nlohmann::json::array_t adaptors; + for (const auto& weakAdaptor : group->adaptors()) + { + auto adaptor = weakAdaptor.lock(); + if (adaptor) + { + childHelper.adaptors().swizzleId(adaptor.get()); + nlohmann::json jsonAdaptor = adaptor; + adaptors.emplace_back(jsonAdaptor); + } + } + config["children"] = { { "tasks", children }, { "adaptors", adaptors } }; + smtk::task::json::Helper::popInstance(); } return config; } diff --git a/smtk/task/json/jsonManager.cxx b/smtk/task/json/jsonManager.cxx index ff170276ac..be84312c72 100644 --- a/smtk/task/json/jsonManager.cxx +++ b/smtk/task/json/jsonManager.cxx @@ -47,8 +47,17 @@ bool jsonManager::serialize( // Serialize tasks nlohmann::json::array_t taskList; taskManager->taskInstances().visit([&taskList](const smtk::task::Task::Ptr& task) { - nlohmann::json jsonTask = task; - taskList.push_back(jsonTask); + auto& helper = Helper::instance(); + if ( + !task->parent() || + (smtk::task::json::Helper::nestingDepth() > 1 && + helper.tasks().unswizzle(1) == task->parent())) + { + // Only serialize top-level tasks. (Tasks with children are responsible + // for serializing their children). + nlohmann::json jsonTask = task; + taskList.push_back(jsonTask); + } return smtk::common::Visit::Continue; }); json["tasks"] = taskList; @@ -56,8 +65,15 @@ bool jsonManager::serialize( // Serialize adaptors nlohmann::json::array_t adaptorList; taskManager->adaptorInstances().visit([&adaptorList](const smtk::task::Adaptor::Ptr& adaptor) { - nlohmann::json jsonAdaptor = adaptor; - adaptorList.push_back(jsonAdaptor); + if ( + adaptor && adaptor->from() && !adaptor->from()->parent() && adaptor->to() && + !adaptor->to()->parent()) + { + // Only serialize top-level adaptors. (Tasks with child adaptors are + // responsible for serializing them.) + nlohmann::json jsonAdaptor = adaptor; + adaptorList.push_back(jsonAdaptor); + } return smtk::common::Visit::Continue; }); json["adaptors"] = adaptorList; @@ -67,6 +83,17 @@ bool jsonManager::serialize( bool jsonManager::deserialize( const std::shared_ptr& managers, const nlohmann::json& json) +{ + std::vector> tasks; + std::vector> adaptors; + return jsonManager::deserialize(tasks, adaptors, managers, json); +} + +bool jsonManager::deserialize( + std::vector>& tasks, + std::vector>& adaptors, + const std::shared_ptr& managers, + const nlohmann::json& json) { auto taskManager = managers->get(); if (!taskManager) @@ -76,25 +103,32 @@ bool jsonManager::deserialize( return false; } + tasks.clear(); + adaptors.clear(); try { auto& helper = Helper::instance(); - helper.clear(); + if (smtk::task::json::Helper::nestingDepth() == 1) + { // Do not clear the parent task when deserializing nested tasks. + helper.clear(); + } helper.setManagers(managers); - std::map taskMap; + std::map taskMap; + std::map adaptorMap; for (const auto& jsonTask : json.at("tasks")) { - auto taskId = jsonTask.at("id").get(); + auto taskId = jsonTask.at("id").get(); Task::Ptr task = jsonTask; taskMap[taskId] = task; helper.tasks().swizzleId(task.get()); + tasks.push_back(task); } // Do a second pass to deserialize dependencies. for (const auto& jsonTask : json.at("tasks")) { if (jsonTask.contains("dependencies")) { - auto taskId = jsonTask.at("id").get(); + auto taskId = jsonTask.at("id").get(); auto task = taskMap[taskId]; auto taskDeps = helper.unswizzleDependencies(jsonTask.at("dependencies")); task->addDependencies(taskDeps); @@ -107,20 +141,26 @@ bool jsonManager::deserialize( { for (const auto& jsonAdaptor : json.at("adaptors")) { - try - { - auto adaptorId = jsonAdaptor.at("id").get(); - auto taskFromId = jsonAdaptor.at("from").get(); - auto taskToId = jsonAdaptor.at("to").get(); - helper.setAdaptorTaskIds(taskFromId, taskToId); - Adaptor::Ptr adaptor = jsonAdaptor; - helper.clearAdaptorTaskIds(); - } - catch (std::exception&) + // Skip things that are not adaptors. + if (jsonAdaptor.is_object() && jsonAdaptor.contains("id")) { - smtkErrorMacro( - smtk::io::Logger::instance(), - "Skipping task because 'id', 'from', and/or 'to' fields are missing."); + try + { + auto adaptorId = jsonAdaptor.at("id").get(); + auto taskFromId = jsonAdaptor.at("from").get(); + auto taskToId = jsonAdaptor.at("to").get(); + helper.setAdaptorTaskIds(taskFromId, taskToId); + Adaptor::Ptr adaptor = jsonAdaptor; + adaptorMap[adaptorId] = adaptor; + helper.adaptors().swizzleId(adaptor.get()); + adaptors.push_back(adaptor); + } + catch (std::exception&) + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Skipping task because 'id', 'from', and/or 'to' fields are missing."); + } } } } diff --git a/smtk/task/json/jsonManager.h b/smtk/task/json/jsonManager.h index 211fd8e1c5..59c0b7b214 100644 --- a/smtk/task/json/jsonManager.h +++ b/smtk/task/json/jsonManager.h @@ -41,7 +41,16 @@ public: /// task manager. Depending on the task instances being deserialized, /// this method may access and modify other managers held by the /// \a managers instance. + /// + /// The second variant accepts a container holding weak pointers + /// to each top-level task deserialized (i.e., child tasks are + /// not included). + static bool deserialize( + const std::shared_ptr& managers, + const nlohmann::json& json); static bool deserialize( + std::vector>& tasks, + std::vector>& adaptors, const std::shared_ptr& managers, const nlohmann::json& json); }; diff --git a/smtk/task/json/jsonTask.cxx b/smtk/task/json/jsonTask.cxx index f898bdd556..5117f1174a 100644 --- a/smtk/task/json/jsonTask.cxx +++ b/smtk/task/json/jsonTask.cxx @@ -29,6 +29,10 @@ Task::Configuration jsonTask::operator()(const Task* task, Helper& helper) const config["id"] = helper.tasks().swizzleId(task); config["type"] = task->typeName(); config["title"] = task->title(); + if (!task->style().empty()) + { + config["style"] = task->style(); + } config["state"] = stateName(task->internalState()); auto deps = helper.swizzleDependencies(task->dependencies()); if (!deps.empty()) diff --git a/smtk/task/pybind11/PybindFillOutAttributes.h b/smtk/task/pybind11/PybindFillOutAttributes.h index 3b5c121691..5d69b9cb63 100644 --- a/smtk/task/pybind11/PybindFillOutAttributes.h +++ b/smtk/task/pybind11/PybindFillOutAttributes.h @@ -31,7 +31,9 @@ inline PySharedPtrClass< smtk::task::FillOutAttributes, smtk::task::Task > pybin .def_static("create", (std::shared_ptr (*)()) &smtk::task::FillOutAttributes::create) .def_static("create", (std::shared_ptr (*)(::std::shared_ptr &)) &smtk::task::FillOutAttributes::create, py::arg("ref")) .def("typeName", &smtk::task::FillOutAttributes::typeName) - .def("visitAttributeSets", &smtk::task::FillOutAttributes::visitAttributeSets, py::arg("visitor")) + .def("visitAttributeSets", (smtk::common::Visit (smtk::task::FillOutAttributes::*)(::smtk::task::FillOutAttributes::ConstAttributeSetVisitor) const) &smtk::task::FillOutAttributes::visitAttributeSets, py::arg("visitor")) + .def("visitAttributeSets", (smtk::common::Visit (smtk::task::FillOutAttributes::*)(::smtk::task::FillOutAttributes::AttributeSetVisitor)) &smtk::task::FillOutAttributes::visitAttributeSets, py::arg("visitor")) + .def_readonly_static("type_name", &smtk::task::FillOutAttributes::type_name) ; py::class_< smtk::task::FillOutAttributes::AttributeSet >(instance, "AttributeSet") diff --git a/smtk/task/testing/cxx/CMakeLists.txt b/smtk/task/testing/cxx/CMakeLists.txt index d40f91337e..34fe555948 100644 --- a/smtk/task/testing/cxx/CMakeLists.txt +++ b/smtk/task/testing/cxx/CMakeLists.txt @@ -1,6 +1,7 @@ set(unit_tests TestActiveTask.cxx TestTaskBasics.cxx + TestTaskGroup.cxx TestTaskJSON.cxx ) diff --git a/smtk/task/testing/cxx/TestActiveTask.cxx b/smtk/task/testing/cxx/TestActiveTask.cxx index d729462943..d129c0805d 100644 --- a/smtk/task/testing/cxx/TestActiveTask.cxx +++ b/smtk/task/testing/cxx/TestActiveTask.cxx @@ -105,6 +105,7 @@ int TestActiveTask(int, char*[]) // resource manager. Task::Configuration c4{ { "title", "Task 4" }, + { "auto-configure", true }, { "resources", { { { "role", "model geometry" }, { "type", "smtk::model::Resource" }, { "max", 2 } }, { { "role", "simulation attribute" }, { "type", "smtk::attribute::Resource" } } } } diff --git a/smtk/task/testing/cxx/TestTaskBasics.cxx b/smtk/task/testing/cxx/TestTaskBasics.cxx index 8250a4dc46..6aded37e69 100644 --- a/smtk/task/testing/cxx/TestTaskBasics.cxx +++ b/smtk/task/testing/cxx/TestTaskBasics.cxx @@ -158,6 +158,26 @@ int TestTaskBasics(int, char*[]) auto t3 = taskManager->taskInstances().create( c3, std::set>{ { t1, t2 } }, managers); + // Test visitors. + called = 0; + test(!t3->hasChildren(), "Expected t3 to have no children."); + t3->visit(Task::RelatedTasks::Child, [&called](Task&) { + ++called; + return smtk::common::Visit::Continue; + }); + test(called == 0, "Expected not to visit any children of t3."); + t3->visit(Task::RelatedTasks::Depend, [&called](Task&) { + ++called; + return smtk::common::Visit::Continue; + }); + test(called == 2, "Expected to visit 2 dependencies of t3."); + called = 0; + t3->visit(Task::RelatedTasks::Depend, [&called](Task&) -> smtk::common::Visit { + ++called; + return smtk::common::Visit::Halt; + }); + test(called == 1, "Expected to terminate early for dependency visitor."); + // Test dependency removal and notification. auto dokey = t3->observers().insert(callback); called = 0; @@ -180,6 +200,7 @@ int TestTaskBasics(int, char*[]) // resource manager. Task::Configuration c4{ { "title", "Task 4" }, + { "auto-configure", true }, { "resources", { { { "role", "model geometry" }, { "type", "smtk::model::Resource" }, { "max", 2 } }, { { "role", "simulation attribute" }, { "type", "smtk::attribute::Resource" } } } } @@ -244,6 +265,7 @@ int TestTaskBasics(int, char*[]) // This should also test initialization of GatherResources when // resource manager is not empty. Task::Configuration c5{ { "title", "Task 5" }, + { "auto-configure", true }, { "resources", { { { "type", "smtk::model::Resource" } }, { { "role", "simulation attribute" } } } } }; diff --git a/smtk/task/testing/cxx/TestTaskGroup.cxx b/smtk/task/testing/cxx/TestTaskGroup.cxx index d274425498..8f5619f601 100644 --- a/smtk/task/testing/cxx/TestTaskGroup.cxx +++ b/smtk/task/testing/cxx/TestTaskGroup.cxx @@ -7,70 +7,48 @@ // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. //========================================================================= +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/ComponentItem.h" +#include "smtk/attribute/Definition.h" +#include "smtk/attribute/ReferenceItem.h" +#include "smtk/attribute/ReferenceItemDefinition.h" #include "smtk/attribute/Registrar.h" #include "smtk/attribute/Resource.h" +#include "smtk/attribute/operators/Signal.h" #include "smtk/common/Managers.h" #include "smtk/model/Registrar.h" #include "smtk/model/Resource.h" +#include "smtk/model/Volume.h" #include "smtk/operation/Manager.h" #include "smtk/operation/Registrar.h" #include "smtk/plugin/Registry.h" #include "smtk/resource/Manager.h" #include "smtk/resource/Registrar.h" +#include "smtk/task/FillOutAttributes.h" +#include "smtk/task/GatherResources.h" +#include "smtk/task/Group.h" +#include "smtk/task/Instances.h" #include "smtk/task/Manager.h" #include "smtk/task/Registrar.h" #include "smtk/task/Task.h" -// #include "smtk/task/GatherResources.h" #include "smtk/task/json/jsonManager.h" #include "smtk/task/json/jsonTask.h" #include "smtk/common/testing/cxx/helpers.h" -namespace test_task +namespace { -using namespace smtk::task; - -} // namespace test_task - -int TestTaskGroup(int, char*[]) +std::string configString = R"( { - using smtk::task::State; - using smtk::task::Task; - - // Create managers - auto managers = smtk::common::Managers::create(); - auto attributeRegistry = smtk::plugin::addToManagers(managers); - auto resourceRegistry = smtk::plugin::addToManagers(managers); - auto operationRegistry = smtk::plugin::addToManagers(managers); - auto taskRegistry = smtk::plugin::addToManagers(managers); - - auto resourceManager = managers->get(); - auto operationManager = managers->get(); - auto taskManager = managers->get(); - - auto attributeResourceRegistry = - smtk::plugin::addToManagers(resourceManager); - auto modelRegistry = smtk::plugin::addToManagers(resourceManager); - auto taskTaskRegistry = smtk::plugin::addToManagers(taskManager); - - auto attrib = resourceManager->create(); - auto model = resourceManager->create(); - attrib->setName("simulation"); - model->setName("geometry"); - attrib->properties().get()["project_role"] = "simulation attribute"; - model->properties().get()["project_role"] = "model geometry"; - auto attribUUIDStr = attrib->id().toString(); - auto modelUUIDStr = model->id().toString(); - - std::string configString = R"({ "tasks": [ { "id": 1, "type": "smtk::task::GatherResources", "title": "Load a model and attribute", "state": "completed", + "auto-configure": true, "resources": [ { "role": "model geometry", @@ -81,26 +59,20 @@ int TestTaskGroup(int, char*[]) "role": "simulation attribute", "type": "smtk::attribute::Resource" } - ], - "output": [ - { "role": "model geometry", "resources": [ "feedface-0000-0000-0000-000000000000" ] }, - { "role": "simulation attribute", "resources": [ "deadbeef-0000-0000-0000-000000000000" ] } ] }, { "id": 2, "type": "smtk::task::Group", + "mode": "parallel", "title": "Prepare simulation", - "state": "incomplete", "dependencies": [ 1 ], - "grouping": { - "mode": "parallel" - "children": [ + "children": { + "tasks": [ { - "id": -1, + "id": 2, "type": "smtk::task::FillOutAttributes", "title": "Mark up model", - "state": "incomplete", "attribute-sets": [ { "role": "simulation attribute", @@ -112,15 +84,14 @@ int TestTaskGroup(int, char*[]) ] }, { - "id": -2, + "id": 3, "type": "smtk::task::FillOutAttributes", - "title": "Specify simulation parameters", - "state": "incomplete", + "title": "Set simulation parameters", "attribute-sets": [ { "role": "simulation attribute", "definitions": [ - "Globals" + "SimulationParameters" ] } ] @@ -128,16 +99,16 @@ int TestTaskGroup(int, char*[]) ], "adaptors": [ { - "id": -1, + "id": 1, "type": "smtk::task::adaptor::ResourceAndRole", - "from": 2, - "to": -1 + "from": 1, + "to": 2 }, { - "id": -1, + "id": 2, "type": "smtk::task::adaptor::ResourceAndRole", - "from": 2, - "to": -2 + "from": 1, + "to": 3 } ] } @@ -153,6 +124,7 @@ int TestTaskGroup(int, char*[]) ], "adaptors": [ { + "id": 1, "type": "smtk::task::adaptor::ResourceAndRole", "from": 1, "to": 2 @@ -160,25 +132,157 @@ int TestTaskGroup(int, char*[]) ] } )"; - auto cursor = configString.find("deadbeef", 0); - configString = configString.replace(cursor, attribUUIDStr.length(), attribUUIDStr); - cursor = configString.find("feedface", 0); - configString = configString.replace(cursor, modelUUIDStr.length(), modelUUIDStr); +void printTaskStates(smtk::task::Manager::Ptr taskManager, const std::string& message) +{ + std::cout << "### " << message << " ###\n"; + int depth = 0; + smtk::task::Task* parent = nullptr; + std::function childVisitor; + childVisitor = [&parent, &depth, &childVisitor](smtk::task::Task& task) { + if (&task == parent) + { + return smtk::common::Visit::Continue; // Skip self when visiting children + } + std::string indent(depth * 2, ' '); + std::cout << indent << &task << " : children? " << (task.hasChildren() ? "Y" : "N") + << " parent " << task.parent() << " state " << smtk::task::stateName(task.state()) + << " " << task.title() << "\n"; + if (task.hasChildren()) + { + parent = &task; + ++depth; + task.visit(smtk::task::Task::RelatedTasks::Child, childVisitor); + --depth; + parent = nullptr; + } + return smtk::common::Visit::Continue; + }; + taskManager->taskInstances().visit( + [&childVisitor](const std::shared_ptr& task) { + if (!task->parent()) + { + childVisitor(*task); + } + return smtk::common::Visit::Continue; + }); +} + +} // anonymous namespace + +int TestTaskGroup(int, char*[]) +{ + using smtk::task::State; + using smtk::task::Task; + + // Create managers + auto managers = smtk::common::Managers::create(); + auto attributeRegistry = smtk::plugin::addToManagers(managers); + auto resourceRegistry = smtk::plugin::addToManagers(managers); + auto operationRegistry = smtk::plugin::addToManagers(managers); + auto taskRegistry = smtk::plugin::addToManagers(managers); + + auto resourceManager = managers->get(); + auto operationManager = managers->get(); + auto taskManager = managers->get(); + + auto attributeResourceRegistry = + smtk::plugin::addToManagers(resourceManager); + auto attributeOperationRegistry = + smtk::plugin::addToManagers(operationManager); + auto modelRegistry = smtk::plugin::addToManagers(resourceManager); + auto taskTaskRegistry = smtk::plugin::addToManagers(taskManager); + + auto attrib = resourceManager->create(); + auto model = resourceManager->create(); + attrib->setName("simulation"); + model->setName("geometry"); + attrib->properties().get()["project_role"] = "simulation attribute"; + model->properties().get()["project_role"] = "model geometry"; + auto attribUUIDStr = attrib->id().toString(); + auto modelUUIDStr = model->id().toString(); auto config = nlohmann::json::parse(configString); std::cout << config.dump(2) << "\n"; bool ok = smtk::task::json::jsonManager::deserialize(managers, config); test(ok, "Failed to parse configuration."); - test(taskManager->taskInstances().size() == 2, "Expected to deserialize 2 tasks."); + test(taskManager->taskInstances().size() == 4, "Expected to deserialize 4 tasks."); + + smtk::task::GatherResources::Ptr gatherResources; + smtk::task::FillOutAttributes::Ptr fillOutAttributes; + taskManager->taskInstances().visit( + [&gatherResources, &fillOutAttributes](const smtk::task::Task::Ptr& task) { + if (!gatherResources) + { + gatherResources = std::dynamic_pointer_cast(task); + } + if (!fillOutAttributes) + { + fillOutAttributes = std::dynamic_pointer_cast(task); + } + return smtk::common::Visit::Continue; + }); + + // Add components and signal to test FillOutAttributes. + // First, the FillOutAttributes task is irrelevant + printTaskStates(taskManager, "Incomplete resource gathering."); + gatherResources->markCompleted(true); + + printTaskStates(taskManager, "Completed resource gathering."); + + auto volume1 = model->addVolume(); + auto volume2 = model->addVolume(); + auto def = attrib->createDefinition("Material"); + auto assoc = def->createLocalAssociationRule(); + assoc->setAcceptsEntries("smtk::model::Resource", "volume", true); + assoc->setNumberOfRequiredValues(1); + assoc->setIsExtensible(true); + auto material1 = attrib->createAttribute("CarbonFiber", "Material"); + + // Signal the change + auto signal = operationManager->create(); + signal->parameters()->findComponent("created")->appendValue(material1); + auto result = signal->operate(); + // Now, the FillOutAttributes task is incomplete + printTaskStates(taskManager, "Added a material attribute."); - // Round trip it. - nlohmann::json config2; - ok = smtk::task::json::jsonManager::serialize(managers, config2); - test(ok, "Failed to serialize task manager."); - std::cout << config2.dump(2) << "\n"; + material1->associate(volume1.component()); + signal->parameters()->findComponent("created")->setNumberOfValues(0); + signal->parameters()->findComponent("modified")->appendValue(material1); + result = signal->operate(); + // Finally, the FillOutAttributes task is completable + printTaskStates(taskManager, "Associated material to model."); - // TODO: Round trip a second time so we should be guaranteed to have - // two machine-(not-hand-)generated strings to compare. + std::string configString2; + { + // Round trip it. + nlohmann::json config2; + ok = smtk::task::json::jsonManager::serialize(managers, config2); + test(ok, "Failed to serialize task manager."); + configString2 = config2.dump(2); + std::cout << configString2 << "\n"; + } + { + // Round trip a second time so we should be guaranteed to have + // two machine-(not-hand-)generated strings to compare. + auto managers2 = smtk::common::Managers::create(); + managers2->insert(resourceManager); + managers2->insert(operationManager); + auto taskManager2 = smtk::task::Manager::create(); + smtk::task::Registrar::registerTo(taskManager2); + managers2->insert(taskManager2); + auto config3 = nlohmann::json::parse(configString2); + ok = smtk::task::json::jsonManager::deserialize(managers2, config3); + test(ok, "Failed to parse second configuration."); + test(taskManager2->taskInstances().size() == 4, "Expected to deserialize 4 tasks."); + nlohmann::json config4; + ok = smtk::task::json::jsonManager::serialize(managers2, config4); + test(ok, "Failed to serialize second manager."); + std::string configString3 = config4.dump(2); + std::cout << "----\n" << configString3 << "\n"; + // TODO: The valid/invalid attribute UUIDs in FillOutAttributes's serialization + // are not being preserved. + // test(configString2 == configString3, "Failed to match strings."); + } return 0; } diff --git a/smtk/task/testing/cxx/TestTaskJSON.cxx b/smtk/task/testing/cxx/TestTaskJSON.cxx index 55b954b803..5cb9169573 100644 --- a/smtk/task/testing/cxx/TestTaskJSON.cxx +++ b/smtk/task/testing/cxx/TestTaskJSON.cxx @@ -61,6 +61,7 @@ int TestTaskJSON(int, char*[]) { using smtk::task::State; using smtk::task::Task; + using smtk::task::WorkflowEvent; // Create managers auto managers = smtk::common::Managers::create(); @@ -80,6 +81,18 @@ int TestTaskJSON(int, char*[]) auto modelRegistry = smtk::plugin::addToManagers(resourceManager); auto taskTaskRegistry = smtk::plugin::addToManagers(taskManager); + smtk::task::Instances::WorkflowObserver wfObserver = + [&](const std::set& workflows, WorkflowEvent event, Task* subject) { + std::cout << "-- Workflow event " << static_cast(event) << " subject: " << subject + << " workflows:\n"; + for (const auto& wftask : workflows) + { + std::cout << "-- head task " << wftask->title() << "\n"; + } + std::cout << "--\n"; + }; + auto workflowObserver = taskManager->taskInstances().workflowObservers().insert(wfObserver); + auto attrib = resourceManager->create(); auto model = resourceManager->create(); attrib->setName("simulation"); @@ -95,7 +108,9 @@ int TestTaskJSON(int, char*[]) "id": 1, "type": "smtk::task::GatherResources", "title": "Load a model and attribute", + "style": [ "foo", "bar", "baz" ], "state": "completed", + "auto-configure": true, "resources": [ { "role": "model geometry", @@ -106,10 +121,6 @@ int TestTaskJSON(int, char*[]) "role": "simulation attribute", "type": "smtk::attribute::Resource" } - ], - "output": [ - { "role": "model geometry", "resources": [ "feedface-0000-0000-0000-000000000000" ] }, - { "role": "simulation attribute", "resources": [ "deadbeef-0000-0000-0000-000000000000" ] } ] }, { @@ -144,17 +155,20 @@ int TestTaskJSON(int, char*[]) "from": 1, "to": 2 } - ] + ], + "styles": { + "foo": { }, + "bar": { }, + "baz": { } + } } )"; - auto cursor = configString.find("deadbeef", 0); - configString = configString.replace(cursor, attribUUIDStr.length(), attribUUIDStr); - cursor = configString.find("feedface", 0); - configString = configString.replace(cursor, modelUUIDStr.length(), modelUUIDStr); auto config = nlohmann::json::parse(configString); std::cout << config.dump(2) << "\n"; + taskManager->taskInstances().pauseWorkflowNotifications(true); bool ok = smtk::task::json::jsonManager::deserialize(managers, config); + taskManager->taskInstances().pauseWorkflowNotifications(false); test(ok, "Failed to parse configuration."); test(taskManager->taskInstances().size() == 2, "Expected to deserialize 2 tasks."); @@ -173,6 +187,10 @@ int TestTaskJSON(int, char*[]) return smtk::common::Visit::Continue; }); + // Test that styles are properly deserialized. + test(fillOutAttributes->style().empty(), "Expected no style for fillOutAttributes."); + test(gatherResources->style().size() == 3, "Expected 3 style class-names for gatherResources."); + // Add components and signal to test FillOutAttributes. // First, the FillOutAttributes task is irrelevant printTaskStates(taskManager); @@ -227,6 +245,26 @@ int TestTaskJSON(int, char*[]) ok = smtk::task::json::jsonManager::deserialize(managers2, config3); test(ok, "Failed to parse second configuration."); test(taskManager2->taskInstances().size() == 2, "Expected to deserialize 2 tasks."); + + // Test that styles are properly round-tripped. + smtk::task::GatherResources::Ptr gatherResources2; + smtk::task::FillOutAttributes::Ptr fillOutAttributes2; + taskManager->taskInstances().visit( + [&gatherResources2, &fillOutAttributes2](const smtk::task::Task::Ptr& task) { + if (!gatherResources2) + { + gatherResources2 = std::dynamic_pointer_cast(task); + } + if (!fillOutAttributes2) + { + fillOutAttributes2 = std::dynamic_pointer_cast(task); + } + return smtk::common::Visit::Continue; + }); + test(fillOutAttributes2->style().empty(), "Expected no style for fillOutAttributes2."); + test( + gatherResources2->style().size() == 3, "Expected 3 style class-names for gatherResources2."); + nlohmann::json config4; ok = smtk::task::json::jsonManager::serialize(managers2, config4); test(ok, "Failed to serialize second manager."); -- GitLab From 1a219f93d11ae16e6fed51b2ccf12cec1bba11c0 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Fri, 27 Aug 2021 04:01:24 -0400 Subject: [PATCH 41/52] Remove documentation of `task::ConfigureOperation`... ... since it has not been implemented yet. --- doc/release/notes/task-classes.rst | 12 ++++++++++-- doc/userguide/task/classes.rst | 20 -------------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/doc/release/notes/task-classes.rst b/doc/release/notes/task-classes.rst index c071c7da6d..afa0b0ad0b 100644 --- a/doc/release/notes/task-classes.rst +++ b/doc/release/notes/task-classes.rst @@ -1,8 +1,16 @@ -New task classes -================ +New task classes and base class changes +======================================= The task subsystem now provides more task types, task-adaptor classes for configuring tasks as they change state, and additional tests. See the `task class documentation`_ for details. +Tasks now include "style" strings that will be used to configure +application state when the task becomes active. + +Tasks now include references to dependencies and dependents, +children and a parent. These are used to provide workflow +observers that user interfaces can use to monitor when tasks +are added-to and removed-from a pipeline. + .. _task class documentation: https://smtk.readthedocs.io/en/latest/userguide/task/classes.html diff --git a/doc/userguide/task/classes.rst b/doc/userguide/task/classes.rst index 19d483313b..82c235abe9 100644 --- a/doc/userguide/task/classes.rst +++ b/doc/userguide/task/classes.rst @@ -33,26 +33,6 @@ Example "completed": false } -ConfigureOperation ------------------- - -:smtk:`ConfigureOperation ` exists to -create and validate inputs to an operation. - -You can use a :smtk:`MapComponents ` -adaptor to configure the operation's associated objects. - -Example -""""""" - -.. code:: json - - { - "type": "smtk::task::ConfigureOperation", - "title": "Export simulation", - "operation": "smtk::session::oscillator::Export" - } - FillOutAttributes ----------------- -- GitLab From a9b9d561da583c97d3668dcb3f77ba876eb22ce0 Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Thu, 19 Aug 2021 21:51:23 -0400 Subject: [PATCH 42/52] Remove redundant resource load behavior Resources are already loaded by from_json() call --- smtk/project/operators/Read.cxx | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/smtk/project/operators/Read.cxx b/smtk/project/operators/Read.cxx index a1b63030c2..5ae800bc74 100644 --- a/smtk/project/operators/Read.cxx +++ b/smtk/project/operators/Read.cxx @@ -105,32 +105,6 @@ Read::Result Read::operateInternal() smtk::project::from_json(j, project); Result result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED); - - // For now, load all project resources - j = j["resources"]; - for (json::const_iterator it = j["resources"].begin(); it != j["resources"].end(); ++it) - { - boost::filesystem::path path(it->at("location").get()); - if (path.is_relative()) - { - path = projectPath / path; - } - smtk::resource::ResourcePtr resource = - project->resources().manager()->read(it->at("type").get(), path.string()); - if (!resource) - { - smtkErrorMacro( - log(), - "Cannot read resource type \"" << it->at("type").get() << "\" at location \"" - << it->at("location").get() << "\"."); - - result = this->createResult(smtk::operation::Operation::Outcome::FAILED); - continue; - } - resource->setClean(true); - project->resources().add(resource, detail::role(resource)); - } - { smtk::attribute::ResourceItem::Ptr created = result->findResource("resource"); created->setValue(project); -- GitLab From a39ea9b92609bff47e4ce1af7ae4556c7b53a00e Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Mon, 30 Aug 2021 13:10:53 -0400 Subject: [PATCH 43/52] Set ppg enitity names to alphabetically sort in numerical order For example, for 10-99 vertices, use two digits, filling in zeros: vertex 01, vertex 02, vertex 03, ... vertex 99 --- smtk/session/polygon/operators/ImportPPG.cxx | 33 +++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/smtk/session/polygon/operators/ImportPPG.cxx b/smtk/session/polygon/operators/ImportPPG.cxx index 6e1108dc21..4247068374 100644 --- a/smtk/session/polygon/operators/ImportPPG.cxx +++ b/smtk/session/polygon/operators/ImportPPG.cxx @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -133,6 +134,20 @@ struct PPGFace std::cout << std::endl; } }; + +// Returns the number of digits needed to represent a give number, e.g. for numbers < 10 only +// one digit is required. For 100 <= number < 999, three digits are needed. +unsigned int numDigitsFor(std::size_t number) +{ + int digits = 0; + while (number) + { + number /= 10; + digits++; + } + return digits; +} + } // namespace namespace smtk @@ -258,6 +273,7 @@ bool ImportPPG::Internal::createVertices() std::stringstream ss; std::vector coords(2); + unsigned digits = numDigitsFor(m_ppgVertexList.size()); for (const auto& v : m_ppgVertexList) { coords[0] = v.x; @@ -269,7 +285,7 @@ bool ImportPPG::Internal::createVertices() // Set the vertex name ss.str(std::string()); ss.clear(); - ss << "vertex " << v.userId; + ss << "vertex " << std::setfill('0') << std::setw(digits) << v.userId; newVertex.setName(ss.str()); // Update member data @@ -288,6 +304,14 @@ bool ImportPPG::Internal::createEdges() std::size_t edgeId = 0; std::stringstream ss; + // Get upper limit on number of edges + std::size_t numEdges = 0; + for (auto& ppgFace : m_ppgFaceList) + { + numEdges += ppgFace.vertexIds.size(); + } + unsigned digits = numDigitsFor(numEdges); + // Initialize and run CreateEdgeFromVertices operation. auto createOp = smtk::session::polygon::CreateEdgeFromVertices::create(); for (auto& ppgFace : m_ppgFaceList) @@ -330,7 +354,7 @@ bool ImportPPG::Internal::createEdges() ++edgeId; ss.str(std::string()); ss.clear(); - ss << "edge " << edgeId; + ss << "edge " << std::setfill('0') << std::setw(digits) << edgeId; edgeRef.setName(ss.str()); ppgFace.edges.push_back(edgeRef.entityRecord()); @@ -387,6 +411,7 @@ bool ImportPPG::Internal::createFaces() // Deal with inner edge loops auto createdItem = result->findComponent("created"); + unsigned digits = numDigitsFor(m_ppgFaceList.size() - m_holeCount); for (std::size_t n = 0; n < createdItem->numberOfValues(); ++n) { smtk::resource::ComponentPtr pcomp = createdItem->value(n); @@ -396,7 +421,7 @@ bool ImportPPG::Internal::createFaces() // First face is the outer loop ss.str(std::string()); ss.clear(); - ss << "face " << outerPPGFace.userId; + ss << "face " << std::setfill('0') << std::setw(digits) << outerPPGFace.userId; faceRef.setName(ss.str()); continue; @@ -419,7 +444,7 @@ bool ImportPPG::Internal::createFaces() // First face is the outer loop ss.str(std::string()); ss.clear(); - ss << "face " << innerPPGFace.userId; + ss << "face " << std::setfill('0') << std::setw(digits) << innerPPGFace.userId; faceRef.setName(ss.str()); } } // for (n) -- GitLab From 23da77f59e3bfc9082ff36986e587551c2ced7cc Mon Sep 17 00:00:00 2001 From: Ryan Krattiger Date: Tue, 6 Jul 2021 13:15:57 -0500 Subject: [PATCH 44/52] CI: Add XMS Mesher contract test --- .gitlab/ci/configure_fedora33_paraview.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab/ci/configure_fedora33_paraview.cmake b/.gitlab/ci/configure_fedora33_paraview.cmake index 7f86900d2a..2203fce351 100644 --- a/.gitlab/ci/configure_fedora33_paraview.cmake +++ b/.gitlab/ci/configure_fedora33_paraview.cmake @@ -4,6 +4,7 @@ set(SMTK_PLUGIN_CONTRACT_FILE_URLS "https://gitlab.kitware.com/cmb/plugins/truchas-extensions/-/raw/master/CMake/truchas-extensions.cmake" "https://gitlab.kitware.com/cmb/cmb/-/raw/master/cmake/cmb.cmake" "https://gitlab.kitware.com/cmb/plugins/ace3p-extensions/-/raw/master/CMake/ace3p-extensions.cmake" + "https://gitlab.kitware.com/cmb/plugins/xmsmeshoperation/-/raw/master/CMake/xms-mesh-operation.cmake" CACHE STRING "") include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora33.cmake") -- GitLab From a9eef6ae262b0398174277b7d4b3e6cdc74ac178 Mon Sep 17 00:00:00 2001 From: Ryan Krattiger Date: Mon, 30 Aug 2021 14:26:38 -0500 Subject: [PATCH 45/52] CI: Update image for fedora33-paraview --- .gitlab/os-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index ad137a44e7..65378dad70 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml @@ -72,7 +72,7 @@ .fedora33_paraview: extends: .fedora33_vtk_python3 - image: "kitware/cmb:ci-smtk-fedora33-paraview-20210714" + image: "kitware/cmb:ci-smtk-fedora33-paraview-20210829" variables: CMAKE_CONFIGURATION: fedora33_paraview -- GitLab From 91b54848efb2196dea7654e3251e59e8501e9078 Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Mon, 30 Aug 2021 14:33:27 -0400 Subject: [PATCH 46/52] Fix ResourceContainer::find() to properly cast the return set --- smtk/project/ResourceContainer.h | 7 ++++++- smtk/project/testing/cxx/TestProject.cxx | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/smtk/project/ResourceContainer.h b/smtk/project/ResourceContainer.h index 5b4eef13c5..8cecd151a1 100644 --- a/smtk/project/ResourceContainer.h +++ b/smtk/project/ResourceContainer.h @@ -283,7 +283,12 @@ std::set> ResourceContainer::findByRole( template std::set> ResourceContainer::find() const { - return this->find(typeid(ResourceType).hash_code()); + std::set> cast_set; + for (const auto& resourceptr : this->find(typeid(ResourceType).hash_code())) + { + cast_set.insert(std::dynamic_pointer_cast(resourceptr)); + } + return cast_set; } template diff --git a/smtk/project/testing/cxx/TestProject.cxx b/smtk/project/testing/cxx/TestProject.cxx index 405be05848..d416d56c13 100644 --- a/smtk/project/testing/cxx/TestProject.cxx +++ b/smtk/project/testing/cxx/TestProject.cxx @@ -116,6 +116,10 @@ int TestProject(int /*unused*/, char** const /*unused*/) resource = project->resources().get(myResource->id()); smtkTest(!!resource, "could not access resource from project interface"); + // Verify the find by type method + auto resourceSet = project->resources().find(); + smtkTest(resourceSet.size() == 1, "could not get resources by type"); + // Remove the instance of smtk::project::Project projectManager->remove(project); -- GitLab From cbeda24f9dc8552b59e109698a7193734adc48da Mon Sep 17 00:00:00 2001 From: Robert O'Bara Date: Fri, 3 Sep 2021 00:18:08 -0400 Subject: [PATCH 47/52] ENH: Improving qtModelEntityView * Selection of the Type of Attribute via the ComboBox no longer requires any button press after the selection has been made * Also addressed a bug related to the View being updated when selecting a different attribute to be assigned to the model entity. The symptom was that instead of accepting the requested type, the View would instead display "Please Select" indicating that the model entity had no attribute associated with it. The reason was due to the fact that the View was being updated via the call to removeAttribute prior to the new attribute being created. The fix was to postpone calling that method until after the new attribute was in place. --- doc/release/notes/modelEntityViewChanges.rst | 6 ++ .../qt/qtModelEntityAttributeView.cxx | 68 ++++++++++++------- .../extension/qt/qtModelEntityAttributeView.h | 7 +- 3 files changed, 49 insertions(+), 32 deletions(-) create mode 100644 doc/release/notes/modelEntityViewChanges.rst diff --git a/doc/release/notes/modelEntityViewChanges.rst b/doc/release/notes/modelEntityViewChanges.rst new file mode 100644 index 0000000000..64a62cecd6 --- /dev/null +++ b/doc/release/notes/modelEntityViewChanges.rst @@ -0,0 +1,6 @@ +Changes to qtModelEntityView +---------------------------- + +* Selection of the Type of Attribute via the ComboBox no longer requires any button press after the selection has been made + +* Also addressed a bug related to the View being updated when selecting a different attribute to be assigned to the model entity. The symptom was that instead of accepting the requested type, the View would instead display "Please Select" indicating that the model entity had no attribute associated with it. The reason was due to the fact that the View was being updated via the call to removeAttribute prior to the new attribute being created. The fix was to postpone calling that method until after the new attribute was in place. diff --git a/smtk/extension/qt/qtModelEntityAttributeView.cxx b/smtk/extension/qt/qtModelEntityAttributeView.cxx index 7b92fd51a0..711665cc17 100644 --- a/smtk/extension/qt/qtModelEntityAttributeView.cxx +++ b/smtk/extension/qt/qtModelEntityAttributeView.cxx @@ -66,7 +66,12 @@ QWidget* qModelEntityAttributeViewComboBoxItemDelegate::createEditor( { auto* cbox = new QComboBox(parent); cbox->addItems(m_values); - connect(cbox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(choiceMade())); + // We want the combo box to immediately display when chosen and + // to immediately closed when a value has been selected + connect(cbox, static_cast(&QComboBox::activated), [=]() { + // This event will be captured by the delegate eventFilter() + QApplication::sendEvent(cbox, new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier)); + }); return cbox; } @@ -78,7 +83,7 @@ void qModelEntityAttributeViewComboBoxItemDelegate::setEditorData( if (cb != nullptr) { // Lets find the proper index of the current value w/r the combobox - auto currentText = index.data(Qt::EditRole).toString(); + auto currentText = index.data(Qt::DisplayRole).toString(); int pos = cb->findText(currentText); if (pos >= 0) { @@ -101,13 +106,9 @@ void qModelEntityAttributeViewComboBoxItemDelegate::setModelData( { if (cb->currentIndex() > -1) { - model->setData(index, cb->currentText(), Qt::EditRole); + model->setData(index, cb->currentText(), Qt::DisplayRole); model->setData(index, qtUIManager::contrastWithText(Qt::white), Qt::BackgroundRole); } - //QApplication::postEvent(model, new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier)); - emit const_cast(this)->choiceMade(); - emit const_cast(this)->destroyEditor( - editor, index); } else { @@ -115,6 +116,25 @@ void qModelEntityAttributeViewComboBoxItemDelegate::setModelData( } } +bool qModelEntityAttributeViewComboBoxItemDelegate::eventFilter(QObject* object, QEvent* event) +{ + // Show combo box popup when the box gains focus + if (event->type() == QEvent::FocusIn) + { + auto* combo_box = qobject_cast(object); + auto* focus_event = dynamic_cast(event); + if ( + combo_box && focus_event && + // Do not consider focus gained when the popup closes + focus_event->reason() != Qt::PopupFocusReason) + { + combo_box->showPopup(); + } + } + + return QStyledItemDelegate::eventFilter(object, event); +} + class qtModelEntityAttributeViewInternals { public: @@ -463,7 +483,6 @@ void qtModelEntityAttributeView::updateModelEntities() auto* col2Delegate = new qModelEntityAttributeViewComboBoxItemDelegate(slist, this->Internals->ListTable); - connect(col2Delegate, SIGNAL(choiceMade()), this, SLOT(selectionMade())); this->Internals->ListTable->blockSignals(true); this->Internals->ListTable->setRowCount(0); this->Internals->ListTable->setItemDelegateForColumn(1, col2Delegate); @@ -550,16 +569,19 @@ void qtModelEntityAttributeView::cellChanged(int row, int column) } // Get the current attribute associated with the model entity (if any) - auto att = this->Internals->getAttribute(entity); - if (att && att->definition()->displayedTypeName() == tname) + smtk::attribute::AttributePtr exisitingAtt = this->Internals->getAttribute(entity); + smtk::attribute::AttributePtr newAtt; + if (exisitingAtt && exisitingAtt->definition()->displayedTypeName() == tname) { // The attribute itself didn't change, so we can stop here return; } - else if (att) + else if (exisitingAtt) { - attRes->removeAttribute(att); - this->attributeRemoved(att); + // Note though we are removing the attribute from the resource here, we can't call + // attributeRemoved until after we have created it's replacement since it will + // cause the view to update. + attRes->removeAttribute(exisitingAtt); } // Now create a new attribute for the model entity of the correct type @@ -568,13 +590,17 @@ void qtModelEntityAttributeView::cellChanged(int row, int column) { if (currentDefs.at(j)->displayedTypeName() == tname) { - att = attRes->createAttribute(currentDefs.at(j)); - att->associate(entity); + newAtt = attRes->createAttribute(currentDefs.at(j)); + newAtt->associate(entity); // Notify the application of the new attribute via an "operation" - this->attributeCreated(att); + this->attributeCreated(newAtt); break; } } + if (exisitingAtt) + { + this->attributeRemoved(exisitingAtt); + } this->Internals->ListTable->selectRow(row); this->selectedRowChanged(); } @@ -791,16 +817,6 @@ void qtModelEntityAttributeView::showAdvanceLevelOverlay(bool show) } } -void qtModelEntityAttributeView::selectionMade() -{ - if (this->Internals->ListTable) - { - QApplication::postEvent( - this->Internals->ListTable, - new QKeyEvent(QKeyEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier)); - } -} - bool qtModelEntityAttributeView::isEmpty() const { QList currentDefs = diff --git a/smtk/extension/qt/qtModelEntityAttributeView.h b/smtk/extension/qt/qtModelEntityAttributeView.h index 16f3b62051..d76c4722a6 100644 --- a/smtk/extension/qt/qtModelEntityAttributeView.h +++ b/smtk/extension/qt/qtModelEntityAttributeView.h @@ -100,9 +100,6 @@ protected: /**\brief Return a presistent object that cooresponds to a table widget item.*/ smtk::resource::PersistentObjectPtr object(QTableWidgetItem* item); -protected slots: - void selectionMade(); - private: qtModelEntityAttributeViewInternals* Internals; @@ -123,10 +120,8 @@ public: void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override; -signals: - void choiceMade(); - protected: + bool eventFilter(QObject* object, QEvent* event) override; QStringList m_values; }; -- GitLab From d40f8630092079abc5478e5ebb3424d9cccb6fe4 Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Thu, 2 Sep 2021 09:10:53 -0400 Subject: [PATCH 48/52] Add SMTK_VERSION_MINOR_INT to remove leading zero --- CMake/Version.h.in | 7 +++++-- CMake/smtkVersion.cmake | 2 ++ smtk/common/Deprecation.h | 4 ++-- smtk/common/VersionMacros.h | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CMake/Version.h.in b/CMake/Version.h.in index 3dceeb1b7e..2a0081bed9 100644 --- a/CMake/Version.h.in +++ b/CMake/Version.h.in @@ -16,10 +16,13 @@ /// Integer-valued major version number #define SMTK_VERSION_MAJOR @SMTK_VERSION_MAJOR@ /// Integer-valued minor version number -#define SMTK_VERSION_MINOR @SMTK_VERSION_MINOR@ +#define SMTK_VERSION_MINOR_INT @SMTK_VERSION_MINOR_INT@ /// Integer-valued patch version number #define SMTK_VERSION_PATCH @SMTK_VERSION_PATCH@ +/// String-valued minor version number +#define SMTK_VERSION_MINOR @SMTK_VERSION_MINOR@ + // On some systems, major and minor are defined as macros. If this is one of // those systems, undefine these macros before defining the static methods // of smtk::common::Version. @@ -42,7 +45,7 @@ public: static int major() { return SMTK_VERSION_MAJOR; } /// Return the version number components as integers. - static int minor() { return SMTK_VERSION_MINOR; } + static int minor() { return SMTK_VERSION_MINOR_INT; } /// Return the version number components as integers. static int patch() { return SMTK_VERSION_PATCH; } diff --git a/CMake/smtkVersion.cmake b/CMake/smtkVersion.cmake index b90c69b419..df0edf8960 100644 --- a/CMake/smtkVersion.cmake +++ b/CMake/smtkVersion.cmake @@ -31,4 +31,6 @@ if (NOT DEFINED SMTK_VERISON) set(SMTK_VERSION_LABEL "${CMAKE_MATCH_4}") endif() + set(SMTK_VERSION_MINOR_STRING ${SMTK_VERSION_MINOR}) + string(REGEX REPLACE "^0" "" SMTK_VERSION_MINOR_INT ${SMTK_VERSION_MINOR}) endif() diff --git a/smtk/common/Deprecation.h b/smtk/common/Deprecation.h index 8189d9ba9d..34efd77917 100644 --- a/smtk/common/Deprecation.h +++ b/smtk/common/Deprecation.h @@ -57,13 +57,13 @@ "SMTK Deprecated in " #version_major "." #version_minor ": " reason #if SMTK_DEPRECATION_LEVEL >= SMTK_VERSION_CHECK(21, 9) -#define SMTK_DEPRECATED_IN_21_09(reason) SMTK_DEPRECATION(SMTK_DEPRECATION_REASON(21, 09, reason)) +#define SMTK_DEPRECATED_IN_21_09(reason) SMTK_DEPRECATION(SMTK_DEPRECATION_REASON(21, 9, reason)) #else #define SMTK_DEPRECATED_IN_21_09(reason) #endif #if SMTK_DEPRECATION_LEVEL >= SMTK_VERSION_CHECK(21, 8) -#define SMTK_DEPRECATED_IN_21_08(reason) SMTK_DEPRECATION(SMTK_DEPRECATION_REASON(21, 08, reason)) +#define SMTK_DEPRECATED_IN_21_08(reason) SMTK_DEPRECATION(SMTK_DEPRECATION_REASON(21, 8, reason)) #else #define SMTK_DEPRECATED_IN_21_08(reason) #endif diff --git a/smtk/common/VersionMacros.h b/smtk/common/VersionMacros.h index d125be56c3..03cf99656a 100644 --- a/smtk/common/VersionMacros.h +++ b/smtk/common/VersionMacros.h @@ -15,6 +15,6 @@ #define SMTK_VERSION_CHECK(major, minor) (100ULL * (major) + (minor)) -#define SMTK_VERSION_NUMBER SMTK_VERSION_CHECK(SMTK_VERSION_MAJOR, SMTK_VERSION_MINOR) +#define SMTK_VERSION_NUMBER SMTK_VERSION_CHECK(SMTK_VERSION_MAJOR, SMTK_VERSION_MINOR_INT) #endif // smtk_common_VersionMacros_h -- GitLab From c066f74257c9b462ed9eeed9c5b11397b4205d14 Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Thu, 2 Sep 2021 09:18:51 -0400 Subject: [PATCH 49/52] Replace deprecated methods --- smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx | 2 +- smtk/extension/qt/qtBaseAttributeView.cxx | 2 +- smtk/extension/qt/qtBaseView.cxx | 2 +- smtk/extension/qt/qtResourceBrowser.cxx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx b/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx index 1657243d64..d24fdeb4a6 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx +++ b/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx @@ -133,7 +133,7 @@ void pqSMTKResourceBrowser::resourceManagerRemoved(pqSMTKWrapper* mgr, pqServer* void pqSMTKResourceBrowser::initSubphraseGenerator() { - std::string subphraseViewType = smtk::view::SubphraseGenerator::getType(this->getObject()); + std::string subphraseViewType = smtk::view::SubphraseGenerator::getType(this->configuration()); auto* smtkSettings = vtkSMTKSettings::GetInstance(); int resourceTreeStyle = smtkSettings->GetResourceTreeStyle(); diff --git a/smtk/extension/qt/qtBaseAttributeView.cxx b/smtk/extension/qt/qtBaseAttributeView.cxx index aca3da8f9c..0e23274f1c 100644 --- a/smtk/extension/qt/qtBaseAttributeView.cxx +++ b/smtk/extension/qt/qtBaseAttributeView.cxx @@ -89,7 +89,7 @@ qtBaseAttributeView::qtBaseAttributeView(const smtk::view::Information& info) m_ScrollArea = nullptr; m_fixedLabelWidth = this->uiManager()->maxValueLabelLength(); m_topLevelInitialized = false; - m_ignoreCategories = this->getObject()->details().attributeAsBool("IgnoreCategories"); + m_ignoreCategories = this->configuration()->details().attributeAsBool("IgnoreCategories"); // We need to be able to determine within the a Signal Operation, which View caused // the change in order to avoid infinite loops. To do this, each View will have an addressString // set to its address. This string is then passed to the signalAttribute function when needed. diff --git a/smtk/extension/qt/qtBaseView.cxx b/smtk/extension/qt/qtBaseView.cxx index 4a48fdc57e..6b418003e0 100644 --- a/smtk/extension/qt/qtBaseView.cxx +++ b/smtk/extension/qt/qtBaseView.cxx @@ -41,7 +41,7 @@ qtBaseView::qtBaseView(const smtk::view::Information& info) m_advOverlayVisible = false; m_isTopLevel = false; m_useSelectionManager = false; - const auto& view = this->getObject(); + const auto& view = this->configuration(); if (view) { m_isTopLevel = view->details().attributeAsBool("TopLevel"); diff --git a/smtk/extension/qt/qtResourceBrowser.cxx b/smtk/extension/qt/qtResourceBrowser.cxx index bd433bb2be..7409af4d00 100644 --- a/smtk/extension/qt/qtResourceBrowser.cxx +++ b/smtk/extension/qt/qtResourceBrowser.cxx @@ -73,7 +73,7 @@ qtResourceBrowser::qtResourceBrowser(const smtk::view::Information& info) smtk::view::PhraseModelPtr phraseModel; std::string modelViewType; QAbstractItemModel* qtPhraseModel = nullptr; - const auto& view = this->getObject(); + const auto& view = this->configuration(); if (view) { // empty Widget attribute is OK, will use default. -- GitLab From b4d0a3f9c49211fae9a41795aea6d5f5b6d6d72f Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Wed, 1 Sep 2021 11:41:04 -0400 Subject: [PATCH 50/52] Compile release notes for 21.09.0 --- ReadMe.md | 2 +- .../depricatingQtUIManagerAttResource.rst | 13 -- doc/release/notes/expressionIO_changes.rst | 6 - ...fixingQtAttributeViewOperationHandling.rst | 12 -- doc/release/notes/replacingGetObject.rst | 8 - doc/release/notes/settingEditorTitle.rst | 28 --- .../notes/supportingExternalExpressions.rst | 18 -- .../notes/supportingMarkedForRemoval.rst | 15 -- doc/release/notes/task-classes.rst | 16 -- doc/release/notes/task-json.rst | 6 - .../notes/usingTypeContainersForViews.rst | 21 --- doc/release/notes/view-badge-speed.rst | 7 - doc/release/smtk-21.09.rst | 175 ++++++++++++++++++ 13 files changed, 176 insertions(+), 151 deletions(-) delete mode 100644 doc/release/notes/depricatingQtUIManagerAttResource.rst delete mode 100644 doc/release/notes/expressionIO_changes.rst delete mode 100644 doc/release/notes/fixingQtAttributeViewOperationHandling.rst delete mode 100644 doc/release/notes/replacingGetObject.rst delete mode 100644 doc/release/notes/settingEditorTitle.rst delete mode 100644 doc/release/notes/supportingExternalExpressions.rst delete mode 100644 doc/release/notes/supportingMarkedForRemoval.rst delete mode 100644 doc/release/notes/task-classes.rst delete mode 100644 doc/release/notes/task-json.rst delete mode 100644 doc/release/notes/usingTypeContainersForViews.rst delete mode 100644 doc/release/notes/view-badge-speed.rst create mode 100644 doc/release/smtk-21.09.rst diff --git a/ReadMe.md b/ReadMe.md index 6c0cd04766..e7a978d46a 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -62,7 +62,7 @@ See [CONTRIBUTING.md][] for instructions to contribute. Latest Release Notes ==================== -Can be found [here](doc/release/smtk-21.07.rst). +Can be found [here](doc/release/smtk-21.09.rst). License ======= diff --git a/doc/release/notes/depricatingQtUIManagerAttResource.rst b/doc/release/notes/depricatingQtUIManagerAttResource.rst deleted file mode 100644 index 1d62c87bd8..0000000000 --- a/doc/release/notes/depricatingQtUIManagerAttResource.rst +++ /dev/null @@ -1,13 +0,0 @@ -Changes to qtBaseAttributeView ------------------------------- - -In the past classes derived from qtBaseAttributeView relied on the qtUIManager to get access to the Attribute Resource they depended on. This is no longer the case. The Attribute Resource is now part of the information used to defined the instance. - -Deprecation of qtUIManager::attResource() method -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This method is no longer needed for qtBaseAttributeView derived classes. - -Added qtItem::attributeResource() method -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This is a convenience method for accessing the qtItem's underlying Attribute Resource diff --git a/doc/release/notes/expressionIO_changes.rst b/doc/release/notes/expressionIO_changes.rst deleted file mode 100644 index b148bffd65..0000000000 --- a/doc/release/notes/expressionIO_changes.rst +++ /dev/null @@ -1,6 +0,0 @@ -Changing Expression Format -========================== -JSON will now store a ValueItem's Expression in ComponentItem format using the key "ExpressionReference" instead of 2 keys called "Expression" and "ExpressionName". This no only simplifies things format wise but will also support expressions stored in different resources. - -**Note** The older format is still supported so this change is backward compatible. -**Note** The XML format is still using the older style. diff --git a/doc/release/notes/fixingQtAttributeViewOperationHandling.rst b/doc/release/notes/fixingQtAttributeViewOperationHandling.rst deleted file mode 100644 index 84804b0c4a..0000000000 --- a/doc/release/notes/fixingQtAttributeViewOperationHandling.rst +++ /dev/null @@ -1,12 +0,0 @@ -Fixing qtAttributeView handling of Operations ---------------------------------------------- - -In qtAttributeView::handleOperationEvent, the code had the following issues: - -1. It assumed that an Attribute that is in the operation's result must be based on the View's current definition. This is only true if the View only contained one Definition. In reality, the attribute's Definition needs to be tested against the Definitions that defined the View and if it matches any of them it needs to be processed. -2. It always assumed that there was a Current Attribute selected. This would result in a crash if there wasn't any. -3. There was a bug in qtAttributeView::getItemFromAttribute, it was getting items from column 0 instead of the name column. This resulted in names not being properly updated - -Added qtAttributeView::matchesDefinitions -~~~~~~~~~~~~~~~~~~ -This method tests a Definition against the Definitions that define the View and returns true if it matches or is derived from any in the list. diff --git a/doc/release/notes/replacingGetObject.rst b/doc/release/notes/replacingGetObject.rst deleted file mode 100644 index 6974ef7d4c..0000000000 --- a/doc/release/notes/replacingGetObject.rst +++ /dev/null @@ -1,8 +0,0 @@ -Replacing qtBaseView::getObject() ---------------------------------- - -This method returns the instance's view::Configuration but the name didn't reflect that. Also the getObject method returned a smtk::view::ConfigurationPtr which means that a new shared pointer was always being created and as a result, incrementing its reference count. The new configuration() method returns a const smtk::view::ConfigurationPtr& instead which does not effect the shared point reference count. - -Developer changes -~~~~~~~~~~~~~~~~~~ -* getObject method is now deprecated. diff --git a/doc/release/notes/settingEditorTitle.rst b/doc/release/notes/settingEditorTitle.rst deleted file mode 100644 index 7be1d65398..0000000000 --- a/doc/release/notes/settingEditorTitle.rst +++ /dev/null @@ -1,28 +0,0 @@ -Added Ability to Set Attribute Editor Panel's Title ----------------------------------------------------- - -The Attribute Editor Panel name can now be configure by a smtk::view::Configuration. - -If the Configuration is Top Level then the following Configuration Attributes can be used: - -* AttributePanelTitle - defines the base name of the Panel. If not specified it defaults to Attribute Editor. -* IncludeResourceNameInPanel - if specified and set to true, the Panel's title will include the name of the resource in () - -SimpleAttribute.sbt contains an example: - -.. code-block:: xml - - - - - - - - - - -Developer changes -~~~~~~~~~~~~~~~~~~ - -* pqSMTKAttributePanel::updateTitle now takes in a const smtk::view::ConfigurationPtr& diff --git a/doc/release/notes/supportingExternalExpressions.rst b/doc/release/notes/supportingExternalExpressions.rst deleted file mode 100644 index 1d5f7b606d..0000000000 --- a/doc/release/notes/supportingExternalExpressions.rst +++ /dev/null @@ -1,18 +0,0 @@ -Supporting "External" Expressions -================================= -There are use cases where the workflow may want to store expressions in a separate Attribute Resource. -The core of SMTK already supported this but the UI system assumed that the Attribute Resource which owned the ValueItem was also the source for expressions. This is no longer the case. - -qtInstancedView can now optionally take in an Attribute Resource instead of solely relying on the one associated with the UI Manager. This allows classes like the qtAttributeEditor to supply the Attribute Resource. - -Added a new query function called: findResourceContainingDefinition that will return an Attribute Resource that contains an Attribute Definition referred to by its typename. If the optional Attribute Resource provided to the function also contains the Definition, it is returned immediately without doing any additional searching. This maintains the original use case where the expressions are stored in the same resource. - -qtInputItem no longer assumes the Item's Attribute Resource is the one being used as a source for expressions. - -Added two template files that can be used to demo the functionality. - -data/attribute/attribute_collection/externalExpressionsPt1.sbt - Contains an Attribute Definition with an Item that can use an expression that is not defined in that template - -data/attribute/attribute_collection/externalExpressionsPt2.sbt - Contains an Attribute Definition that represents the expressions used in Pt1. - -Closes #439 diff --git a/doc/release/notes/supportingMarkedForRemoval.rst b/doc/release/notes/supportingMarkedForRemoval.rst deleted file mode 100644 index ab794ff0f5..0000000000 --- a/doc/release/notes/supportingMarkedForRemoval.rst +++ /dev/null @@ -1,15 +0,0 @@ -Supporting MarkedForRemoval --------------------------- - -Resources can now be markedForRemoval indicating that the resource will be removed from memory (as apposed to deletion which also means it is being deleted from storage as well). This can be used in the UI to determine if a View needs to worry about keeping its contents up to date if the reason it is using is going to be removed. This also gets around a current issue with the Resource Links system which will cause a resource to be pulled back into memory even though the resources that its associated with is going to be removed. - -Another benefit is to potentially optimize the removal of components when its resource is targeted for removal. - -Developer changes -~~~~~~~~~~~~~~~~~~ - -* Added the following API to smtk::resource::Resource - * setMarkedForRemoval - sets the resource's marked for removal state - * isMarkedForRemoval - returns the resource's marked for removal state -* UI class changes - * All Attribute Related Qt classes that handle operations will now check to see if the resource is marked for removal. diff --git a/doc/release/notes/task-classes.rst b/doc/release/notes/task-classes.rst deleted file mode 100644 index afa0b0ad0b..0000000000 --- a/doc/release/notes/task-classes.rst +++ /dev/null @@ -1,16 +0,0 @@ -New task classes and base class changes -======================================= - -The task subsystem now provides more task types, task-adaptor classes -for configuring tasks as they change state, and additional tests. -See the `task class documentation`_ for details. - -Tasks now include "style" strings that will be used to configure -application state when the task becomes active. - -Tasks now include references to dependencies and dependents, -children and a parent. These are used to provide workflow -observers that user interfaces can use to monitor when tasks -are added-to and removed-from a pipeline. - -.. _task class documentation: https://smtk.readthedocs.io/en/latest/userguide/task/classes.html diff --git a/doc/release/notes/task-json.rst b/doc/release/notes/task-json.rst deleted file mode 100644 index c4d824950d..0000000000 --- a/doc/release/notes/task-json.rst +++ /dev/null @@ -1,6 +0,0 @@ -Task serialization/deserialization -================================== - -The task classes and task manager now support serialization -and deserialization (to/from JSON). See the TestTaskJSON -test and user guide for more details. diff --git a/doc/release/notes/usingTypeContainersForViews.rst b/doc/release/notes/usingTypeContainersForViews.rst deleted file mode 100644 index 9d3e7aa4fe..0000000000 --- a/doc/release/notes/usingTypeContainersForViews.rst +++ /dev/null @@ -1,21 +0,0 @@ -Using TypeContainers instead of ViewInfo ----------------------------------------- - -In order to make the View System more flexible and to work with the new Task System, the following changes were made: - -* smtk::view::Information is now derived from TypeContainer and is no longer an abstract class. As a result it can now do the job that ViewInfo and OperationViewInfo does -* ViewInfo and OperationViewInfo are no longer needed. -* qtBaseView's m_viewInfo is now an instance of smtk::view::Information and not ViewInfo - -Developer changes -~~~~~~~~~~~~~~~~~~ - -Unless the qtView is directly accessing m_viewInfo, there should be no required changes. - -When dealing with smtk::view::information, it is important that the type you insert into the container exactly matches the type you use to get information from the container. For example if you insert a QPushButton* into the container and attempt to get a QWidget* back, it will fail and throw an exception. - -So it is recommended you explicitly state the template type instead of having the compiler determine it. In the above example you would need to do an insert(myQtPushButton) in order to get a QWidget* back. - -Removed Data Structures -+++++++++++++++++++++++ -smtk::external::ViewInfo and smtk::external::OperatorViewInfo are no longer needed and have been removed. smtk::view::Information object should be used instead. diff --git a/doc/release/notes/view-badge-speed.rst b/doc/release/notes/view-badge-speed.rst deleted file mode 100644 index ca0eb87eae..0000000000 --- a/doc/release/notes/view-badge-speed.rst +++ /dev/null @@ -1,7 +0,0 @@ -Visibility badge improvements -============================= - -The ParaView visibility-badge extension had an issue when large numbers -of phrase-model instances existed and a resource was closed: the visibility -was updated by completely rebuilding the map of visible entities which -is slow. This is now fixed. diff --git a/doc/release/smtk-21.09.rst b/doc/release/smtk-21.09.rst new file mode 100644 index 0000000000..2ab4de624a --- /dev/null +++ b/doc/release/smtk-21.09.rst @@ -0,0 +1,175 @@ +.. _release-notes-21.09: + +========================= +SMTK 21.09 Release Notes +========================= + +See also :ref:`release-notes-21.07` for previous changes. + + +SMTK Resource and Component Changes +=================================== + +Supporting MarkedForRemoval +-------------------------- +Resources can now be markedForRemoval indicating that the resource will be removed from memory (as apposed to deletion which also means it is being deleted from storage as well). This can be used in the UI to determine if a View needs to worry about keeping its contents up to date if the reason it is using is going to be removed. This also gets around a current issue with the Resource Links system which will cause a resource to be pulled back into memory even though the resources that its associated with is going to be removed. + +Another benefit is to potentially optimize the removal of components when its resource is targeted for removal. + +Developer changes +~~~~~~~~~~~~~~~~~~ +* Added the following API to smtk::resource::Resource + * setMarkedForRemoval - sets the resource's marked for removal state + * isMarkedForRemoval - returns the resource's marked for removal state +* UI class changes + * All Attribute Related Qt classes that handle operations will now check to see if the resource is marked for removal. + + +Attribute Resource Changes +========================== + +Changing Expression Format +-------------------------- +JSON will now store a ValueItem's Expression in ComponentItem format using the key "ExpressionReference" instead of 2 keys called "Expression" and "ExpressionName". This no only simplifies things format wise but will also support expressions stored in different resources. + +**Note** The older format is still supported so this change is backward compatible. +**Note** The XML format is still using the older style. + +Supporting "External" Expressions +--------------------------------- +There are use cases where the workflow may want to store expressions in a separate Attribute Resource. +The core of SMTK already supported this but the UI system assumed that the Attribute Resource which owned the ValueItem was also the source for expressions. This is no longer the case. + +qtInstancedView can now optionally take in an Attribute Resource instead of solely relying on the one associated with the UI Manager. This allows classes like the qtAttributeEditor to supply the Attribute Resource. + +Added a new query function called: findResourceContainingDefinition that will return an Attribute Resource that contains an Attribute Definition referred to by its typename. If the optional Attribute Resource provided to the function also contains the Definition, it is returned immediately without doing any additional searching. This maintains the original use case where the expressions are stored in the same resource. + +qtInputItem no longer assumes the Item's Attribute Resource is the one being used as a source for expressions. + +Added two template files that can be used to demo the functionality. + +* `data/attribute/attribute_collection/externalExpressionsPt1.sbt` - Contains an Attribute Definition with an Item that can use an expression that is not defined in that template + +* `data/attribute/attribute_collection/externalExpressionsPt2.sbt` - Contains an Attribute Definition that represents the expressions used in Pt1. + + +Qt UI Changes +============= + +Changes to qtBaseAttributeView +------------------------------ +In the past classes derived from qtBaseAttributeView relied on the qtUIManager to get access to the Attribute Resource they depended on. This is no longer the case. The Attribute Resource is now part of the information used to defined the instance. + +Deprecation of qtUIManager::attResource() method +------------------------------------------------ +This method is no longer needed for qtBaseAttributeView derived classes. + +Added qtItem::attributeResource() method +---------------------------------------- +This is a convenience method for accessing the qtItem's underlying Attribute Resource + +Fixing qtAttributeView handling of Operations +--------------------------------------------- +In qtAttributeView::handleOperationEvent, the code had the following issues: + +1. It assumed that an Attribute that is in the operation's result must be based on the View's current definition. This is only true if the View only contained one Definition. In reality, the attribute's Definition needs to be tested against the Definitions that defined the View and if it matches any of them it needs to be processed. +2. It always assumed that there was a Current Attribute selected. This would result in a crash if there wasn't any. +3. There was a bug in qtAttributeView::getItemFromAttribute, it was getting items from column 0 instead of the name column. This resulted in names not being properly updated + +Added qtAttributeView::matchesDefinitions +----------------------------------------- +This method tests a Definition against the Definitions that define the View and returns true if it matches or is derived from any in the list. + +Replacing qtBaseView::getObject() +--------------------------------- +This method returns the instance's view::Configuration but the name didn't reflect that. Also the getObject method returned a smtk::view::ConfigurationPtr which means that a new shared pointer was always being created and as a result, incrementing its reference count. The new configuration() method returns a const smtk::view::ConfigurationPtr& instead which does not effect the shared point reference count. + +Developer changes +~~~~~~~~~~~~~~~~~ +* `qtBaseView::getObject()` method is now deprecated. + +Added Ability to Set Attribute Editor Panel's Title +---------------------------------------------------- +The Attribute Editor Panel name can now be configure by a smtk::view::Configuration. + +If the Configuration is Top Level then the following Configuration Attributes can be used: + +* AttributePanelTitle - defines the base name of the Panel. If not specified it defaults to Attribute Editor. +* IncludeResourceNameInPanel - if specified and set to true, the Panel's title will include the name of the resource in () + +SimpleAttribute.sbt contains an example: + +.. code-block:: xml + + + + + + + + + +Developer changes +~~~~~~~~~~~~~~~~~~ +* `pqSMTKAttributePanel::updateTitle()` now takes in a `const smtk::view::ConfigurationPtr&` argument. + + +SMTK Task Subsystem (Preview) +============================= + +New Task Classes +---------------- +The task subsystem now provides more task types, task-adaptor classes +for configuring tasks as they change state, and additional tests. +See the `task class documentation`_ for details. + +Tasks now include "style" strings that will be used to configure +application state when the task becomes active. + +Tasks now include references to dependencies and dependents, +children and a parent. These are used to provide workflow +observers that user interfaces can use to monitor when tasks +are added-to and removed-from a pipeline. + +.. _task class documentation: https://smtk.readthedocs.io/en/latest/userguide/task/classes.html + +Task serialization/deserialization +---------------------------------- +The task classes and task manager now support serialization +and deserialization (to/from JSON). See the TestTaskJSON +test and user guide for more details. + + +Other SMTK Core Changes +======================= + +Using TypeContainers instead of ViewInfo +---------------------------------------- + +In order to make the View System more flexible and to work with the new Task System, the following changes were made: + +* smtk::view::Information is now derived from TypeContainer and is no longer an abstract class. As a result it can now do the job that ViewInfo and OperationViewInfo does +* ViewInfo and OperationViewInfo are no longer needed. +* qtBaseView's m_viewInfo is now an instance of smtk::view::Information and not ViewInfo + +Developer changes +~~~~~~~~~~~~~~~~~~ + +Unless the qtView is directly accessing m_viewInfo, there should be no required changes. + +When dealing with smtk::view::information, it is important that the type you insert into the container exactly matches the type you use to get information from the container. For example if you insert a QPushButton* into the container and attempt to get a QWidget* back, it will fail and throw an exception. + +So it is recommended you explicitly state the template type instead of having the compiler determine it. In the above example you would need to do an insert(myQtPushButton) in order to get a QWidget* back. + +Removed Data Structures ++++++++++++++++++++++++ +smtk::external::ViewInfo and smtk::external::OperatorViewInfo are no longer needed and have been removed. smtk::view::Information object should be used instead. + + +Visibility badge improvements +----------------------------- +The ParaView visibility-badge extension had an issue when large numbers +of phrase-model instances existed and a resource was closed: the visibility +was updated by completely rebuilding the map of visible entities which +is slow. This is now fixed. -- GitLab From 30e64041a3e7a8b5f92f9591bbdbc92adc3b65a2 Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Wed, 1 Sep 2021 15:11:49 -0400 Subject: [PATCH 51/52] Update version number to 21.09.0 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 16406cd4a7..7ded421501 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -21.07.0 +21.09.0 -- GitLab From 75d9753bd0b6f3b493b7b34b0b3af4764bf060d7 Mon Sep 17 00:00:00 2001 From: John Tourtellott Date: Wed, 1 Sep 2021 15:16:12 -0400 Subject: [PATCH 52/52] Update cdash-groups.json to track the release group --- .gitlab/ci/cdash-groups.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.gitlab/ci/cdash-groups.json b/.gitlab/ci/cdash-groups.json index 25147edd2f..8907e88fae 100644 --- a/.gitlab/ci/cdash-groups.json +++ b/.gitlab/ci/cdash-groups.json @@ -1,57 +1,57 @@ { - "latest-master": [ + "latest-release": [ { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "fedora33_asan" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "fedora33_coverage" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "fedora33_nodata" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "fedora33_paraview" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "fedora33_plain" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "fedora33_tidy" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "fedora33_ubsan" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "fedora33_vtk_python3" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "macos_arm64" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "macos_x86_64" }, { - "group": "master", + "group": "release", "site": "gitlab-ci", "buildname": "windows_vs2019_ninja" } -- GitLab
State "truth table" given internal and dependency states
Dependencies/
Internal
UnavailableIncomplete CompletableCompleted
User has not marked task completed
User has not marked task completed
Irrelevant Irrelevant Irrelevant Irrelevant Irrelevant
Unavailable UnavailableUnavailableUnavailableUnavailable
Incomplete Unavailable IncompleteIncomplete Incomplete
Completable Unavailable IncompleteCompletableCompletable
User has marked task completed
User has marked task completed
Irrelevant Irrelevant Irrelevant Irrelevant Irrelevant
Unavailable UnavailableUnavailableUnavailableUnavailable
Incomplete Unavailable IncompleteIncomplete Incomplete
Completable Unavailable IncompleteCompletableCompleted