diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5a092893ae4ef4fdcad3b2d167fe4d3a0a6e9de1..8dbdd489c4045a10b3627588280f40f2f6d5a975 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/ci/cmake.ps1 b/.gitlab/ci/cmake.ps1 index 5d01e3f9e336c06f702a93c63e16a8d3cb44d67e..11e37069b6f359fe8b2016d7fae067f33ca51c67 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 1547e30aa246c3d387c39a1992f3e9fc4ebc47fd..0a1d13682b6cfffedabc887af08e796a107e3b50 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" ;; *) diff --git a/.gitlab/ci/configure_fedora33_paraview.cmake b/.gitlab/ci/configure_fedora33_paraview.cmake index 7f86900d2a66d42a53719110b780d91b3c598f81..2203fce351a48f185db83619f5b97f2480302047 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") diff --git a/.gitlab/ci/docker/fedora33-paraview/install_superbuild.sh b/.gitlab/ci/docker/fedora33-paraview/install_superbuild.sh index bd3df49bcd5ae986dcc5c2e9f27a2c575f7984d4..96f9a77836cbe96bbe4099d0dc147b6ff6e8a181 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" diff --git a/.gitlab/issue_templates/new-release.md b/.gitlab/issue_templates/new-release.md index dec45344e4e562dc29120f77040269b3a24c8531..9d7e38e0d5372d6baf25d9d7732d8c3e43e35307 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,8 @@ 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`. + - [ ] 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/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index ad137a44e71c457acb8bab33b6a4d06f8b193d28..65378dad707b98e74622fbdbb65a8075366bf532 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 diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml index 7cb8f5030c6d3fef5e2cf94e216f51adad2a42bc..5f4dcbae355bfe0be4e79e98288c20454c4c2980 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 ### Build and test diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml index b65671aa0045211797bd0f36c1a0a8ffbb3b00bb..3c111a4858b720cfb7ef00a8a6c16e1329bfcdcf 100644 --- a/.gitlab/os-windows.yml +++ b/.gitlab/os-windows.yml @@ -7,7 +7,9 @@ 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 ### Build and test @@ -75,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 @@ -90,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 diff --git a/.gitlab/rules.yml b/.gitlab/rules.yml index 6c7db2fbd4dbb6407adab6166d8157d899919f78..76f23103b99705f98cec4481ed03aa65ee73769c 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 diff --git a/CMake/CTestCustom.cmake.in b/CMake/CTestCustom.cmake.in index a2ad18fc406e47269b39bdb7720ea8d213835199..6bb43e294307b5d5531c8fcf0c5ae5dd177f949b 100644 --- a/CMake/CTestCustom.cmake.in +++ b/CMake/CTestCustom.cmake.in @@ -34,11 +34,11 @@ 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" + + # Warnings from sccache that we don't care about. + "sccache: warning: The server looks like it shut down unexpectedly, compiling locally instead" ) ##------------------------------------------------------------------------------ diff --git a/CMake/Version.h.in b/CMake/Version.h.in index 3dceeb1b7e917a1530bc776a220ea6ae295a1b14..2a0081bed9989536dade69beb7795efd3052aaae 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 b90c69b419e394aca5649b130e9d1f1a6249ed26..df0edf89603223909b0dcc49d1f8eb9eaca48337 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/CTestConfig.cmake b/CTestConfig.cmake index 1dcdb9b5fdb82ce8f5fdbe44f0cb17f138139d6a..f44a82eda3a0366e756bff228a799a7011e5abd9 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") diff --git a/ReadMe.md b/ReadMe.md index 5383954b5ce076e0558d8b46bd089748f2abb8e1..e7a978d46ab79b8ba70eecf2138185bc1125e9bd 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.09.rst). License ======= diff --git a/data/attribute/attribute_collection/SimpleAttribute.sbt b/data/attribute/attribute_collection/SimpleAttribute.sbt new file mode 100644 index 0000000000000000000000000000000000000000..768f04c62681dfc3c87a271938b21e23dc6d23f4 --- /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/data/attribute/attribute_collection/externalExpressionsPt1.sbt b/data/attribute/attribute_collection/externalExpressionsPt1.sbt new file mode 100644 index 0000000000000000000000000000000000000000..700ab9aa24d8007cdf06e66296af400e925e1574 --- /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 0000000000000000000000000000000000000000..aa77be233905e8728e5c9f88dbdcc6b7e1c14207 --- /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/CMakeLists.txt b/doc/CMakeLists.txt index 3cab56ba9a49717244c4a736cf184d7f9ca6effe..4ce8765629f884ee319a7be5bef153d99d69204f 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/conf.py b/doc/conf.py index 4042d9b2711e764fee69366a11deaed4cd79dd88..3edbc0d0327bf59bba267753f667a62b8b591d4c 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/index.rst b/doc/index.rst index b80e6b90e4bf09e75b638d5a868751cdee3efd51..c4e3ce7d2578396d9d1686be03af4e02ac009c06 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 0000000000000000000000000000000000000000..5e771a138542a0cad2ba60d72f2d6f8d09e3ec6c --- /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/notes/modelEntityViewChanges.rst b/doc/release/notes/modelEntityViewChanges.rst new file mode 100644 index 0000000000000000000000000000000000000000..64a62cecd691d00746b5bdc5650cb21d8f4f878e --- /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/doc/release/smtk-21.05.rst b/doc/release/smtk-21.05.rst index e93039cf4a7f888e7abfa32b8858f29bf2e75e0c..fbee5ab111079c58110a82342a8b5e1cdbc34497 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 a1bde93c3628e6b3325a0b897c2dc2c5d180ea42..93d1b4ee12ebf3e2cbdd89ee87534929db645a44 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 ==================== diff --git a/doc/release/smtk-21.09.rst b/doc/release/smtk-21.09.rst new file mode 100644 index 0000000000000000000000000000000000000000..2ab4de624ad678c90a465eea14cdfa9c1c98e829 --- /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. diff --git a/doc/requirements/dev.txt b/doc/requirements/dev.txt index fd3c0b0f03c05bdf540966db2de66cceb538ad6b..8f1ed1d4c03dc51d48515bfbacc9fa53f0585449 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 diff --git a/doc/smtk.doxyfile.in b/doc/smtk.doxyfile.in index 8f8467ace6e90e0a5069b342b1b0da92d72d9827..b84db938032331168d07d946189d0ef137bc5893 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/figures/task-terminology.png b/doc/userguide/figures/task-terminology.png new file mode 100644 index 0000000000000000000000000000000000000000..8e6215ee3c7aec76d3518825a095dad9bbc11f7a --- /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 0000000000000000000000000000000000000000..857f6a053054e5cac2c54a1136793cac2106325b --- /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 bcd785c26157a0243bc9e76b047c2771dd1ebcce..f8e411fc983179dc31ba3fbe335d76543e10caa7 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 new file mode 100644 index 0000000000000000000000000000000000000000..313c936ebb48da8feddaf2272ab9fa726e51960d --- /dev/null +++ b/doc/userguide/task/adaptors.rst @@ -0,0 +1,82 @@ +.. _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, +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 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 +""""""" + +.. code:: json + + { + "id": 1, + "type": "smtk::task::adaptor::ResourceAndRole", + "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 b574b380dc83cbb31976cee1fb37125d430e6676..82c235abe957a9a1255e9f1b43c03930d0443b73 100644 --- a/doc/userguide/task/classes.rst +++ b/doc/userguide/task/classes.rst @@ -16,48 +16,215 @@ 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. -Example: +Example +""""""" .. code:: json { "type": "smtk::task::Task", "title": "Instructions to users.", + "style": [ "unique-component-colors", "fancy-menu" ], "completed": false } -TaskNeedsResources ------------------- +FillOutAttributes +----------------- -The :smtk:`TaskNeedsResources ` class monitors +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 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; 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 +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 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 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: + +* ``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 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 +""""""" + +.. code:: json + + { + "type": "smtk::task::Group", + "title": "Perform the child tasks in order.", + "mode": "serial", + "children": [ + { + "id": 2, + "type": "smtk::task::Task", + "title": "Step 1." + }, + { + "id": 3, + "type": "smtk::task::Task", + "title": "Step 2." + } + ], + "adaptors": [ + { + "//": "How the parent configures its child." + "type": "smtk::task::adaptor::ResourceAndRole", + "from-tag": "simulation", + "from": 1, + "to": 2 + }, + { + "//": "How the parent configures its child." + "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": 2, + "to": 3 + }, + { + "//": "How a child task configures its parent's" + "//": "output. Be careful to avoid loops." + "type": "smtk::task::adaptor::PassComponents", + "from": 3, + "to": 1 + } + ] + } + + +GatherResources +--------------- + +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). 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: - * ``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: +Example +""""""" .. code:: json { - "type": "smtk::task::TaskNeedsResources", + "type": "smtk::task::GatherResources", "title": "Load a geometric model (or models) and a simulation template.", "resources": [ { diff --git a/doc/userguide/task/concepts.rst b/doc/userguide/task/concepts.rst index bb933e14b29a00a26524c66a96bfa3e087f1b6b3..c23393fdbf408adf5cbfc93590a9af31ddf270cb 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. @@ -27,9 +61,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,7 +76,34 @@ 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. 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 c4b9aaa2ae3dee629a9c73ca19ab19616e69b9b8..638b3081a926d65dff3635b6044b7967bcc87c56 100644 --- a/doc/userguide/task/index.rst +++ b/doc/userguide/task/index.rst @@ -1,15 +1,23 @@ .. _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 concepts.rst classes.rst + adaptors.rst diff --git a/doc/userguide/task/io.rst b/doc/userguide/task/io.rst new file mode 100644 index 0000000000000000000000000000000000000000..b0bba9cc42f87296bb9908e2d49f983051880ba4 --- /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/doc/userguide/workflow/concepts.rst b/doc/userguide/workflow/concepts.rst deleted file mode 100644 index 3aa7917805c33334d82a1eec53bdc928a708b4c7..0000000000000000000000000000000000000000 --- 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 1143ad6f952aa0c1c999792e13e395ffeb96f9c2..0000000000000000000000000000000000000000 --- 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/attribute/Item.cxx b/smtk/attribute/Item.cxx index f690a35cd176a767042c29230a90e22e7e6fed39..c97e42749799a520cac3eb201963ce77e4bd399b 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; diff --git a/smtk/attribute/json/jsonValueItem.h b/smtk/attribute/json/jsonValueItem.h index 474c5b1d193b57f38bd901d3901aec623b070de4..ff2715a2ce7b48393d3b56ed1ebc90a732b9c827 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; diff --git a/smtk/attribute/utility/Queries.cxx b/smtk/attribute/utility/Queries.cxx index d23353563993577cfdc2d74bf1488cf31ed443f1..8e6f5b33fc36c0f09c32fee060128b8d98ffeee6 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 d1cd54b4bd6116ec53db50bc0530f2325bb2ffc3..9ec518f834161084672aaf6cfd0b73657dce7a00 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/common/Deprecation.h b/smtk/common/Deprecation.h index 9911f882847b5fd3f795d2e5e024640069f34e79..34efd77917cfdbd8f637fdc5baec3b928149d809 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. @@ -54,7 +54,19 @@ #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, 9) +#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, 8, 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)) diff --git a/smtk/common/Instances.h b/smtk/common/Instances.h index 3932bf5696c9906918350f9767e22deff3d03e04..0ab0562970094a57e709c497a091a107e2b75900 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/common/TypeContainer.h b/smtk/common/TypeContainer.h index 36f8a1ae9f26aec7babd4f989af90da23dd7e202..751e003ccc377d5fc389771d1f383e92febceefd 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/common/VersionMacros.h b/smtk/common/VersionMacros.h index d125be56c39b881409adc8bbc8d6c1fee01dbb2f..03cf99656a1cfef65dd1d2a63551ce37de146e58 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 diff --git a/smtk/doc.h b/smtk/doc.h index 53eeb9428bfdeaeffe543c2700eaa3e2909420a9..56dd6eed080f4090c29f51990636769a604b4825 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/extension/paraview/appcomponents/VisibilityBadge.cxx b/smtk/extension/paraview/appcomponents/VisibilityBadge.cxx index c98ee0c5d7c0f39c85274af981f0dc70c1c23cae..cc15326818f699eb5cc7cc79db45f444addac00b 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); } } diff --git a/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx b/smtk/extension/paraview/appcomponents/pqSMTKAttributePanel.cxx index cdc7cd4bddc20f8c7aace35774936269fa282e53..6d99f0dc2cab2a1da9d5d296df46a989725f73ff 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 d654e9e36b61f74365c5637bb4b7b96a904e44dc..97d9e934f8928ee898d306cf9aea8957b2962eee 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; diff --git a/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx b/smtk/extension/paraview/appcomponents/pqSMTKResourceBrowser.cxx index 9cc4c91151c7438c88462dd3b08d99d7907a0785..d24fdeb4a6534b8830142b70839923e825f83161 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->configuration()); 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 777a87269f930569d64e3d746449abdb36ab7ad6..cd1fa4d1e84921064cfed7db9d4a3f5c70b53246 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 ffd17fd8b80ba4d3900f43672ff244c786f086ad..edbffe89f542c66c96595b87757904213adf1cc8 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) @@ -260,7 +258,7 @@ void smtkAssignColorsView::prepPaletteChooser() void smtkAssignColorsView::createWidget() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -360,7 +358,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 +569,5 @@ void smtkAssignColorsView::requestModelEntityAssociation() void smtkAssignColorsView::setInfoToBeDisplayed() { - m_infoDialog->displayInfo(this->getObject()); + m_infoDialog->displayInfo(this->configuration()); } diff --git a/smtk/extension/paraview/operators/smtkAssignColorsView.h b/smtk/extension/paraview/operators/smtkAssignColorsView.h index 13afec5bf559dbdb4c466b10c3adb6ed7ecd96f6..48a06835a4c1d738d1d684abfdd318e735260291 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/paraview/widgets/qtSimpleExpressionEvaluationView.cxx b/smtk/extension/paraview/widgets/qtSimpleExpressionEvaluationView.cxx index 72e5f16589203ed82068455eb859917719ed314d..e6f1fcde53541856622e5114feaaf58bb633e514 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/CMakeLists.txt b/smtk/extension/qt/CMakeLists.txt index f933973b7575b20935f9dad5a9fcd68d8547eadb..77ab37de33ff3ca1f13f202cce2e7da656945bfa 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 7f75f422bbea109cd027bef691d66303a749839f..214392f81178616eb8015e254bc1adf39f6684a4 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 55218e3259bb2b25d3e4ee6a0b798109745df1e9..38311eb803dacc64634f9bd0b145faa44f527d76 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,15 +35,20 @@ #include #include +#include using namespace smtk::attribute; 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) @@ -62,7 +69,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; @@ -74,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); @@ -94,6 +101,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()); @@ -119,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/qtAssociation2ColumnWidget.cxx b/smtk/extension/qt/qtAssociation2ColumnWidget.cxx index 2590ca39ba4e119c2b47eb471a7ed4793f1f012c..1fb01e193e1d64007b22ccd15c05e4ab645691fe 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)) @@ -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/qtAssociationView.cxx b/smtk/extension/qt/qtAssociationView.cxx index f41009629e7b2e1ced30895b8305dfd38e57e161..941809a739f075768499871971deab47dd55e0b4 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) @@ -251,13 +252,13 @@ void qtAssociationView::onShowCategory() void qtAssociationView::getAllDefinitions() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { 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 690af18ab91068fcdc711daa2429469b41869f5f..8c6189f4532eb0c74ace9b862bfc94859a5b5992 100644 --- a/smtk/extension/qt/qtAttributeEditorDialog.cxx +++ b/smtk/extension/qt/qtAttributeEditorDialog.cxx @@ -48,7 +48,11 @@ 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); + 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); diff --git a/smtk/extension/qt/qtAttributeView.cxx b/smtk/extension/qt/qtAttributeView.cxx index d6701a4467debaed5928af09b9572e5a1e7d1455..aa819370788122b4ab9c7bbc6c771d4b429a5e81 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) @@ -208,7 +212,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 +258,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; @@ -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(); @@ -584,7 +588,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; @@ -671,7 +675,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); @@ -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); @@ -1070,10 +1074,10 @@ 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)); + this->attributeResource()->findAttribute(smtk::common::UUID(activeAttUuid)); if (activeAtt) { @@ -1242,13 +1246,13 @@ int qtAttributeView::currentViewBy() void qtAttributeView::getAllDefinitions() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { 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(); } @@ -1471,6 +1475,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, @@ -1497,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; } @@ -1514,43 +1527,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")) { - // 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); + item->setFlags(itemFlags); + } + else + { + Qt::ItemFlags itemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + item->setFlags(itemFlags); } } } @@ -1570,24 +1580,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 +1606,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 c957c2cb4696656478c69a44a8fd36a7c7b1e38f..9733542bee8c075d9ae4c75adb4e7be6e8b64b51 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, diff --git a/smtk/extension/qt/qtBaseAttributeView.cxx b/smtk/extension/qt/qtBaseAttributeView.cxx index f4d32e19e662750bf2d610f116f8d95d31771e3e..0e23274f1c6dd59a10e6282e2c3dde0913988a43 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->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. @@ -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()); } @@ -549,10 +560,10 @@ 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(); + const attribute::ResourcePtr attResource = this->attributeResource(); this->topLevelPrepAdvanceLevels(view); this->topLevelPrepConfigurations(view, attResource); @@ -634,7 +645,7 @@ void qtBaseAttributeView::showAdvanceLevel(int level) return; } - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -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 e45ff38513d9210dae4ab89d6961d76fb0088fda..4a745062f17b315b10f449461061e33c2b4f728b 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 46acff7c06da33a262afbaff233ee31630ef01a0..6b418003e0890ff8bb1aa6c6383b39f157f47b59 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->configuration(); + 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"); } } @@ -49,7 +56,7 @@ qtBaseView::~qtBaseView() void qtBaseView::makeTopLevel() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -111,5 +118,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 f0eb154e1103abac8694f1496abc458c3f3263d8..1c21e1b304577fda6ab8e0a4a38d7ad90952a438 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" @@ -33,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 @@ -81,18 +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::view::ConfigurationPtr getObject() const { return m_viewInfo.m_view; } + SMTK_DEPRECATED_IN_21_09("Method has been replaced by qtBaseView::configuration") + 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; } @@ -113,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(); @@ -150,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 72faca772dd7c932b3e31d082189330cdbf2ce2a..5831ae501c957dd2cd3653f437fe8309639dc269 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) @@ -67,7 +71,7 @@ qtCategorySelectorView::~qtCategorySelectorView() void qtCategorySelectorView::createWidget() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -83,8 +87,8 @@ void qtCategorySelectorView::createWidget() bool qtCategorySelectorView::createChildren() { - smtk::view::ConfigurationPtr view = this->getObject(); - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::view::ConfigurationPtr view = this->configuration(); + 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) { @@ -144,11 +148,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 +175,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/qtDiscreteValueEditor.cxx b/smtk/extension/qt/qtDiscreteValueEditor.cxx index b4df9d3b66f3df971d87a3ddc0f4fe8b78fe1f8c..ceb7f3412e149001141a866d2f41049a6d01a0ce 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 3be1e46834ce1ab07d56136f536cf8fd55c97cc1..078122f0605eab203ec4ce9c9bf7957cbaf55d17 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); @@ -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) @@ -164,7 +168,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 +210,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 +261,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; @@ -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) { @@ -325,8 +330,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 +435,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 +446,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 +459,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 +509,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 +554,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 +569,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/qtInputsItem.cxx b/smtk/extension/qt/qtInputsItem.cxx index e66f153508ab88135e3f778141b3acb3d438a9c0..7a3b7b72cc7cbff6a284b3b6f7593b87d0941caa 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 908e8b1ec8bfe9902f33ed4d02cc101db0ca2bba..06a4e40a0b93c1ed422372fbbe777cf4c8e5e1c3 100644 --- a/smtk/extension/qt/qtInstancedView.cxx +++ b/smtk/extension/qt/qtInstancedView.cxx @@ -55,9 +55,13 @@ public: qtBaseView* qtInstancedView::createViewWidget(const smtk::view::Information& info) { - qtInstancedView* view = new qtInstancedView(info); - 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) @@ -81,7 +85,7 @@ qtInstancedView::~qtInstancedView() void qtInstancedView::createWidget() { - if (!this->getObject()) + if (!this->configuration()) { return; } @@ -96,7 +100,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); @@ -130,13 +134,13 @@ void qtInstancedView::onShowCategory() void qtInstancedView::updateUI() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; } - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); std::string attName, defName; smtk::attribute::AttributePtr att; smtk::attribute::DefinitionPtr attDef; @@ -287,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/qtInstancedView.h b/smtk/extension/qt/qtInstancedView.h index 56a109af1507ed1d92e192511ca937a76f279884..d14034b72a411c28157221efb0dfe18b1717af24 100644 --- a/smtk/extension/qt/qtInstancedView.h +++ b/smtk/extension/qt/qtInstancedView.h @@ -35,6 +35,9 @@ class SMTKQTEXT_EXPORT qtInstancedView : public qtBaseAttributeView public: smtkTypenameMacro(qtInstancedView); + ///\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); qtInstancedView(const smtk::view::Information& info); diff --git a/smtk/extension/qt/qtItem.cxx b/smtk/extension/qt/qtItem.cxx index fff67ed07951ba4ba2df93e7490aa7d8b476cc05..1866ab9bab92d14354e6c4244155af114ec1cc8e 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 a604612799e6be3a5a90de20d6fed4f071f676ad..ec6b6bcb9cf4d69439fdc7f4c755b6332d08af07 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 ae411115ee31b392d09f72e282d1452ea35ebc7e..711665cc17f8c989b70238314f44da7c42dc0072 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,15 +116,32 @@ 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: ~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 +214,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) @@ -257,14 +279,14 @@ qtModelEntityAttributeView::attDefinitionMap() const void qtModelEntityAttributeView::createWidget() { - auto view = this->getObject(); + auto view = this->configuration(); if (view == nullptr) { return; } 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 +389,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 +466,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; @@ -461,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); @@ -530,7 +551,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 +559,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) @@ -548,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 @@ -566,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(); } @@ -699,13 +727,13 @@ void qtModelEntityAttributeView::displayAttribute(smtk::attribute::AttributePtr void qtModelEntityAttributeView::getAllDefinitions() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; } - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::attribute::ResourcePtr resource = this->attributeResource(); std::string attName, defName, val; smtk::attribute::AttributePtr att; @@ -789,19 +817,9 @@ 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 = - this->Internals->getCurrentDefs(this->uiManager()); + this->Internals->getCurrentDefs(this->attributeResource()); return currentDefs.isEmpty(); } diff --git a/smtk/extension/qt/qtModelEntityAttributeView.h b/smtk/extension/qt/qtModelEntityAttributeView.h index 16f3b620510049c6a8357801bee46531c85b39a0..d76c4722a6a610ec19b7471e2eb6eec06c90a212 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; }; diff --git a/smtk/extension/qt/qtOperationView.cxx b/smtk/extension/qt/qtOperationView.cxx index 323843dd4f31fb55a9b5eb43f9b12b8fa091cc6c..ed0bd84a110af50e1637d76ca55c0a3ec4b2416b 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,28 +51,31 @@ 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->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (view) { this->Internals->m_instancedViewDef->copyContents(*view); @@ -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 4c72f5c9590d6776c0bd91df915c0abd5ddc6d04..2903eb84468b64664e3d9bcd66a3177ac2e8f86f 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/qtReferenceItem.h b/smtk/extension/qt/qtReferenceItem.h index eef104c0387d347bab5e045b00ead085ef55ffbe..bcdf73447595c64ed79c015d11df1d70b432b9ae 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/qtReferenceItemEditor.cxx b/smtk/extension/qt/qtReferenceItemEditor.cxx index 1ba8b8e6fd40f0d1674a877c11c7812faba03051..b33cd1e1e0f9d0735c4f17d501a1a800029e3bf5 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 @@ -743,9 +751,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 3e47af0f4c319d87e7035fb97fdb12c76633f779..7409af4d00c4c1e3d978797ebcda9f38e618b786 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->configuration(); + 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 4aeeb493a9e35dd904a99dae887a95c5967db7a5..64b201d4126a7175356ced5538be3931f7c51149 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/qtResourceItem.cxx b/smtk/extension/qt/qtResourceItem.cxx index fe2d6bb24f3b10e7a041b0367343801e9aeb4cf9..ff083982420e3fad13df51536dacf2c4f92178fe 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/qtSMTKUtilities.h b/smtk/extension/qt/qtSMTKUtilities.h index 01ea4c6ba0a60678e8f39d1e178053c1cb2577f5..a57298a8852b91069437f70005d6ebde7f5db9ff 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 f28070c4634f4d821e042b3e86a73954143a5257..58f8bce7c64580aecb2bf8bed49a74aee0e24c7d 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) @@ -71,7 +75,7 @@ qtSelectorView::~qtSelectorView() void qtSelectorView::createWidget() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { return; @@ -94,13 +98,13 @@ 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); // 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); @@ -172,8 +176,8 @@ bool qtSelectorView::isEmpty() const bool qtSelectorView::createChildren() { - smtk::view::ConfigurationPtr view = this->getObject(); - smtk::attribute::ResourcePtr resource = this->uiManager()->attResource(); + smtk::view::ConfigurationPtr view = this->configuration(); + 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) { @@ -245,11 +250,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 +277,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 92d62445942847ec55c5bfef9c6f5ef8a4c94193..2f7dc902bc97d4e0c445675863922599462ff07f 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) @@ -99,7 +103,7 @@ qtSimpleExpressionView::~qtSimpleExpressionView() void qtSimpleExpressionView::createWidget() { - if (!this->getObject()) + if (!this->configuration()) { return; } @@ -110,7 +114,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); @@ -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)); @@ -774,12 +778,12 @@ void qtSimpleExpressionView::onShowCategory() void qtSimpleExpressionView::updateUI() { - smtk::view::ConfigurationPtr view = this->getObject(); + smtk::view::ConfigurationPtr view = this->configuration(); if (!view) { 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 f2ae607ac4476ddb888a2841890bed0fcfcaeddc..117a8bec0d7c1da2cf80222aed09ecf93873c1b7 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,21 +221,27 @@ 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->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) @@ -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) { @@ -447,7 +453,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)); } } @@ -888,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 137d7098b0c55d516c167547779a1f3ad2f84951..861003e8e68fb4ac2d86f2888b4961f45c9fc38a 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.h b/smtk/extension/qt/qtViewInterface.h deleted file mode 100644 index b4892d4e17dcbcca4dca0e2216f07bbe0127508d..0000000000000000000000000000000000000000 --- 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/extension/qt/qtVoidItem.cxx b/smtk/extension/qt/qtVoidItem.cxx index 5c09cdd1b3d41e7fa29dc8bfe00bffd77dcdb19f..4cb1183cea179744a6636e20398c185cc9cf1f33 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); diff --git a/smtk/project/ResourceContainer.h b/smtk/project/ResourceContainer.h index 5b4eef13c51fa1caddd30ceee183549b23050973..8cecd151a1e962d09711234d5d20a9f4f4159e8a 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/operators/Read.cxx b/smtk/project/operators/Read.cxx index a1b63030c23e0e74a20f19eab16bf4839b1403c0..5ae800bc7496a3a999e69c69bfa91c82f8008703 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); diff --git a/smtk/project/plugin/pqSMTKProjectPanel.cxx b/smtk/project/plugin/pqSMTKProjectPanel.cxx index c9203d80a1dc06704cacc2f0475a6771874eb73b..08b13b41b14e7279487dbf0e9547b37de3f5aac3 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/project/testing/cxx/TestProject.cxx b/smtk/project/testing/cxx/TestProject.cxx index 405be05848b1980f4cefbe55a70860c251c92d17..d416d56c13a99cd9357f72eb1f2e21d2a93cd85f 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); diff --git a/smtk/resource/PersistentObject.h b/smtk/resource/PersistentObject.h index bd368eee73d23e2ecd6f740292f521bbaa62e1d9..2fd5be8c66fbfc7bcc8760e3524eafddefa1f677 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 f8e2359e298d08f86027e798d416cef7f68f95c3..7b39a65001ee760a1ec0e01de78af7fe3889a147 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; }; diff --git a/smtk/session/polygon/CMakeLists.txt b/smtk/session/polygon/CMakeLists.txt index c64eb60b5269bbabf705575e35a28852271702cc..17d28a86cbe6aa295d87f876d1bef338a0235ac9 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 diff --git a/smtk/session/polygon/operators/ImportPPG.cxx b/smtk/session/polygon/operators/ImportPPG.cxx index 6e1108dc212b61c10b1bf169dbbcfb9305eb46f6..424706837435072d4174c646448bc725e3dc2273 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) diff --git a/smtk/session/polygon/operators/Read.cxx b/smtk/session/polygon/operators/Read.cxx index 3152b174cf7a10627517b7548f52f4f81b57a24d..e32bba1eb1f96534edc40b838b1c7a8d0ffcf64e 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 6b2a1bab40bfcfe34f3b159f4b570648f4605fbc..d267544ac272d1b4b3f8fa95e5cca143c8a84b54 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; }; diff --git a/smtk/task/Adaptor.cxx b/smtk/task/Adaptor.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7606000c1f812cc65118d9d3b5e6d23d6178f671 --- /dev/null +++ b/smtk/task/Adaptor.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/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, Task* from, Task* 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 0000000000000000000000000000000000000000..4df51a3f1e2ea93ef0f0039b1e7f71f3363fdd03 --- /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, Task* from, Task* 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. + Task* from() const { return m_from; } + /// The task to which this adaptor applies configuration parameters. + Task* to() const { return m_to; } + +protected: + Task* m_from; + Task* 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 f6096393c2985a560772097fbf37c8df2ccfb412..93b8bb9c4a2baa9c2b2a44be9257c16138714a91 100644 --- a/smtk/task/CMakeLists.txt +++ b/smtk/task/CMakeLists.txt @@ -1,20 +1,53 @@ +# 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 + Adaptor.cxx + Instances.cxx Manager.cxx Registrar.cxx - Task.cxx - TaskNeedsResources.cxx + adaptor/ResourceAndRole.cxx + json/Configurator.cxx + json/Helper.cxx + json/jsonManager.cxx + json/jsonAdaptor.cxx ) set(taskHeaders Active.h + Adaptor.h Instances.h Manager.h Registrar.h - Task.h - TaskNeedsResources.h + adaptor/ResourceAndRole.h + json/Configurator.h + json/Configurator.txx + json/Helper.h + json/jsonAdaptor.h + json/jsonManager.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/ConfigureOperation.cxx b/smtk/task/ConfigureOperation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b90c188dde95cd188462ea3d45819df71e633a9c --- /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 0000000000000000000000000000000000000000..385e7d6e13af1abd6b39d1d46540905640a9fd7a --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..31c7152d12627d232933d2f25edd7d593a19ba3a --- /dev/null +++ b/smtk/task/FillOutAttributes.cxx @@ -0,0 +1,365 @@ +//========================================================================= +// 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()); + } +} + +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) + { + 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/FillOutAttributes.h b/smtk/task/FillOutAttributes.h new file mode 100644 index 0000000000000000000000000000000000000000..b16ffef0946a254e3a68682f3f63c20ab5400ae3 --- /dev/null +++ b/smtk/task/FillOutAttributes.h @@ -0,0 +1,125 @@ +//========================================================================= +// 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; + using ConstAttributeSetVisitor = 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; + + /// 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: + 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/GatherResources.cxx b/smtk/task/GatherResources.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4203ecefb53c8ce0c879260c589f4203234443b1 --- /dev/null +++ b/smtk/task/GatherResources.cxx @@ -0,0 +1,212 @@ +//========================================================================= +// 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/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/io/Logger.h" + +#include + +namespace smtk +{ +namespace task +{ + +GatherResources::GatherResources() = default; + +GatherResources::GatherResources( + const Configuration& config, + const smtk::common::Managers::Ptr& managers) + : Task(config, managers) + , m_managers(managers) +{ + this->configure(config); +} + +GatherResources::GatherResources( + const Configuration& config, + const PassedDependencies& dependencies, + const smtk::common::Managers::Ptr& managers) + : Task(config, dependencies, managers) + , m_managers(managers) +{ + this->configure(config); +} + +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"); + if (rsrcSpecs.is_array()) + { + for (const auto& spec : rsrcSpecs) + { + if (!spec.contains("role")) + { + continue; + } + + ResourceSet resourceSet; + spec.get_to(resourceSet); + m_resourcesByRole[resourceSet.m_role] = spec.get(); + } + } + } + if (m_managers) + { + if (auto resourceManager = m_managers->get()) + { + m_observer = resourceManager->observers().insert( + [this](const smtk::resource::Resource& resource, smtk::resource::EventType event) { + this->updateResources(const_cast(resource), event); + }, + /* priority */ 0, + /* initialize */ true, + "GatherResources monitors resources and their roles."); + } + } + if (!m_resourcesByRole.empty()) + { + this->internalStateChanged(this->computeInternalState()); + } +} + +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) + { + 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 GatherResources::updateResources( + smtk::resource::Resource& resource, + smtk::resource::EventType event) +{ + bool resourceSetsUpdated = false; + auto resourcePtr = resource.shared_from_this(); + switch (event) + { + case smtk::resource::EventType::ADDED: + { + if (m_autoconfigure) + { + // 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 (!it->second.m_validator || it->second.m_validator(resource, it->second)) + { + it->second.m_resources.insert(resourcePtr); + resourceSetsUpdated = true; + } + } + } + } + break; + case smtk::resource::EventType::REMOVED: + { + // Remove the resource from the appropriate entry. + const std::string& role = smtk::project::detail::role(resourcePtr); + auto it = m_resourcesByRole.find(role); + if (it != m_resourcesByRole.end()) + { + resourceSetsUpdated = it->second.m_resources.erase(resourcePtr) > 0; + } + } + break; + case smtk::resource::EventType::MODIFIED: + // TODO: Maybe a role was removed? + break; + } + if (resourceSetsUpdated) + { + this->internalStateChanged(this->computeInternalState()); + } +} + +State GatherResources::computeInternalState() const +{ + State s = State::Completable; + for (const auto& entry : m_resourcesByRole) + { + const auto& resourceSet(entry.second); + if (resourceSet.m_resources.size() < static_cast(resourceSet.m_minimumCount)) + { + s = State::Incomplete; + } + else if ( + resourceSet.m_maximumCount >= 0 && + resourceSet.m_resources.size() > static_cast(resourceSet.m_maximumCount)) + { + s = State::Incomplete; + } + } + return s; +} + +} // namespace task +} // namespace smtk diff --git a/smtk/task/TaskNeedsResources.h b/smtk/task/GatherResources.h similarity index 63% rename from smtk/task/TaskNeedsResources.h rename to smtk/task/GatherResources.h index 06d261101e8bfddd331bcebd2a9a3c7a13ecd244..cd5a80d2beba908b93200177b5cc213370ed2d8f 100644 --- a/smtk/task/TaskNeedsResources.h +++ b/smtk/task/GatherResources.h @@ -7,34 +7,36 @@ // 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" #include "smtk/resource/Resource.h" #include "smtk/task/Task.h" +#include "smtk/common/Visit.h" + 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); /// 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. @@ -45,31 +47,51 @@ 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 ResourceSetVisitor = 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); + /// 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: /// Respond to resource changes that may change task state. void updateResources(smtk::resource::Resource& resource, smtk::resource::EventType event); @@ -77,11 +99,12 @@ 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; + std::map m_resourcesByRole; }; } // namespace task } // namespace smtk -#endif // smtk_task_TaskNeedsResources_h +#endif // smtk_task_GatherResources_h diff --git a/smtk/task/Group.cxx b/smtk/task/Group.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d998926c263d8ab583c71ecc5dd0806eccd0ea0e --- /dev/null +++ b/smtk/task/Group.cxx @@ -0,0 +1,231 @@ +//========================================================================= +// 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/Group.h" + +#include "smtk/project/ResourceContainer.h" + +#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" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/Resource.h" + +#include "smtk/resource/Manager.h" + +#include + +namespace smtk +{ +namespace task +{ + +Group::Group() = default; + +Group::Group(const Configuration& config, const smtk::common::Managers::Ptr& managers) + : Task(config, managers) + , m_managers(managers) +{ + this->configure(config); +} + +Group::Group( + const Configuration& config, + const PassedDependencies& dependencies, + const smtk::common::Managers::Ptr& managers) + : Task(config, dependencies, managers) + , m_managers(managers) +{ + this->configure(config); +} + +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 +{ + std::vector result; + result.reserve(m_children.size()); + for (const auto& entry : m_children) + { + result.push_back(entry.first); + } + 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 != 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 new file mode 100644 index 0000000000000000000000000000000000000000..62849bb3ccb712010b9ef7d0a3635e0b3a30f9ca --- /dev/null +++ b/smtk/task/Group.h @@ -0,0 +1,90 @@ +//========================================================================= +// 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_Group_h +#define smtk_task_Group_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 +{ +class Adaptor; + +/**\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. + * + */ +class SMTKCORE_EXPORT Group : public Task +{ +public: + smtkTypeMacro(smtk::task::Group); + smtkSuperclassMacro(smtk::task::Task); + smtkCreateMacro(smtk::task::Task); + + 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); + + ~Group() override = default; + + 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 + +#endif // smtk_task_Group_h diff --git a/smtk/task/Instances.cxx b/smtk/task/Instances.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4d60fdae59eba88106eec9ff9b8adbde495b390d --- /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 5f362cd9492259c6e041a5bdffa4b7d7f518f02a..8b10b69f14810ce53315ca60d3055c2b26ea1559 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/Manager.cxx b/smtk/task/Manager.cxx index f4ed4c6c289042e9a9a01642f8276e5f3a89d500..3b7f7bdb4ef7530f22b75813d565009c6a6dacf5 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 7e46c7aea7ac5029c48b5afc1d012de647c8ebd3..153b87c205bf8fb1aee856ee25d275e23ac071b4 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 7786ea78f695cd2a130412e00362554425d903e6..c5d35b34f291d179d32ae07a8dfa016166f2a59d 100644 --- a/smtk/task/Registrar.cxx +++ b/smtk/task/Registrar.cxx @@ -10,8 +10,21 @@ // //============================================================================= #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/TaskNeedsResources.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" @@ -22,7 +35,11 @@ namespace smtk namespace task { -using TaskList = 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) { @@ -51,14 +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(); + 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(); + 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 2cd73a0d886ec8eaa96890c813710212048b6f92..59e13566ade97eb3d696f4efdda9e23365d18b63 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 d710b9badef350e40941fc20ab96fce937adf046..a8f3301d60fb6333879c1b991ec4bbca26c47ff8 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; @@ -75,6 +154,7 @@ State Task::state() const case State::Incomplete: s = State::Unavailable; break; + case State::Irrelevant: case State::Completable: case State::Completed: break; @@ -95,6 +175,7 @@ bool Task::markCompleted(bool completed) { switch (this->state()) { + case State::Irrelevant: // fall through case State::Unavailable: // fall through case State::Incomplete: return false; @@ -139,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), @@ -146,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) @@ -159,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) { @@ -172,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 5f31dd6ad7e26f709cdb09f77520874ba290c1fa..65973a3071eb72bd964e7814138b481db602c84c 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/TaskNeedsResources.cxx b/smtk/task/TaskNeedsResources.cxx deleted file mode 100644 index 4310d8d48422f74cc7b6d584612d5ccbab793ae0..0000000000000000000000000000000000000000 --- a/smtk/task/TaskNeedsResources.cxx +++ /dev/null @@ -1,208 +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/task/TaskNeedsResources.h" - -#include "smtk/operation/Manager.h" -#include "smtk/operation/SpecificationOps.h" -#include "smtk/project/ResourceContainer.h" - -#include - -namespace smtk -{ -namespace task -{ - -void to_json(json& j, const TaskNeedsResources::Predicate& 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, TaskNeedsResources::Predicate& 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::Predicate&) - { return true; }; - */ - } -} - -TaskNeedsResources::TaskNeedsResources() = default; - -TaskNeedsResources::TaskNeedsResources( - const Configuration& config, - const smtk::common::Managers::Ptr& managers) - : Task(config, managers) - , m_managers(managers) -{ - this->configure(config); -} - -TaskNeedsResources::TaskNeedsResources( - const Configuration& config, - const PassedDependencies& dependencies, - const smtk::common::Managers::Ptr& managers) - : Task(config, dependencies, managers) - , m_managers(managers) -{ - this->configure(config); -} - -void TaskNeedsResources::configure(const Configuration& config) -{ - if (config.contains("resources")) - { - auto rsrcSpecs = config.at("resources"); - if (rsrcSpecs.is_array()) - { - for (const auto& spec : rsrcSpecs) - { - if (!spec.contains("role")) - { - continue; - } - - Predicate predicate; - spec.get_to(predicate); - m_resourcesByRole[predicate.m_role] = spec.get(); - } - } - } - if (m_managers) - { - if (auto resourceManager = m_managers->get()) - { - m_observer = resourceManager->observers().insert( - [this](const smtk::resource::Resource& resource, smtk::resource::EventType event) { - this->updateResources(const_cast(resource), event); - }, - /* priority */ 0, - /* initialize */ true, - "TaskNeedsResources monitors results for resources and their roles."); - } - } - if (!m_resourcesByRole.empty()) - { - this->internalStateChanged(this->computeInternalState()); - } -} - -void TaskNeedsResources::updateResources( - smtk::resource::Resource& resource, - smtk::resource::EventType event) -{ - bool predicatesUpdated = false; - auto resourcePtr = resource.shared_from_this(); - switch (event) - { - 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 (!it->second.m_validator || it->second.m_validator(resource, it->second)) - { - it->second.m_resources.insert(resourcePtr); - predicatesUpdated = true; - } - } - } - break; - case smtk::resource::EventType::REMOVED: - { - // Remove the resource from the appropriate entry. - const std::string& role = smtk::project::detail::role(resourcePtr); - auto it = m_resourcesByRole.find(role); - if (it != m_resourcesByRole.end()) - { - predicatesUpdated = it->second.m_resources.erase(resourcePtr) > 0; - } - } - break; - case smtk::resource::EventType::MODIFIED: - // TODO - break; - } - if (predicatesUpdated) - { - this->internalStateChanged(this->computeInternalState()); - } -} - -State TaskNeedsResources::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)) - { - s = State::Incomplete; - } - else if ( - predicate.m_maximumCount >= 0 && - predicate.m_resources.size() > static_cast(predicate.m_maximumCount)) - { - s = State::Incomplete; - } - } - return s; -} - -} // namespace task -} // namespace smtk diff --git a/smtk/task/adaptor/Instances.h b/smtk/task/adaptor/Instances.h new file mode 100644 index 0000000000000000000000000000000000000000..92b2eeb98c4824d16426f316d38980bd6c6b5527 --- /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*, // Source task (from) + smtk::task::Task* // 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 0000000000000000000000000000000000000000..7ce4a8547f49f1b66a490c8987d104e7b4ca0116 --- /dev/null +++ b/smtk/task/adaptor/ResourceAndRole.cxx @@ -0,0 +1,196 @@ +//========================================================================= +// 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/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" + +#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, Task* from, Task* to) + : Superclass(config, from, to) +{ + this->configureSelf(config); +} + +bool ResourceAndRole::reconfigureTask() +{ + bool didChange = false; + 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 = dynamic_cast(source)) + { + 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) + { + auto resource_locked = resource.lock(); + if (resource_locked) + { + role_config.insert(resource_locked); + } + } + return smtk::common::Visit::Continue; + }); + // Look for matching roles in attribute sets and populate with + // the gathered resources. + 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) + { + auto resource = weakResource.lock(); + if (resource) + { + role_config.insert(resource); + } + } + } + } + // Look for matching roles in attribute sets and populate with + // the gathered resources. + fill->visitAttributeSets(updateFillOutAttributes); + if (didChange) + { + fill->internalStateChanged(fill->computeInternalState()); + } + } + else + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Cannot configure resource and role on a \"" << (dest ? dest->typeName() : "null") + << "."); + } + } + else + { + smtkErrorMacro( + smtk::io::Logger::instance(), + "Cannot adapt resource and role from a \"" << source->typeName() << "\"."); + } + } + return didChange; +} + +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 +} // namespace smtk diff --git a/smtk/task/adaptor/ResourceAndRole.h b/smtk/task/adaptor/ResourceAndRole.h new file mode 100644 index 0000000000000000000000000000000000000000..6c0f3d4e0f1e8cea530f22585be402b7c92bd8f5 --- /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, Task* from, Task* 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); + + std::string m_fromTag; + std::string m_toTag; +}; +} // 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 0000000000000000000000000000000000000000..4fc977de3b074ce3757376fb4b7f654f4b4c0837 --- /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 0000000000000000000000000000000000000000..bd90e735c357c848c0c3019f546e955f5ae103cb --- /dev/null +++ b/smtk/task/json/Configurator.h @@ -0,0 +1,156 @@ +//========================================================================= +// 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; + /// 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; + + // 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 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. + SwizzleId swizzleId(const ObjectType* object); + + /// Return the pointer to an object given its swizzled ID (or null). + ObjectType* unswizzle(SwizzleId 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::map m_swizzleBck; + SwizzleId m_nextSwizzle = 1; + SwizzleId m_nextNested = -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 0000000000000000000000000000000000000000..1d9ec1e671e9657fac7011b044461c81627a1a3a --- /dev/null +++ b/smtk/task/json/Configurator.txx @@ -0,0 +1,224 @@ +//========================================================================= +// 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; +} + +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 +typename Configurator::SwizzleId 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; + } + SwizzleId 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(SwizzleId 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 new file mode 100644 index 0000000000000000000000000000000000000000..a2d92954711506604dbc39f1389fba5eaedd7b89 --- /dev/null +++ b/smtk/task/json/Helper.cxx @@ -0,0 +1,157 @@ +//========================================================================= +// 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/task/json/Configurator.txx" + +#include "smtk/io/Logger.h" + +#include +#include + +namespace +{ +std::mutex g_types; +thread_local std::vector> g_instanceStack; +} // anonymous namespace + +namespace smtk +{ +namespace task +{ +namespace json +{ + +Helper::Helper() + : m_tasks(this) + , m_adaptors(this) +{ +} + +Helper::~Helper() = default; + +Helper& Helper::instance() +{ + if (g_instanceStack.empty()) + { + g_instanceStack.emplace_back(new Helper); + } + 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() +{ + return m_tasks; +} + +Configurator& Helper::adaptors() +{ + return m_adaptors; +} + +void Helper::setManagers(const smtk::common::Managers::Ptr& managers) +{ + m_managers = managers; +} + +smtk::common::Managers::Ptr Helper::managers() +{ + return m_managers; +} + +void Helper::clear() +{ + m_tasks.clear(); + m_adaptors.clear(); +} + +Helper::json Helper::swizzleDependencies(const Task::PassedDependencies& deps) +{ + auto ids = Helper::json::array({}); + for (const auto& dep : deps) + { + if (dep) + { + SwizzleId id = m_tasks.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* ptr = m_tasks.unswizzle(id); + if (!ptr) + { + smtkWarningMacro( + smtk::io::Logger::instance(), "No task or null task for ID " << taskId << ". Skipping."); + } + deps.insert(ptr->shared_from_this()); + } + return deps; +} + +void Helper::setAdaptorTaskIds(SwizzleId fromId, SwizzleId toId) +{ + m_adaptorFromId = fromId; + m_adaptorToId = toId; +} + +void Helper::clearAdaptorTaskIds() +{ + m_adaptorFromId = ~static_cast(0); + m_adaptorToId = ~static_cast(0); +} + +std::pair Helper::getAdaptorTasks() +{ + Task::Ptr fromPtr; + auto* from = m_tasks.unswizzle(m_adaptorFromId); + auto* to = m_tasks.unswizzle(m_adaptorToId); + return std::make_pair(from, to); +} + +} // 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 0000000000000000000000000000000000000000..b8bd188db70114e9b44add0ce877c96035fb41fd --- /dev/null +++ b/smtk/task/json/Helper.h @@ -0,0 +1,143 @@ +//========================================================================= +// 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/json/Configurator.h" + +#include "smtk/task/Adaptor.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: + /// 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". + /// + /// 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(); + + /// 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 + /// 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(); + + /// 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. + /// Additionally, calling it after each of these tasks is recommended since + /// it will free memory. + void clear(); + + // --- 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. + json swizzleDependencies(const Task::PassedDependencies& deps); + + /// Return a deserialization of de-swizzled task-references. + Task::PassedDependencies unswizzleDependencies(const json& ids) const; + + /// 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(); + /// 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; + 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 +} // 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 0000000000000000000000000000000000000000..84fdc2a161f6a76c8a40540777fe50bd1dbb72fb --- /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()) }, + { "to", helper.tasks().swizzleId(adaptor->to()) } }; + } + 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 0000000000000000000000000000000000000000..8b15cf94f174bc496e4584b8de32c6ccba4764ac --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..bb5421d8837494a4766becf52c8f35fb4528e1fb --- /dev/null +++ b/smtk/task/json/jsonFillOutAttributes.cxx @@ -0,0 +1,90 @@ +//========================================================================= +// 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); + } + 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); + } +} + +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](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 0000000000000000000000000000000000000000..5d40ad11255bc25628672ea3d1eb313eaf6a6c74 --- /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/jsonGatherResources.cxx b/smtk/task/json/jsonGatherResources.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f6af8f72966ed7dd31e36d5df2562794a4dd7c75 --- /dev/null +++ b/smtk/task/json/jsonGatherResources.cxx @@ -0,0 +1,154 @@ +//========================================================================= +// 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/jsonGatherResources.h" +#include "smtk/task/json/Helper.h" +#include "smtk/task/json/jsonTask.h" + +#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 +{ + +Task::Configuration jsonGatherResources::operator()(const Task* task, Helper& helper) const +{ + Task::Configuration config; + auto* nctask = const_cast(task); + auto* gatherResources = dynamic_cast(nctask); + if (gatherResources) + { + jsonTask superclass; + config = superclass(gatherResources, helper); + nlohmann::json::array_t resourceSets; + gatherResources->visitResourceSets( + [&resourceSets](const GatherResources::ResourceSet& resourceSet) -> smtk::common::Visit { + nlohmann::json jsonResourceSet = resourceSet; + resourceSets.push_back(jsonResourceSet); + return smtk::common::Visit::Continue; + }); + config["resources"] = resourceSets; + } + return config; +} + +} // namespace json +} // namespace task +} // namespace smtk diff --git a/smtk/task/json/jsonGatherResources.h b/smtk/task/json/jsonGatherResources.h new file mode 100644 index 0000000000000000000000000000000000000000..d83cfd96becb38812a7b9546983ce7db707eacc6 --- /dev/null +++ b/smtk/task/json/jsonGatherResources.h @@ -0,0 +1,40 @@ +//========================================================================= +// 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_GatherResources_h +#define smtk_task_json_GatherResources_h + +#include "smtk/task/GatherResources.h" + +#include +#include + +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 +{ + +class Helper; + +struct SMTKCORE_EXPORT jsonGatherResources +{ + Task::Configuration operator()(const Task* task, Helper& helper) const; +}; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_GatherResources_h diff --git a/smtk/task/json/jsonGroup.cxx b/smtk/task/json/jsonGroup.cxx new file mode 100644 index 0000000000000000000000000000000000000000..beff672a5af7ad3a1d09d41418779b9347a1a5cb --- /dev/null +++ b/smtk/task/json/jsonGroup.cxx @@ -0,0 +1,63 @@ +//========================================================================= +// 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/jsonAdaptor.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); + 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.emplace_back(jsonChild); + } + 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; +} + +} // namespace json +} // namespace task +} // namespace smtk diff --git a/smtk/extension/qt/qtViewInterface.cxx b/smtk/task/json/jsonGroup.h similarity index 53% rename from smtk/extension/qt/qtViewInterface.cxx rename to smtk/task/json/jsonGroup.h index 0c9d6365aa0aeae0ab63c03ca64c762f8a8225f2..94a85a31d5128d86069242fef69803391b8f6834 100644 --- a/smtk/extension/qt/qtViewInterface.cxx +++ b/smtk/task/json/jsonGroup.h @@ -7,10 +7,30 @@ // 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" +#ifndef smtk_task_json_Group_h +#define smtk_task_json_Group_h -using namespace smtk::extension; +#include "smtk/task/Task.h" -qtViewInterface::qtViewInterface() = default; +#include +#include -qtViewInterface::~qtViewInterface() = default; +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 new file mode 100644 index 0000000000000000000000000000000000000000..be84312c7274a579f15240a213d5923ac4e7fbf9 --- /dev/null +++ b/smtk/task/json/jsonManager.cxx @@ -0,0 +1,180 @@ +//========================================================================= +// 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/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" + +#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->taskInstances().visit([&taskList](const smtk::task::Task::Ptr& task) { + 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; + + // Serialize adaptors + nlohmann::json::array_t adaptorList; + taskManager->adaptorInstances().visit([&adaptorList](const smtk::task::Adaptor::Ptr& adaptor) { + 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; + return true; +} + +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) + { + // Should we succeed silently instead of failing verbosely? + smtkErrorMacro(smtk::io::Logger::instance(), "Could not find a destination task manager."); + return false; + } + + tasks.clear(); + adaptors.clear(); + try + { + auto& helper = Helper::instance(); + 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 adaptorMap; + for (const auto& jsonTask : json.at("tasks")) + { + 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 task = taskMap[taskId]; + auto taskDeps = helper.unswizzleDependencies(jsonTask.at("dependencies")); + task->addDependencies(taskDeps); + } + } + // Now configure dependent tasks with adaptors if specified. + // Note that tasks have already been deserialized, so the + // helper's map from task-id to task-pointer is complete. + if (json.contains("adaptors")) + { + for (const auto& jsonAdaptor : json.at("adaptors")) + { + // Skip things that are not adaptors. + if (jsonAdaptor.is_object() && jsonAdaptor.contains("id")) + { + 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."); + } + } + } + } + + 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 0000000000000000000000000000000000000000..59c0b7b214cca0cc77f03eb0430c8c8b82898613 --- /dev/null +++ b/smtk/task/json/jsonManager.h @@ -0,0 +1,62 @@ +//========================================================================= +// 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/Manager.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. + /// + /// 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); +}; + +} // namespace json +} // namespace task +} // namespace smtk + +#endif // smtk_task_json_Manager_h diff --git a/smtk/task/json/jsonResourceAndRole.cxx b/smtk/task/json/jsonResourceAndRole.cxx new file mode 100644 index 0000000000000000000000000000000000000000..bd839255b3e33c6f4813ba25d6a7d9a3660a8656 --- /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 0000000000000000000000000000000000000000..ac309595f495414d2677560ad83b52eab3c3c43b --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..5117f1174ac3dcb37b9758552fafe2e675e78999 --- /dev/null +++ b/smtk/task/json/jsonTask.cxx @@ -0,0 +1,77 @@ +//========================================================================= +// 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.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()) + { + 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.tasks().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->taskInstances().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 0000000000000000000000000000000000000000..483b9c6cfd4af33502f36ffee92964140e469470 --- /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/pybind11/PybindActive.h b/smtk/task/pybind11/PybindActive.h new file mode 100644 index 0000000000000000000000000000000000000000..d7068724ddc687b933f94ebc87793c21228e0ba6 --- /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 0000000000000000000000000000000000000000..5d69b9cb63ec7b2fda51a55ae0dbc86937a26413 --- /dev/null +++ b/smtk/task/pybind11/PybindFillOutAttributes.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 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::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") + .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/PybindGatherResources.h b/smtk/task/pybind11/PybindGatherResources.h new file mode 100644 index 0000000000000000000000000000000000000000..9640bef61eb1b1629ec25e24f44639b508c8bcaf --- /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::ResourceSet >(instance, "ResourceSet") + .def(py::init<::smtk::task::GatherResources::ResourceSet const &>()) + .def(py::init<>()) + .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; +} + +#endif diff --git a/smtk/task/pybind11/PybindManager.h b/smtk/task/pybind11/PybindManager.h index b276ddb0f7535a78df82e8756115f86ae995515d..de03506f5224706727a4ee68809913878fc7e1d5 100644 --- a/smtk/task/pybind11/PybindManager.h +++ b/smtk/task/pybind11/PybindManager.h @@ -15,18 +15,24 @@ #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("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("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("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 660f1096b8b3c52f6c95b3ba5d35bb8d02d6e755..7fbe0c568d703d53026c6935818590c1e0b9a8bd 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 "PybindTaskNeedsResources.h" -#include "PybindTask.h" +#include "PybindActive.h" +#include "PybindFillOutAttributes.h" +#include "PybindGatherResources.h" #include "PybindManager.h" #include "PybindRegistrar.h" #include "PybindState.h" +#include "PybindTask.h" PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); @@ -36,11 +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_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 2a76509d5e3bd30fdd60ddcfa0946abe0dbfa341..0000000000000000000000000000000000000000 --- 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/CMakeLists.txt b/smtk/task/testing/cxx/CMakeLists.txt index c2628128fd157741932c77ca0d5d34a8f049f5b5..34fe55594804f1583b0c3f96920e46b11e324b49 100644 --- a/smtk/task/testing/cxx/CMakeLists.txt +++ b/smtk/task/testing/cxx/CMakeLists.txt @@ -1,6 +1,8 @@ set(unit_tests - TestActive.cxx - TestTask.cxx + TestActiveTask.cxx + TestTaskBasics.cxx + TestTaskGroup.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 92% rename from smtk/task/testing/cxx/TestActive.cxx rename to smtk/task/testing/cxx/TestActiveTask.cxx index 9cea1a72e3622916b1d056daebf58371f6b24268..d129c0805d393785d2601c073cd783957ae6fa12 100644 --- a/smtk/task/testing/cxx/TestActive.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" @@ -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; @@ -71,8 +71,8 @@ int TestActive(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,24 +92,25 @@ int TestActive(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()); - // 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 // 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" } } } } }; - 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 +160,7 @@ int TestActive(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/TestTask.cxx b/smtk/task/testing/cxx/TestTaskBasics.cxx similarity index 82% rename from smtk/task/testing/cxx/TestTask.cxx rename to smtk/task/testing/cxx/TestTaskBasics.cxx index 04f0277c02b8fd7dfcb74bf4bed7bab6ab3b62e8..6aded37e69652bfa1581e9afc232ef7cab1efef4 100644 --- a/smtk/task/testing/cxx/TestTask.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" @@ -61,7 +61,7 @@ public: } // namespace test_task -int TestTask(int, char*[]) +int TestTaskBasics(int, char*[]) { using smtk::task::State; using smtk::task::Task; @@ -83,9 +83,9 @@ int TestTask(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 TestTask(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 TestTask(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,9 +155,29 @@ int TestTask(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 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; @@ -173,19 +193,20 @@ int TestTask(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 // 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" } } } } }; - auto t4 = taskManager->instances().create(c4, managers); - test(!!t4, "Could not create TaskNeedsResources."); + 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); @@ -241,15 +262,16 @@ int TestTask(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" }, + { "auto-configure", true }, { "resources", { { { "type", "smtk::model::Resource" } }, { { "role", "simulation attribute" } } } } }; auto t5 = - taskManager->instances().createFromName("smtk::task::TaskNeedsResources", c5, managers); - test(!!t5, "Could not create TaskNeedsResources."); + 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."); called = 0; @@ -264,29 +286,29 @@ int TestTask(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 new file mode 100644 index 0000000000000000000000000000000000000000..8f5619f601a0785980f15d88b7df445ad3b94692 --- /dev/null +++ b/smtk/task/testing/cxx/TestTaskGroup.cxx @@ -0,0 +1,288 @@ +//========================================================================= +// 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/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/json/jsonManager.h" +#include "smtk/task/json/jsonTask.h" + +#include "smtk/common/testing/cxx/helpers.h" + +namespace +{ + +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", + "type": "smtk::model::Resource", + "max": 2 + }, + { + "role": "simulation attribute", + "type": "smtk::attribute::Resource" + } + ] + }, + { + "id": 2, + "type": "smtk::task::Group", + "mode": "parallel", + "title": "Prepare simulation", + "dependencies": [ 1 ], + "children": { + "tasks": [ + { + "id": 2, + "type": "smtk::task::FillOutAttributes", + "title": "Mark up model", + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": [ + "Material", + "BoundaryCondition" + ] + } + ] + }, + { + "id": 3, + "type": "smtk::task::FillOutAttributes", + "title": "Set simulation parameters", + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": [ + "SimulationParameters" + ] + } + ] + } + ], + "adaptors": [ + { + "id": 1, + "type": "smtk::task::adaptor::ResourceAndRole", + "from": 1, + "to": 2 + }, + { + "id": 2, + "type": "smtk::task::adaptor::ResourceAndRole", + "from": 1, + "to": 3 + } + ] + } + } + ], + "//": [ + "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." + ], + "adaptors": [ + { + "id": 1, + "type": "smtk::task::adaptor::ResourceAndRole", + "from": 1, + "to": 2 + } + ] +} + )"; +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() == 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."); + + 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."); + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..5cb91695732aa84f8905c95a4ece0afaff29221b --- /dev/null +++ b/smtk/task/testing/cxx/TestTaskJSON.cxx @@ -0,0 +1,277 @@ +//========================================================================= +// 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/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" +// #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 + +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; + using smtk::task::Task; + using smtk::task::WorkflowEvent; + + // 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); + + 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"); + 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", + "style": [ "foo", "bar", "baz" ], + "state": "completed", + "auto-configure": true, + "resources": [ + { + "role": "model geometry", + "type": "smtk::model::Resource", + "max": 2 + }, + { + "role": "simulation attribute", + "type": "smtk::attribute::Resource" + } + ] + }, + { + "id": 2, + "type": "smtk::task::FillOutAttributes", + "title": "Mark up model", + "state": "incomplete", + "dependencies": [ 1 ], + "attribute-sets": [ + { + "role": "simulation attribute", + "definitions": [ + "Material", + "BoundaryCondition" + ] + } + ] + } + ], + "//": [ + "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." + ], + "adaptors": [ + { + "id": 1, + "type": "smtk::task::adaptor::ResourceAndRole", + "from": 1, + "to": 2 + } + ], + "styles": { + "foo": { }, + "bar": { }, + "baz": { } + } +} + )"; + + 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."); + + 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; + }); + + // 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); + 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); + + 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); + + 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."); + + // 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."); + std::string configString3 = config4.dump(2); + std::cout << "----\n" << configString3 << "\n"; + // test(configString2 == configString3, "Failed to match strings."); + } + + return 0; +} diff --git a/smtk/view/Information.h b/smtk/view/Information.h index 3a0b142803829e23927532d615e5ffb9e1b5fb2e..3d82d68c9993620d9170d4175b8df7d0e0ccf95c 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 diff --git a/version.txt b/version.txt index 16406cd4a7122de27ee90b9bd92eb168edf50afb..7ded421501a6eaba424f26aebf8785a108f845e9 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -21.07.0 +21.09.0
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