diff --git a/CMakeLists.txt b/CMakeLists.txt index 93e704374ac5792980c842d4b686cc5e7942e9a5..82b0b037e09574cced083733197237bd3e010068 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -432,19 +432,23 @@ if(SMTK_ENABLE_PYTHON_WRAPPING) # Note that SMTK_PYTHON_MODULEDIR may be provided if SMTK is being # built as a submodule or as part of a superbuild. if (NOT DEFINED SMTK_PYTHON_MODULEDIR) - if (SMTK_INSTALL_PYTHON_TO_SITE_PACKAGES) - execute_process( - COMMAND - "$" - -c "import site; print(site.getsitepackages())[-1]" - RESULT_VARIABLE SMTK_PYTHON_MODULEDIR - ) - elseif(WIN32) - set(SMTK_PYTHON_MODULEDIR - "bin/Lib/site-packages") + if (DEFINED SKBUILD) + set(SMTK_PYTHON_MODULEDIR "${SKBUILD_PLATLIB_DIR}") else() - set(SMTK_PYTHON_MODULEDIR - "${CMAKE_INSTALL_LIBDIR}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages") + if (SMTK_INSTALL_PYTHON_TO_SITE_PACKAGES) + execute_process( + COMMAND + "$" + -c "import site; print(site.getsitepackages())[-1]" + RESULT_VARIABLE SMTK_PYTHON_MODULEDIR + ) + elseif(WIN32) + set(SMTK_PYTHON_MODULEDIR + "bin/Lib/site-packages") + else() + set(SMTK_PYTHON_MODULEDIR + "${CMAKE_INSTALL_LIBDIR}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages") + endif() endif() endif() diff --git a/data/baseline/smtk/widgets/BoxWidget_1.png b/data/baseline/smtk/widgets/BoxWidget_1.png new file mode 100644 index 0000000000000000000000000000000000000000..51a1c3731e63a412f4021f14e21de38ccf03d967 --- /dev/null +++ b/data/baseline/smtk/widgets/BoxWidget_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2ca3417aa7139f5af78b450856ebd3698aa94e6b78a71dc492f7867df3ca849 +size 2756 diff --git a/data/baseline/smtk/widgets/FrameWidget_1.png b/data/baseline/smtk/widgets/FrameWidget_1.png new file mode 100644 index 0000000000000000000000000000000000000000..46fed62a49d44e86e17ed4de1921b4f26f1f98b7 --- /dev/null +++ b/data/baseline/smtk/widgets/FrameWidget_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d73cc6b84eb19103d0a7e598a83cfc80d3ac7cfb116af2ec20f25f129bc8814 +size 5160 diff --git a/data/baseline/smtk/widgets/LineWidget_1.png b/data/baseline/smtk/widgets/LineWidget_1.png index 9de74e00f3d098c7af1b0c0281cc83e96e214024..8e1dbdbe4a3cd258945885428aa152bab298e21e 100644 --- a/data/baseline/smtk/widgets/LineWidget_1.png +++ b/data/baseline/smtk/widgets/LineWidget_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d73cb3f41b34360f25039f95cc9b5b29702eb575daa268b353de8d0b2ac869f -size 2217 +oid sha256:758ca2ffa6f601289b48f6aa1f6b2ae8afe16a6db487331c4c8e28d44bc3a999 +size 1740 diff --git a/data/baseline/smtk/widgets/LineWidget_2.png b/data/baseline/smtk/widgets/LineWidget_2.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1dbdbe4a3cd258945885428aa152bab298e21e --- /dev/null +++ b/data/baseline/smtk/widgets/LineWidget_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:758ca2ffa6f601289b48f6aa1f6b2ae8afe16a6db487331c4c8e28d44bc3a999 +size 1740 diff --git a/data/baseline/smtk/widgets/PlaneWidget_1.png b/data/baseline/smtk/widgets/PlaneWidget_1.png index 71a94393f23b3a517808fdc89498dfe8842e0404..cb66a138366032188b4ac3c0c3fd3ed526b7e0c8 100644 --- a/data/baseline/smtk/widgets/PlaneWidget_1.png +++ b/data/baseline/smtk/widgets/PlaneWidget_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:614a4cb449f52276865cdbddfbe8bd3ab7c88fd3183cefad9667917642976fd2 -size 4230 +oid sha256:1a5098fe68c8264572ae7dd393ad985061dd99bc8968493393ace6f7a0f5883d +size 3895 diff --git a/data/baseline/smtk/widgets/PlaneWidget_2.png b/data/baseline/smtk/widgets/PlaneWidget_2.png new file mode 100644 index 0000000000000000000000000000000000000000..cb66a138366032188b4ac3c0c3fd3ed526b7e0c8 --- /dev/null +++ b/data/baseline/smtk/widgets/PlaneWidget_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a5098fe68c8264572ae7dd393ad985061dd99bc8968493393ace6f7a0f5883d +size 3895 diff --git a/data/baseline/smtk/widgets/PointWidget_1.png b/data/baseline/smtk/widgets/PointWidget_1.png new file mode 100644 index 0000000000000000000000000000000000000000..5d412046b5760f0653f867ef626f2cd25b4bb34f --- /dev/null +++ b/data/baseline/smtk/widgets/PointWidget_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a07ebc9105e2cd07944992192741de5577eee2cb5eee33bec3b5dfbef6668c66 +size 1288 diff --git a/data/baseline/smtk/widgets/SphereWidget_1.png b/data/baseline/smtk/widgets/SphereWidget_1.png new file mode 100644 index 0000000000000000000000000000000000000000..a395164d979e3ca4f77929d39e4e7d0cee3439bf --- /dev/null +++ b/data/baseline/smtk/widgets/SphereWidget_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c0d4d6948b7837a7e5e8e83ce1f33e39275860eabcc789a0950d9d2b229f0cac +size 22182 diff --git a/data/baseline/smtk/widgets/SplineWidget_1.png b/data/baseline/smtk/widgets/SplineWidget_1.png index 95407a738e8d16eda17aa7f7a7eb72931fa4a3ec..67bc8e21fe5930d66c2c9bdfc1136b0e45c2738d 100644 --- a/data/baseline/smtk/widgets/SplineWidget_1.png +++ b/data/baseline/smtk/widgets/SplineWidget_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e6beb7d67e5c9f145601737aa59969dd4f4b3ccaa77e9c7699c43bfb90218c0 -size 6702 +oid sha256:dde9e0ad172e656ce1f6d10d9cab734a42697bc5b7efb8486499221abdb04d5b +size 5986 diff --git a/data/baseline/smtk/widgets/SplineWidget_3.png b/data/baseline/smtk/widgets/SplineWidget_3.png new file mode 100644 index 0000000000000000000000000000000000000000..67bc8e21fe5930d66c2c9bdfc1136b0e45c2738d --- /dev/null +++ b/data/baseline/smtk/widgets/SplineWidget_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dde9e0ad172e656ce1f6d10d9cab734a42697bc5b7efb8486499221abdb04d5b +size 5986 diff --git a/doc/release/notes/attribute-operations.rst b/doc/release/notes/attribute-operations.rst new file mode 100644 index 0000000000000000000000000000000000000000..0104a9b1333a10fb29c60122e07f181808aa21a1 --- /dev/null +++ b/doc/release/notes/attribute-operations.rst @@ -0,0 +1,8 @@ +Attribute System +================ + +The attribute system now provides operations to create, +delete, and rename attributes. +These were previously performed by GUI code (and still are +in the Qt GUI extensions), but in preparation for Trame +support, these operations are being made available. diff --git a/doc/release/notes/operation-exceptions.rst b/doc/release/notes/operation-exceptions.rst new file mode 100644 index 0000000000000000000000000000000000000000..558d79c9c7b4da42dd609134376b879d02cf9293 --- /dev/null +++ b/doc/release/notes/operation-exceptions.rst @@ -0,0 +1,10 @@ +Operation System +================ + +Exception handling +------------------ + +Since April 2025, exceptions thrown from within :smtk:`smtk::operation::Operation::operateInternal` +have been caught to avoid situations where resource locks are never freed because the stack frame +holding the lock was unwound. We now catch exceptions thrown by operation observers and handlers +for the same reason. diff --git a/doc/release/notes/paraview-6-support.rst b/doc/release/notes/paraview-6-support.rst new file mode 100644 index 0000000000000000000000000000000000000000..bfb7eb9be15a8b3da0ae5298b0c993e37a9ada7f --- /dev/null +++ b/doc/release/notes/paraview-6-support.rst @@ -0,0 +1,5 @@ +ParaView Extensions +=================== + +SMTK's ParaView extensions now support building with ParaView 6 +(but still require ParaView to be built using Qt5, not Qt6). diff --git a/doc/userguide/operation/operators.rst b/doc/userguide/operation/operators.rst index 3d6beb6259036c460d17d897c440a20c4668c935..d0822eb5f82180bc66014822b95e7a2e8d4a9d40 100644 --- a/doc/userguide/operation/operators.rst +++ b/doc/userguide/operation/operators.rst @@ -15,6 +15,11 @@ may run in parallel in other threads. By using the locking mechanism which operations provide, you avoid race conditions, memory stomping, and many other issues encountered with parallel code. +All exceptions thrown inside an operation or an operation handler/observer (described +below) will be caught and logged as errors. +This is done to avoid situations where resource locks are never released because an +exception thrown inside these code segments. + The next sections describe in detail: first, how operators specify the inputs they require and outputs they produce; and second, how operators register themselves for introspection by applications and scripts. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..2d1ec4bf5c89107bd8e8f31e621824eca6e94cad --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,38 @@ +[build-system] +requires = ["scikit-build-core"] +build-backend = "scikit_build_core.build" + +[project] +name = "smtk" +version = "25.03" # This gets rewritten to 25.3 somewhere. I assume that's OK? + +[tool.scikit-build] +cmake.version = ">=3.20" +ninja.version = ">=1.11" +ninja.make-fallback = false + +cmake.verbose = true +logging.level = "INFO" +cmake.args = [] +cmake.build-type = "Debug" + +# wheel.packages = [ 'smtk/python' ] +wheel.license-files = [ 'LICENSE.txt' ] + +[tool.scikit-build.cmake.define] + +SMTK_ENABLE_TESTING = true +SMTK_ENABLE_PYTHON_WRAPPING = true +SMTK_ENABLE_OPERATION_THREADS = true +# These are hardwired at the moment; they come from the superbuild. +units_DIR = '/stage/bld/aeva/superbuild/install/lib/cmake/units-24.07.0' +Eigen3_DIR = '/stage/bld/aeva/superbuild/install/share/eigen3/cmake' + +SMTK_NO_SYSTEM_BOOST = false +SMTK_ENABLE_VTK_SUPPORT = false +SMTK_ENABLE_PARAVIEW_SUPPORT = false +SMTK_ENABLE_QT_SUPPORT = false +SMTK_ENABLE_APPLICATIONS = false +SMTK_ENABLE_EXAMPLES = false +SMTK_ENABLE_POLYGON_SESSION = false +SMTK_ENABLE_VTK_SESSION = false diff --git a/smtk/SharedFromThis.h b/smtk/SharedFromThis.h index 84ec3a1c2bddc298007f5609c6a417a5c7b9d20e..a9bf74de9554ae090813d568f9c565ddd88fe78f 100644 --- a/smtk/SharedFromThis.h +++ b/smtk/SharedFromThis.h @@ -30,10 +30,6 @@ /// Used by smtkTypeMacro and smtkTypeMacroBase to provide access to the inheritance hierarchy. #define smtkInheritanceHierarchyBase(...) \ - virtual smtk::string::Token typeToken() const \ - { \ - return smtk::string::Token(type_name); \ - } \ virtual std::vector classHierarchy() const \ { \ static std::vector baseTypes; \ @@ -71,10 +67,6 @@ } #define smtkInheritanceHierarchy(...) \ - smtk::string::Token typeToken() const override \ - { \ - return smtk::string::Token(type_name); \ - } \ std::vector classHierarchy() const override \ { \ static std::vector baseTypes; \ @@ -111,6 +103,11 @@ { \ return type_name; \ } \ + virtual smtk::string::Token typeToken() const \ + { \ + using namespace smtk::string::literals; \ + return smtk::string::Token::fromHash(#__VA_ARGS__##_hash); \ + } \ smtkInheritanceHierarchyBase(__VA_ARGS__); #define smtkTypenameMacro(...) \ static constexpr const char* const type_name = #__VA_ARGS__; \ @@ -118,6 +115,11 @@ { \ return type_name; \ } \ + smtk::string::Token typeToken() const override \ + { \ + using namespace smtk::string::literals; \ + return smtk::string::Token::fromHash(#__VA_ARGS__##_hash); \ + } \ smtkInheritanceHierarchy(__VA_ARGS__); ///@} diff --git a/smtk/attribute/CMakeLists.txt b/smtk/attribute/CMakeLists.txt index f1bdfbbe07fc3c35d02d5a0da20e7bc6e2ed4028..c5717dd8e6545a35334405fbe325a40b4184c3a1 100644 --- a/smtk/attribute/CMakeLists.txt +++ b/smtk/attribute/CMakeLists.txt @@ -74,13 +74,17 @@ set(jsonAttributeSrcs set(attributeOperators Associate + CreateAttribute + DeleteAttribute Dissociate + EditAttributeItem Export Import Read + RenameAttribute Signal Write - ) +) set(attributeHeaders ${jsonAttributeHeaders} diff --git a/smtk/attribute/CustomItem.h b/smtk/attribute/CustomItem.h index 4d27f7f060e4a66da01b48a314be56d40e9c12fd..7b97ecd7c0d338b72bed0575a4e70f3d4c3de06f 100644 --- a/smtk/attribute/CustomItem.h +++ b/smtk/attribute/CustomItem.h @@ -37,7 +37,8 @@ namespace attribute class SMTKCORE_EXPORT CustomItemBase : public Item { public: - smtkTypeMacro(CustomItemBase); + smtkTypeMacro(smtk::attribute::CustomItemBase); + smtkSuperclassMacro(smtk::attribute::Item); CustomItemBase(smtk::attribute::Attribute* owningAttribute, int itemPosition) : Item(owningAttribute, itemPosition) @@ -62,8 +63,23 @@ template class CustomItem : public CustomItemBase { public: - typedef std::shared_ptr Ptr; + smtkTypedefs(smtk::attribute::CustomItem); + std::string typeName() const override + { + std::ostringstream tname; + tname << "smtk::attribute::CustomItem<" << smtk::common::typeName() << ">"; + return tname.str(); + } + smtk::string::Token typeToken() const override { return smtk::string::Token(this->typeName()); } + smtkInheritanceHierarchy(smtk::attribute::CustomItem); + smtkSuperclassMacro(smtk::attribute::CustomItemBase); +private: + // Prevent smtk::common::typeName() from grabbing our subclass's type_name + // by changing its access specifier: + using CustomItemBase::type_name; + +public: static Ptr New(const std::string& myName) { return Ptr(new ItemType(myName)); } CustomItem(smtk::attribute::Attribute* owningAttribute, int itemPosition) diff --git a/smtk/attribute/DateTimeItem.h b/smtk/attribute/DateTimeItem.h index 33c8f3b94115bf65ff5319b59cc3649ce353d317..e128dca5a208ab3d217436ca6647f8e776e19fea 100644 --- a/smtk/attribute/DateTimeItem.h +++ b/smtk/attribute/DateTimeItem.h @@ -32,6 +32,7 @@ class SMTKCORE_EXPORT DateTimeItem : public Item public: smtkTypeMacro(smtk::attribute::DateTimeItem); + smtkSuperclassMacro(smtk::attribute::Item); ~DateTimeItem() override; Item::Type type() const override; diff --git a/smtk/attribute/DirectoryItem.h b/smtk/attribute/DirectoryItem.h index 600b0f715c8d6008d92c1aab40097c55fd59079c..15fefcbacc1944d613be3bffba46d3823094f2e6 100644 --- a/smtk/attribute/DirectoryItem.h +++ b/smtk/attribute/DirectoryItem.h @@ -31,6 +31,7 @@ class SMTKCORE_EXPORT DirectoryItem : public FileSystemItem public: smtkTypeMacro(smtk::attribute::DirectoryItem); + smtkSuperclassMacro(smtk::attribute::FileSystemItem); ~DirectoryItem() override; Item::Type type() const override; diff --git a/smtk/attribute/DoubleItem.h b/smtk/attribute/DoubleItem.h index c582d9775b561232acb188d809365fc9f670e943..3c998cd143d1aaa5dd6efa34c7bdfa89408e5342 100644 --- a/smtk/attribute/DoubleItem.h +++ b/smtk/attribute/DoubleItem.h @@ -29,6 +29,7 @@ class SMTKCORE_EXPORT DoubleItem : public ValueItemTemplate public: smtkTypeMacro(smtk::attribute::DoubleItem); + smtkSuperclassMacro(smtk::attribute::ValueItemTemplate); ~DoubleItem() override; Item::Type type() const override; diff --git a/smtk/attribute/FileItem.h b/smtk/attribute/FileItem.h index b38a8e5a7ddbe09b4048072ed45e910119b26ad7..2b54b085b605682fe1dcee5ad93d0a5acc26207b 100644 --- a/smtk/attribute/FileItem.h +++ b/smtk/attribute/FileItem.h @@ -31,6 +31,7 @@ class SMTKCORE_EXPORT FileItem : public FileSystemItem public: smtkTypeMacro(smtk::attribute::FileItem); + smtkSuperclassMacro(smtk::attribute::FileSystemItem); ~FileItem() override; Item::Type type() const override; diff --git a/smtk/attribute/FileSystemItem.h b/smtk/attribute/FileSystemItem.h index 1ad5eb5033fe5fe942f0e922087156eb3667259d..6d8301028b1d112e910aef47091d242e9a1b5642 100644 --- a/smtk/attribute/FileSystemItem.h +++ b/smtk/attribute/FileSystemItem.h @@ -34,6 +34,7 @@ public: typedef std::vector::const_iterator const_iterator; smtkTypeMacro(smtk::attribute::FileSystemItem); + smtkSuperclassMacro(smtk::attribute::Item); ~FileSystemItem() override; Item::Type type() const override = 0; diff --git a/smtk/attribute/GroupItem.h b/smtk/attribute/GroupItem.h index ed4e6ab1d8061484a3bbe376e41428d037b2d6a4..da91f2005e3cbf5699b7c2fa9fbfb1f8cb208db5 100644 --- a/smtk/attribute/GroupItem.h +++ b/smtk/attribute/GroupItem.h @@ -53,6 +53,7 @@ public: typedef std::vector>::const_iterator const_iterator; smtkTypeMacro(smtk::attribute::GroupItem); + smtkSuperclassMacro(smtk::attribute::Item); ~GroupItem() override; Item::Type type() const override; diff --git a/smtk/attribute/IntItem.h b/smtk/attribute/IntItem.h index 46a826a4b78e6890c0acbba475f7434c5c67aff4..26721b2e1fd7e9df572fe24f12815c19f9ea4ad0 100644 --- a/smtk/attribute/IntItem.h +++ b/smtk/attribute/IntItem.h @@ -29,6 +29,7 @@ class SMTKCORE_EXPORT IntItem : public ValueItemTemplate public: smtkTypeMacro(smtk::attribute::IntItem); + smtkSuperclassMacro(smtk::attribute::ValueItemTemplate); ~IntItem() override; Item::Type type() const override; diff --git a/smtk/attribute/ModelEntityItem.h b/smtk/attribute/ModelEntityItem.h index a987ffc9fe3140d4d7445705250fb7621ce075dd..41398859484e2b9d776cdd9a22a275b7c04cde13 100644 --- a/smtk/attribute/ModelEntityItem.h +++ b/smtk/attribute/ModelEntityItem.h @@ -47,6 +47,7 @@ class SMTKCORE_EXPORT ModelEntityItem : public ComponentItem public: smtkTypeMacro(smtk::attribute::ModelEntityItem); + smtkSuperclassMacro(smtk::attribute::ComponentItem); ~ModelEntityItem() override; using ComponentItem::appendValue; diff --git a/smtk/attribute/ReferenceItem.h b/smtk/attribute/ReferenceItem.h index b1cfe87bd2028ca4506c41b10b91e552405fab2c..80f645250c6502b3626d3d307580ac9a0d9c2aa4 100644 --- a/smtk/attribute/ReferenceItem.h +++ b/smtk/attribute/ReferenceItem.h @@ -120,8 +120,8 @@ public: /// and the second one is the id of the component link. using Key = std::pair; - smtkTypeMacro(ReferenceItem); - smtkSuperclassMacro(Item); + smtkTypeMacro(smtk::attribute::ReferenceItem); + smtkSuperclassMacro(smtk::attribute::Item); ReferenceItem(const ReferenceItem&); ~ReferenceItem() override; diff --git a/smtk/attribute/Registrar.cxx b/smtk/attribute/Registrar.cxx index 0efb66f9f1d09472e1d96fdf6b957eeb79fe027a..919712982ce2340bb7359de625a9feaefeb6e263 100644 --- a/smtk/attribute/Registrar.cxx +++ b/smtk/attribute/Registrar.cxx @@ -18,10 +18,14 @@ #include "smtk/attribute/UpdateManager.h" #include "smtk/attribute/operators/Associate.h" +#include "smtk/attribute/operators/CreateAttribute.h" +#include "smtk/attribute/operators/DeleteAttribute.h" #include "smtk/attribute/operators/Dissociate.h" +#include "smtk/attribute/operators/EditAttributeItem.h" #include "smtk/attribute/operators/Export.h" #include "smtk/attribute/operators/Import.h" #include "smtk/attribute/operators/Read.h" +#include "smtk/attribute/operators/RenameAttribute.h" #include "smtk/attribute/operators/Signal.h" #include "smtk/attribute/operators/Write.h" @@ -29,9 +33,11 @@ #include "smtk/attribute/PythonRule.h" #endif +#include "smtk/operation/groups/DeleterGroup.h" #include "smtk/operation/groups/ExporterGroup.h" #include "smtk/operation/groups/ImporterGroup.h" #include "smtk/operation/groups/InternalGroup.h" +#include "smtk/operation/groups/NamingGroup.h" #include "smtk/operation/groups/ReaderGroup.h" #include "smtk/operation/groups/WriterGroup.h" @@ -43,8 +49,22 @@ namespace attribute { namespace { -typedef std::tuple OperationList; -} +// clang-format off +using OperationList = std::tuple< + Associate, + CreateAttribute, + DeleteAttribute, + Dissociate, + EditAttributeItem, + Export, + Import, + Read, + RenameAttribute, + Signal, + Write +>; +// clang-format on +} // namespace void Registrar::registerTo(const smtk::common::Managers::Ptr& managers) { @@ -106,38 +126,35 @@ void Registrar::registerTo(const smtk::operation::Manager::Ptr& operationManager { operationManager->registerOperations(); - smtk::operation::ReaderGroup(operationManager) - .registerOperation(); - - smtk::operation::ExporterGroup(operationManager) - .registerOperation(); - - smtk::operation::ImporterGroup(operationManager) - .registerOperation(); - - smtk::operation::WriterGroup(operationManager) - .registerOperation(); - + // clang-format off + smtk::operation::ReaderGroup(operationManager).registerOperation(); + smtk::operation::ExporterGroup(operationManager).registerOperation(); + smtk::operation::ImporterGroup(operationManager).registerOperation(); + smtk::operation::WriterGroup(operationManager).registerOperation(); + smtk::operation::NamingGroup(operationManager).registerOperation(); smtk::operation::InternalGroup(operationManager).registerOperation(); + smtk::operation::DeleterGroup(operationManager).registerOperation(); + // clang-format on } void Registrar::unregisterFrom(const smtk::operation::Manager::Ptr& operationManager) { + // clang-format off smtk::operation::ReaderGroup(operationManager).unregisterOperation(); - smtk::operation::ExporterGroup(operationManager).unregisterOperation(); - smtk::operation::ImporterGroup(operationManager).unregisterOperation(); - smtk::operation::WriterGroup(operationManager).unregisterOperation(); - + smtk::operation::NamingGroup(operationManager).unregisterOperation(); smtk::operation::InternalGroup(operationManager).unregisterOperation(); + smtk::operation::DeleterGroup(operationManager).unregisterOperation(); + // clang-format on operationManager->unregisterOperations(); } void Registrar::registerTo(const smtk::resource::Manager::Ptr& resourceManager) { + smtk::string::Token dummy("smtk::attribute::Resource"); resourceManager->registerResource(read, write); auto& typeLabels = resourceManager->objectTypeLabels(); diff --git a/smtk/attribute/ResourceItem.h b/smtk/attribute/ResourceItem.h index bd3ae59f7481cd03245b43217bdd1b4f3bf85650..d50fcef330e9e851a105e796cf887eab308b0c90 100644 --- a/smtk/attribute/ResourceItem.h +++ b/smtk/attribute/ResourceItem.h @@ -40,7 +40,7 @@ public: using const_iterator = ReferenceItemConstIteratorTemplate; using value_type = ResourcePtr; smtkTypeMacro(smtk::attribute::ResourceItem); - smtkSuperclassMacro(ReferenceItem); + smtkSuperclassMacro(smtk::attribute::ReferenceItem); /// Destructor ~ResourceItem() override; diff --git a/smtk/attribute/StringItem.h b/smtk/attribute/StringItem.h index 2a283e914cd714d3d08887de32daa4c032519bbc..3b4b3edc60815a2efed076e1a268dee7fb681a10 100644 --- a/smtk/attribute/StringItem.h +++ b/smtk/attribute/StringItem.h @@ -29,6 +29,7 @@ class SMTKCORE_EXPORT StringItem : public ValueItemTemplate public: smtkTypeMacro(smtk::attribute::StringItem); + smtkSuperclassMacro(smtk::attribute::ValueItemTemplate); ~StringItem() override; Item::Type type() const override; diff --git a/smtk/attribute/ValueItem.h b/smtk/attribute/ValueItem.h index 93e5ea4ffce8ea25240512aae42b0d9000fbb2dc..6dccc030c1245d12113a88ac68d0a18e9d9adb41 100644 --- a/smtk/attribute/ValueItem.h +++ b/smtk/attribute/ValueItem.h @@ -38,6 +38,7 @@ class SMTKCORE_EXPORT ValueItem : public smtk::attribute::Item { public: smtkTypeMacro(smtk::attribute::ValueItem); + smtkSuperclassMacro(smtk::attribute::Item); friend class ValueItemDefinition; ~ValueItem() override; diff --git a/smtk/attribute/ValueItemTemplate.h b/smtk/attribute/ValueItemTemplate.h index 4e903d7abd2c4359093e2d46ce8befd19aae165c..aaaff065b11e77e61cd960a70634e8f25754b622 100644 --- a/smtk/attribute/ValueItemTemplate.h +++ b/smtk/attribute/ValueItemTemplate.h @@ -39,6 +39,14 @@ public: typedef typename value_type::const_iterator const_iterator; typedef ValueItemDefinitionTemplate DefType; + smtkSuperclassMacro(smtk::attribute::ValueItem); + std::string typeName() const override + { + std::ostringstream tname; + tname << "smtk::attribute::ValueItemTemplate<" << smtk::common::typeName() << ">"; + return tname.str(); + } + ~ValueItemTemplate() override = default; const_iterator begin() const { return m_values.begin(); } const_iterator end() const { return m_values.end(); } @@ -131,6 +139,11 @@ protected: const std::vector m_dummy; //(1, DataT()); std::string streamValue(const DataT& val) const; + +private: + // Prevent smtk::common::typeName() from grabbing our subclass's type_name + // by changing its access specifier: + using ValueItem::type_name; }; template diff --git a/smtk/attribute/VoidItem.h b/smtk/attribute/VoidItem.h index 45aaf7517bcf0285ac77e3627785a66bcbad01ad..40e34878f04224ef97fd8f64153bc9374b385a68 100644 --- a/smtk/attribute/VoidItem.h +++ b/smtk/attribute/VoidItem.h @@ -30,6 +30,7 @@ class SMTKCORE_EXPORT VoidItem : public Item public: smtkTypeMacro(smtk::attribute::VoidItem); + smtkSuperclassMacro(smtk::attribute::Item); ~VoidItem() override; Item::Type type() const override; diff --git a/smtk/attribute/operators/CreateAttribute.cxx b/smtk/attribute/operators/CreateAttribute.cxx new file mode 100644 index 0000000000000000000000000000000000000000..be325eb0db3d58ddc59ac0d8cc02181bbf4c94a5 --- /dev/null +++ b/smtk/attribute/operators/CreateAttribute.cxx @@ -0,0 +1,76 @@ +//========================================================================= +// 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/operators/CreateAttribute.h" + +#include "smtk/attribute/operators/CreateAttribute_xml.h" + +#include "smtk/operation/MarkGeometry.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/ComponentItem.h" +#include "smtk/attribute/IntItem.h" +#include "smtk/attribute/Resource.h" +#include "smtk/attribute/ResourceItem.h" +#include "smtk/attribute/StringItem.h" +#include "smtk/attribute/VoidItem.h" + +#include "smtk/geometry/Geometry.h" + +#include "smtk/io/Logger.h" + +namespace smtk +{ +namespace attribute +{ + +CreateAttribute::Result CreateAttribute::operateInternal() +{ + auto params = this->parameters(); + auto resource = params->associations()->valueAs(); + if (!resource) + { + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + auto attribute = resource->createAttribute(params->findString("definition")->value()); + if (!attribute) + { + smtkErrorMacro( + this->log(), + "Could not create an attribute of type \"" << params->findString("definition")->value() + << "\"."); + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + + auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED); + auto created = result->findComponent("created"); + created->appendValue(attribute); + + // Force any geometry on the created attribute to be updated. + smtk::operation::MarkGeometry marker; + // Only empty the cache entry if there is a geometry backend + // for the resource. + auto& geom = resource->geometry(); + if (geom) + { + marker.markModified(attribute); + } + + return result; +} + +void CreateAttribute::generateSummary(Operation::Result& /*unused*/) {} + +const char* CreateAttribute::xmlDescription() const +{ + return CreateAttribute_xml; +} +} // namespace attribute +} // namespace smtk diff --git a/smtk/attribute/operators/CreateAttribute.h b/smtk/attribute/operators/CreateAttribute.h new file mode 100644 index 0000000000000000000000000000000000000000..9cc753f4ba2607e5a7b24bde24cc9a84cff0b293 --- /dev/null +++ b/smtk/attribute/operators/CreateAttribute.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_operation_operators_CreateAttribute_h +#define smtk_operation_operators_CreateAttribute_h + +#include "smtk/operation/XMLOperation.h" + +namespace smtk +{ +namespace attribute +{ + +/**\brief An operation to create an attribute according to a concrete definition. + */ +class SMTKCORE_EXPORT CreateAttribute : public smtk::operation::XMLOperation +{ +public: + smtkTypeMacro(smtk::attribute::CreateAttribute); + smtkCreateMacro(CreateAttribute); + smtkSharedFromThisMacro(smtk::operation::Operation); + smtkSuperclassMacro(smtk::operation::XMLOperation); + +protected: + Result operateInternal() override; + void generateSummary(Operation::Result&) override; + const char* xmlDescription() const override; +}; +} // namespace attribute +} // namespace smtk + +#endif // smtk_operation_operators_CreateAttribute_h diff --git a/smtk/attribute/operators/CreateAttribute.sbt b/smtk/attribute/operators/CreateAttribute.sbt new file mode 100644 index 0000000000000000000000000000000000000000..4582d0d16bb80bb4f418eec9903ef91ceaab6a95 --- /dev/null +++ b/smtk/attribute/operators/CreateAttribute.sbt @@ -0,0 +1,35 @@ + + + + + + + + + + Create an attribute of the given type. + + + + + The resource in which to create the resource. + + + + + + The name of a concrete attribute definition type to create. + + + + + + + + + + + + diff --git a/smtk/attribute/operators/DeleteAttribute.cxx b/smtk/attribute/operators/DeleteAttribute.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1b97176162a4a2d2606c702338be338780cf44b1 --- /dev/null +++ b/smtk/attribute/operators/DeleteAttribute.cxx @@ -0,0 +1,84 @@ +//========================================================================= +// 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/operators/DeleteAttribute.h" + +#include "smtk/attribute/operators/DeleteAttribute_xml.h" + +#include "smtk/operation/MarkGeometry.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/ComponentItem.h" +#include "smtk/attribute/IntItem.h" +#include "smtk/attribute/Resource.h" +#include "smtk/attribute/ResourceItem.h" +#include "smtk/attribute/StringItem.h" +#include "smtk/attribute/VoidItem.h" + +#include "smtk/geometry/Geometry.h" + +#include "smtk/io/Logger.h" + +namespace smtk +{ +namespace attribute +{ + +DeleteAttribute::Result DeleteAttribute::operateInternal() +{ + auto params = this->parameters(); + int numFailed = 0; + auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED); + auto expunged = result->findComponent("expunged"); + smtk::operation::MarkGeometry marker; + for (const auto& obj : *params->associations()) + { + if (auto attribute = std::dynamic_pointer_cast(obj)) + { + auto rsrc = attribute->attributeResource(); + auto& geom = rsrc->geometry(); + if (!rsrc->removeAttribute(attribute)) + { + smtkErrorMacro( + this->log(), + "Could not delete attribute " << attribute->name() << " of type \"" << attribute->type() + << "\"."); + ++numFailed; + } + else + { + expunged->appendValue(attribute); + // Force any geometry on the created attribute to be updated. + // Only empty the cache entry if there is a geometry backend + // for the resource. + if (geom) + { + marker.markModified(attribute); + } + } + } + } + + if (numFailed > 0) + { + setOutcome(result, smtk::operation::Operation::Outcome::FAILED); + } + + return result; +} + +void DeleteAttribute::generateSummary(Operation::Result& /*unused*/) {} + +const char* DeleteAttribute::xmlDescription() const +{ + return DeleteAttribute_xml; +} +} // namespace attribute +} // namespace smtk diff --git a/smtk/attribute/operators/DeleteAttribute.h b/smtk/attribute/operators/DeleteAttribute.h new file mode 100644 index 0000000000000000000000000000000000000000..0edcaca918773c90eef708f4d4c49889593ecd2d --- /dev/null +++ b/smtk/attribute/operators/DeleteAttribute.h @@ -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. +//========================================================================= +#ifndef smtk_operation_operators_DeleteAttribute_h +#define smtk_operation_operators_DeleteAttribute_h + +#include "smtk/operation/XMLOperation.h" + +namespace smtk +{ +namespace attribute +{ + +/**\brief An operation to delete an attribute. + * + * No checking is performed to ensure the attribute is unreferenced/unassociated + * before deletion. + */ +class SMTKCORE_EXPORT DeleteAttribute : public smtk::operation::XMLOperation +{ +public: + smtkTypeMacro(smtk::attribute::DeleteAttribute); + smtkCreateMacro(DeleteAttribute); + smtkSharedFromThisMacro(smtk::operation::Operation); + smtkSuperclassMacro(smtk::operation::XMLOperation); + +protected: + Result operateInternal() override; + void generateSummary(Operation::Result&) override; + const char* xmlDescription() const override; +}; +} // namespace attribute +} // namespace smtk + +#endif // smtk_operation_operators_DeleteAttribute_h diff --git a/smtk/attribute/operators/DeleteAttribute.sbt b/smtk/attribute/operators/DeleteAttribute.sbt new file mode 100644 index 0000000000000000000000000000000000000000..385a221909d7685915ced70afa24dc0506b802d9 --- /dev/null +++ b/smtk/attribute/operators/DeleteAttribute.sbt @@ -0,0 +1,28 @@ + + + + + + + + + + Delete the associated attribute(s). + + + + + The attribute(s) to delete. + + + + + + + + + + + diff --git a/smtk/attribute/operators/EditAttributeItem.cxx b/smtk/attribute/operators/EditAttributeItem.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e76bc2af8864b9d163f6e379203fedd7245237dd --- /dev/null +++ b/smtk/attribute/operators/EditAttributeItem.cxx @@ -0,0 +1,150 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= + +#include "smtk/attribute/operators/EditAttributeItem.h" + +#include "smtk/attribute/operators/EditAttributeItem_xml.h" + +#include "smtk/operation/MarkGeometry.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/ComponentItem.h" +#include "smtk/attribute/IntItem.h" +#include "smtk/attribute/Resource.h" +#include "smtk/attribute/ResourceItem.h" +#include "smtk/attribute/StringItem.h" +#include "smtk/attribute/VoidItem.h" + +#include "smtk/geometry/Geometry.h" + +#include "smtk/io/Logger.h" + +namespace smtk +{ +namespace attribute +{ + +EditAttributeItem::Result EditAttributeItem::operateInternal() +{ + bool didModify = false; + auto params = this->parameters(); + auto attrib = params->associations()->valueAs(); + if (!attrib) + { + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + auto item = attrib->itemAtPath(params->findString("item path")->value()); + if (!item) + { + smtkErrorMacro( + this->log(), "No item at path \"" << params->findString("item path")->value() << "\"."); + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + auto enableItem = params->findInt("enable"); + int enable = enableItem->isEnabled() ? enableItem->value() : -1; + if (enable >= 0) + { + if (!item->isOptional()) + { + smtkErrorMacro(this->log(), "Cannot enable/disable a non-optional item."); + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + int current = item->isEnabled() ? 1 : 0; + if (current != enable) + { + item->setIsEnabled(enable); + didModify = true; + } + // else: It is not an error to request a non-change. Silently ignore. + } + + int extend = params->findInt("extend")->value(); + if (extend >= 0) + { + auto vitem = std::dynamic_pointer_cast(item); + if (!vitem) + { + // TODO: Handle ReferenceItem. + smtkErrorMacro(this->log(), "Cannot extend an item of type \"" << item->typeName() << "\"."); + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + if (!vitem->setNumberOfValues(extend)) + { + smtkErrorMacro(this->log(), "Cannot extend item to length " << extend << "."); + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + didModify = true; + } + + auto valueItem = params->findString("value"); + if (valueItem->numberOfValues() > 0) + { + auto vitem = std::dynamic_pointer_cast(item); + if (!vitem) + { + // TODO: Handle ReferenceItem. + smtkErrorMacro(this->log(), "Cannot extend an item of type \"" << item->typeName() << "\"."); + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + if (vitem->numberOfValues() != valueItem->numberOfValues()) + { + smtkErrorMacro( + this->log(), + "Cannot set values with mismatching lengths " << vitem->numberOfValues() << " vs " + << valueItem->numberOfValues() << "."); + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + std::size_t numVals = vitem->numberOfValues(); + for (std::size_t ii = 0; ii < numVals; ++ii) + { + if (vitem->valueAsString(ii) != valueItem->value(ii)) + { + if (!vitem->setValueFromString(ii, valueItem->value(ii))) + { + smtkErrorMacro( + this->log(), "Cannot set value " << ii << " to " << valueItem->value(ii) << "."); + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + didModify = true; + } + } + } + auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED); + auto modOut = result->findComponent("modified"); + if (didModify) + { + modOut->appendValue(attrib); + + // Force any geometry on the modified attribute to be updated. + smtk::operation::MarkGeometry marker; + auto* rsrc = dynamic_cast(attrib->parentResource()); + if (rsrc) + { + // Only empty the cache entry if there is a geometry backend + // for the resource. + auto& geom = rsrc->geometry(); + if (geom) + { + marker.markModified(attrib); + } + } + } + + return result; +} + +void EditAttributeItem::generateSummary(Operation::Result& /*unused*/) {} + +const char* EditAttributeItem::xmlDescription() const +{ + return EditAttributeItem_xml; +} +} // namespace attribute +} // namespace smtk diff --git a/smtk/attribute/operators/EditAttributeItem.h b/smtk/attribute/operators/EditAttributeItem.h new file mode 100644 index 0000000000000000000000000000000000000000..640fa252662c09481e458b26853bd94c476fa9a9 --- /dev/null +++ b/smtk/attribute/operators/EditAttributeItem.h @@ -0,0 +1,44 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= +#ifndef smtk_operation_operators_EditAttributeItem_h +#define smtk_operation_operators_EditAttributeItem_h + +#include "smtk/operation/XMLOperation.h" + +namespace smtk +{ +namespace attribute +{ + +/**\brief An operation to edit one item of one attribute. + + If the item is optional, it may be enabled/disabled. + If the item is extensible, the number of values may be modified. + If the item has values, those values may be modified. + If any edits are performed, the operation succeeds and + the corresponding attribute is marked modified. + */ +class SMTKCORE_EXPORT EditAttributeItem : public smtk::operation::XMLOperation +{ +public: + smtkTypeMacro(smtk::attribute::EditAttributeItem); + smtkCreateMacro(EditAttributeItem); + smtkSharedFromThisMacro(smtk::operation::Operation); + smtkSuperclassMacro(smtk::operation::XMLOperation); + +protected: + Result operateInternal() override; + void generateSummary(Operation::Result&) override; + const char* xmlDescription() const override; +}; +} // namespace attribute +} // namespace smtk + +#endif // smtk_operation_operators_EditAttributeItem_h diff --git a/smtk/attribute/operators/EditAttributeItem.sbt b/smtk/attribute/operators/EditAttributeItem.sbt new file mode 100644 index 0000000000000000000000000000000000000000..92c389f59ccd5def2be79d09b0d2f5dcc1082640 --- /dev/null +++ b/smtk/attribute/operators/EditAttributeItem.sbt @@ -0,0 +1,62 @@ + + + + + + + + + + Make a change to one item of an attribute. + + + + + The attribute containing the item to be edited. + + + + + + The path to the item whose value or enabled status will be edited. + + + + + If enabled and the item is optional, its state will set to true or false + depending on the value of this item (positive for true and zero for false). + It is an error for this to be enabled and its value to be negative. + + -1 + + + + If this value is non-negative and the item is extensible, set its number of + values to the requested size. This may fail depending on the number of values + required by the item. + + -1 + + + + The vector of values the item should hold. + + + The vector of values the item should hold. + The length of this vector may be zero (if no edits to item values are to be made). + If of non-zero length, the length must match the item's current size unless the + "extend" item is non-negative – in which case the length must match that value. + + + + + + + + + + + + + + diff --git a/smtk/attribute/operators/RenameAttribute.cxx b/smtk/attribute/operators/RenameAttribute.cxx new file mode 100644 index 0000000000000000000000000000000000000000..da35ef22bf77a97d5e129d17cfd97e24390ed501 --- /dev/null +++ b/smtk/attribute/operators/RenameAttribute.cxx @@ -0,0 +1,80 @@ +//========================================================================= +// 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/operators/RenameAttribute.h" + +#include "smtk/attribute/operators/RenameAttribute_xml.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/ComponentItem.h" +#include "smtk/attribute/IntItem.h" +#include "smtk/attribute/Resource.h" +#include "smtk/attribute/ResourceItem.h" +#include "smtk/attribute/StringItem.h" +#include "smtk/attribute/VoidItem.h" + +#include "smtk/geometry/Geometry.h" + +#include "smtk/io/Logger.h" + +namespace smtk +{ +namespace attribute +{ + +RenameAttribute::Result RenameAttribute::operateInternal() +{ + auto params = this->parameters(); + auto attrItem = params->associations(); + auto nameItem = params->findString("name"); + if (attrItem->numberOfValues() != nameItem->numberOfValues()) + { + return this->createResult(smtk::operation::Operation::Outcome::FAILED); + } + + std::size_t nn = nameItem->numberOfValues(); + std::size_t numFailed = 0; + auto result = this->createResult(smtk::operation::Operation::Outcome::SUCCEEDED); + auto modified = result->findComponent("modified"); + for (std::size_t ii = 0; ii < nn; ++ii) + { + auto attribute = attrItem->valueAs(ii); + if (!attribute) + { + continue; + } + if (!attribute->attributeResource()->rename(attribute, nameItem->value(ii))) + { + smtkErrorMacro( + this->log(), + "Failed to rename \"" << attribute->name() << "\" to \"" << nameItem->value(ii) << "\"."); + ++numFailed; + } + else + { + modified->appendValue(attribute); + } + } + + if (numFailed > 0) + { + smtk::operation::setOutcome(result, smtk::operation::Operation::Outcome::FAILED); + } + return result; +} + +void RenameAttribute::generateSummary(Operation::Result& /*unused*/) {} + +const char* RenameAttribute::xmlDescription() const +{ + return RenameAttribute_xml; +} +} // namespace attribute +} // namespace smtk diff --git a/smtk/attribute/operators/RenameAttribute.h b/smtk/attribute/operators/RenameAttribute.h new file mode 100644 index 0000000000000000000000000000000000000000..622e5e6544c3186ff575417f725d07cd2b188027 --- /dev/null +++ b/smtk/attribute/operators/RenameAttribute.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_operation_operators_RenameAttribute_h +#define smtk_operation_operators_RenameAttribute_h + +#include "smtk/operation/XMLOperation.h" + +namespace smtk +{ +namespace attribute +{ + +/**\brief An operation to create an attribute according to a concrete definition. + */ +class SMTKCORE_EXPORT RenameAttribute : public smtk::operation::XMLOperation +{ +public: + smtkTypeMacro(smtk::attribute::RenameAttribute); + smtkCreateMacro(RenameAttribute); + smtkSharedFromThisMacro(smtk::operation::Operation); + smtkSuperclassMacro(smtk::operation::XMLOperation); + +protected: + Result operateInternal() override; + void generateSummary(Operation::Result&) override; + const char* xmlDescription() const override; +}; +} // namespace attribute +} // namespace smtk + +#endif // smtk_operation_operators_RenameAttribute_h diff --git a/smtk/attribute/operators/RenameAttribute.sbt b/smtk/attribute/operators/RenameAttribute.sbt new file mode 100644 index 0000000000000000000000000000000000000000..c44b1a0db26c7bbfee0f1f068d8dbdb2ca0ca9b1 --- /dev/null +++ b/smtk/attribute/operators/RenameAttribute.sbt @@ -0,0 +1,35 @@ + + + + + + + + + + Rename the associated attribute(s). + + + + + The attribute(s) to rename. + + + + + + The desired attribute name(s). + + + + + + + + + + + + diff --git a/smtk/attribute/operators/Signal.sbt b/smtk/attribute/operators/Signal.sbt index 7301fad7e6b86a305af2fac748003908c95bb4bb..74bb1fa19a1793faa77eb87e7e8cccfe58366f30 100644 --- a/smtk/attribute/operators/Signal.sbt +++ b/smtk/attribute/operators/Signal.sbt @@ -1,5 +1,5 @@ - + diff --git a/smtk/attribute/pybind11/PybindItem.h b/smtk/attribute/pybind11/PybindItem.h index c756d835f37a2b73ff9b1681489495b867cb2002..995524d9e87db4e2740fc984b05cd6ffc63b9e28 100644 --- a/smtk/attribute/pybind11/PybindItem.h +++ b/smtk/attribute/pybind11/PybindItem.h @@ -19,9 +19,18 @@ #include "smtk/attribute/CopyAssignmentOptions.h" #include "smtk/io/Logger.h" #include "smtk/simulation/UserData.h" +#include "smtk/view/HandleManager.h" + +#include +#include namespace py = pybind11; +namespace +{ + std::unordered_set allItems; +} + inline PySharedPtrClass< smtk::attribute::Item > pybind11_init_smtk_attribute_Item(py::module &m) { PySharedPtrClass< smtk::attribute::Item > instance(m, "Item"); @@ -81,6 +90,20 @@ inline PySharedPtrClass< smtk::attribute::Item > pybind11_init_smtk_attribute_It auto result = item.assign(sourceItem, options, logger); return result.success(); }, py::arg("sourceItem"), py::arg("options"), py::arg("logger")) + .def("pointer", [](smtk::attribute::Item& self) + { + return smtk::view::HandleManager::instance()->handle(&self); + + }) + .def_static("fromPointer", [](const std::string& ptrStr) -> std::shared_ptr + { + auto* item = smtk::view::HandleManager::instance()->fromHandle(ptrStr); + if (!item) + { + return smtk::attribute::Item::Ptr(); + } + return item->shared_from_this(); + }) .def_static("type2String", &smtk::attribute::Item::type2String, py::arg("t")) .def_static("string2Type", &smtk::attribute::Item::string2Type, py::arg("s")) ; diff --git a/smtk/attribute/pybind11/PybindValueItemTemplate.h b/smtk/attribute/pybind11/PybindValueItemTemplate.h index 7377993b3990ac4d4923e12e4e38f1846b47d26a..c730ab90ae0ef5fea24a9cb94c8f5d1ed6f3d181 100644 --- a/smtk/attribute/pybind11/PybindValueItemTemplate.h +++ b/smtk/attribute/pybind11/PybindValueItemTemplate.h @@ -40,6 +40,20 @@ inline PySharedPtrClass< smtk::attribute::ValueItemTemplate, smtk::attribut .def("value", (int (smtk::attribute::ValueItemTemplate::*)(::size_t) const) &smtk::attribute::ValueItemTemplate::value, py::arg("element") = 0) .def("value", (int (smtk::attribute::ValueItemTemplate::*)(smtk::io::Logger&) const) &smtk::attribute::ValueItemTemplate::value, py::arg("log")) .def("value", (int (smtk::attribute::ValueItemTemplate::*)(::size_t, smtk::io::Logger&) const) &smtk::attribute::ValueItemTemplate::value, py::arg("element"), py::arg("log")) + .def("setValues", [&](smtk::attribute::ValueItemTemplate* self, const std::vector& values) + { + return self->setValues(values.begin(), values.end()); + }, py::arg("values")) + .def("values", [&](smtk::attribute::ValueItemTemplate* self) -> std::vector + { + std::vector values; + values.reserve(self->numberOfValues()); + for (const auto& vv : *self) + { + values.push_back(vv); + } + return values; + }) ; return instance; } @@ -66,6 +80,20 @@ inline PySharedPtrClass, smtk::attrib .def("value", (double (smtk::attribute::ValueItemTemplate::*)(::size_t) const) &smtk::attribute::ValueItemTemplate::value, py::arg("element") = 0) .def("value", (double (smtk::attribute::ValueItemTemplate::*)(smtk::io::Logger&) const) &smtk::attribute::ValueItemTemplate::value, py::arg("log")) .def("value", (double (smtk::attribute::ValueItemTemplate::*)(::size_t, smtk::io::Logger&) const) &smtk::attribute::ValueItemTemplate::value, py::arg("element"), py::arg("log")) + .def("setValues", [&](smtk::attribute::ValueItemTemplate* self, const std::vector& values) + { + return self->setValues(values.begin(), values.end()); + }, py::arg("values")) + .def("values", [&](smtk::attribute::ValueItemTemplate* self) -> std::vector + { + std::vector values; + values.reserve(self->numberOfValues()); + for (const auto& vv : *self) + { + values.push_back(vv); + } + return values; + }) ; return instance; } @@ -92,9 +120,22 @@ inline PySharedPtrClass, smtk::a .def("value", (std::string (smtk::attribute::ValueItemTemplate::*)(::size_t) const) &smtk::attribute::ValueItemTemplate::value, py::arg("element") = 0) .def("value", (std::string (smtk::attribute::ValueItemTemplate::*)(smtk::io::Logger&) const) &smtk::attribute::ValueItemTemplate::value, py::arg("log")) .def("value", (std::string (smtk::attribute::ValueItemTemplate::*)(::size_t, smtk::io::Logger&) const) &smtk::attribute::ValueItemTemplate::value, py::arg("element"), py::arg("log")) + .def("setValues", [&](smtk::attribute::ValueItemTemplate* self, const std::vector& values) + { + return self->setValues(values.begin(), values.end()); + }, py::arg("values")) + .def("values", [&](smtk::attribute::ValueItemTemplate* self) -> std::vector + { + std::vector values; + values.reserve(self->numberOfValues()); + for (const auto& vv : *self) + { + values.push_back(vv); + } + return values; + }) ; return instance; - } #endif diff --git a/smtk/attribute/testing/python/CMakeLists.txt b/smtk/attribute/testing/python/CMakeLists.txt index 967dab4405e605005786ba4408d73b8258690b9d..08554423d17d8bae0ed616558695b6fb74ca8293 100644 --- a/smtk/attribute/testing/python/CMakeLists.txt +++ b/smtk/attribute/testing/python/CMakeLists.txt @@ -29,6 +29,14 @@ if (SMTK_ENABLE_VTK_SESSION) list(APPEND vtk_tests buildAttributeTest) endif() +# Tests that require plugins to be loaded currently require ParaView. +set(paraview_tests) +if (SMTK_ENABLE_PARAVIEW_SUPPORT) + list(APPEND paraview_tests + attributeOperationsAndHandlesTest + ) +endif() + # Tests that require SMTK_DATA_DIR set(smtkAttributePythonDataTests copyDefinitionTest @@ -45,6 +53,7 @@ set(smtkAttributePythonNewDataTests valueItemRotateTest vectorExpressionTest ${vtk_tests} + ${paraview_tests} ) if (SMTK_DATA_DIR) diff --git a/smtk/attribute/testing/python/attributeOperationsAndHandlesTest.py b/smtk/attribute/testing/python/attributeOperationsAndHandlesTest.py new file mode 100644 index 0000000000000000000000000000000000000000..1024842f22771374dc417fbda320543513219885 --- /dev/null +++ b/smtk/attribute/testing/python/attributeOperationsAndHandlesTest.py @@ -0,0 +1,147 @@ +import sys +import os +# ============================================================================= +# +# 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. +# +# ============================================================================= + +import smtk +import smtk.string +import smtk.common +import smtk.io +import smtk.resource +import smtk.attribute +import smtk.operation +import smtk.view +from smtk import attribute +from smtk import io + +import smtk.testing + + +class TestAttributeOperationsAndHandles(smtk.testing.TestCase): + + def setUp(self): + print('Setup') + if smtk.testing.DATA_DIR == '': + self.skipTest('SMTK test-data directory not provided') + + self.app = smtk.applicationContext() + print(f' Application context? {self.app != None}') + self.resource_mgr = self.app.get('smtk.resource.Manager') + self.operation_mgr = self.app.get('smtk.operation.Manager') + print(f' Resource manager? {self.resource_mgr != None}') + print(f' Operation manager? {self.operation_mgr != None}') + if self.resource_mgr == None: + # This test should only be run if ParaView support was enabled. + raise Exception('Required plugins were not loaded.') + + # Attribute registrar + print(' Attribute registrar') + smtk.attribute.Registrar.registerTo(self.resource_mgr) + smtk.attribute.Registrar.registerTo(self.operation_mgr) + # Operation registrar + print(' Operation registrar') + smtk.operation.Registrar.registerTo(self.operation_mgr) + + print(' Resource') + self.resource = self.resource_mgr.createResource( + 'smtk::attribute::Resource') + print(' created.') + logger = smtk.io.Logger() + reader = smtk.io.AttributeReader() + filenm = os.path.join(smtk.testing.DATA_DIR, 'attribute', + 'attribute_collection', 'HydraTemplateV1.sbt') + status = reader.read(self.resource, filenm, logger) + print(' read from template.') + print( + '\n'.join([logger.record(i).message for i in range(logger.numberOfRecords())])) + self.assertFalse(status, 'Could not read {fn}'.format(fn=filenm)) + + def checkAttributeCreation(self, op, result): + """When the creation operation launched by testAttributeOperations() completes, + this method is called to test its success.""" + outcome = smtk.operation.outcome(result) + self.assertEqual( + outcome, smtk.operation.Operation.Outcome.SUCCEEDED, 'Creation failed') + self.att = result.findComponent('created').values()[0] + self.attHandle = smtk.view.HandleManager.instance().handle(self.att) + print(f' Attribute {str(self.att)} handle {self.attHandle}') + + def checkAttributeRename(self, op, result): + """When the rename operation launched by testAttributeOperations() completes, + this method is called to test its success.""" + outcome = smtk.operation.outcome(result) + self.assertEqual( + outcome, smtk.operation.Operation.Outcome.SUCCEEDED, 'Rename failed') + self.assertEqual(self.att, smtk.view.HandleManager.instance().object(self.attHandle), + 'Attribute should still be fetchable from handle.') + self.assertEqual(self.att.name(), 'thingy', + 'Name should have been changed.') + + def checkAttributeDeletion(self, op, result): + """When the deletion operation launched by testAttributeOperations() completes, + this method is called to test its success.""" + outcome = smtk.operation.outcome(result) + op.log().setFlushToStdout(True) + self.assertEqual( + outcome, smtk.operation.Operation.Outcome.SUCCEEDED, 'Delete failed') + + def handleEvents(self, handleMap, event): + """Called when handles are created/modified/expunged.""" + print(' Handle updates', event, handleMap) + + def testAttributeOperations(self): + print('1. Insert handle observer') + key = smtk.view.HandleManager.instance().observers().insert( + self.handleEvents, 0, 'Handle observer') + print(f' Key assigned? {key.assigned()}') + + # Test creation + print('2. Create an attribute') + op = self.operation_mgr.createOperation( + 'smtk::attribute::CreateAttribute') + print(f' Operation? {op != None}') + op.parameters().associate(self.resource) + op.parameters().findString('definition').setValue('hydrostat') + print(f' Operation able to run? {op.ableToOperate()}') + res = op.operate() + print(f' Operation outcome: {smtk.operation.outcome(res)}') + self.checkAttributeCreation(op, res) + + # Test rename + print('3. Rename an attribute') + op = self.operation_mgr.createOperation( + 'smtk::attribute::RenameAttribute') + print(f' Operation? {op != None}') + op.parameters().associate(self.att) + op.parameters().findString('name').appendValue('thingy') + print(f' Operation able to run? {op.ableToOperate()}') + res = op.operate() + print(f' Operation outcome: {smtk.operation.outcome(res)}') + self.checkAttributeRename(op, res) + + # Test deletion + print('4. Delete an attribute') + op = self.operation_mgr.createOperation( + 'smtk::attribute::DeleteAttribute') + print(f' Operation? {op != None}') + op.parameters().associate(self.att) + print(f' Operation able to run? {op.ableToOperate()}') + res = op.operate() + print(f' Operation outcome: {smtk.operation.outcome(res)}') + self.checkAttributeDeletion(op, res) + self.assertEqual(None, smtk.view.HandleManager.instance().object(self.attHandle), + 'Attribute should not still be fetchable from handle.') + + +if __name__ == '__main__': + smtk.testing.process_arguments() + smtk.testing.main() diff --git a/smtk/common/CMakeLists.txt b/smtk/common/CMakeLists.txt index 132b5029b941f38a0fbd3a62fa288f8ffc004d70..5781f463595ea9241e817cba7b1d4e91a8737b28 100644 --- a/smtk/common/CMakeLists.txt +++ b/smtk/common/CMakeLists.txt @@ -1,91 +1,82 @@ # set up sources to build +set(commonClasses + Archive + Categories + Color + CompilerInformation + DateTime + DateTimeZonePair + Environment + Extension + Factory + FileLocation + Generator + GeometryUtilities + InfixExpressionError + InfixExpressionEvaluation + InfixExpressionGrammar + InfixExpressionGrammarImpl + Instances + Links + Managers + Observers + Paths + RangeDetector + RuntimeTypeContainer + Singleton + Singletons + Status + StringUtil + ThreadPool + TimeZone + timezonespec + TypeHierarchy + TypeMap + TypeName + TypeContainer + TypeTraits + URL + UUID + UUIDGenerator + VersionNumber + WeakReferenceWrapper + + categories/Actions + categories/Evaluators + categories/Grammar + + json/Helper + json/jsonLinks + json/jsonTypeMap + json/jsonUUID + json/jsonVersionNumber + + testing/cxx/helpers + + update/Factory +) + set(commonSrcs - Archive.cxx - Categories.cxx - Color.cxx - DateTime.cxx - DateTimeZonePair.cxx - Environment.cxx - Extension.cxx - FileLocation.cxx - InfixExpressionGrammar.cxx - Managers.cxx - Paths.cxx - RuntimeTypeContainer.cxx - Status.cxx - StringUtil.cxx - TimeZone.cxx - timezonespec.cxx - TypeContainer.cxx - URL.cxx - UUID.cxx - UUIDGenerator.cxx - VersionNumber.cxx - json/Helper.cxx - json/jsonLinks.cxx - json/jsonUUID.cxx - json/jsonVersionNumber.cxx ) set(commonHeaders - Archive.h - Categories.h - Color.h - CompilerInformation.h - DateTime.h - DateTimeZonePair.h Deprecation.h - Environment.h - Extension.h - Factory.h - FileLocation.h - Generator.h - GeometryUtilities.h - InfixExpressionError.h - InfixExpressionEvaluation.h - InfixExpressionGrammar.h - InfixExpressionGrammarImpl.h - Instances.h - Links.h - Managers.h - Observers.h - Paths.h Processing.h - RangeDetector.h - RuntimeTypeContainer.h - Singleton.h - Status.h - StringUtil.h - ThreadPool.h - TimeZone.h - timezonespec.h - TypeHierarchy.h - TypeMap.h - TypeName.h - TypeContainer.h - TypeTraits.h - URL.h - UUID.h - UUIDGenerator.h - VersionNumber.h VersionMacros.h Visit.h - WeakReferenceWrapper.h - categories/Actions.h - categories/Evaluators.h - categories/Grammar.h - json/Helper.h - json/jsonLinks.h - json/jsonTypeMap.h - json/jsonUUID.h - json/jsonVersionNumber.h - testing/cxx/helpers.h - - update/Factory.h ${CMAKE_CURRENT_BINARY_DIR}/Version.h ) +foreach(class ${commonClasses}) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${class}.h") + list(APPEND commonHeaders "${class}.h") + endif() + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${class}.cxx") + list(APPEND commonSrcs "${class}.cxx") + endif() +endforeach() + if (APPLE) set(commonSrcs ${commonSrcs} PathsHelperMacOSX.mm) set(commonHeaders ${commonHeaders} PathsHelperMacOSX.h) diff --git a/smtk/common/Singleton.h b/smtk/common/Singleton.h index b2b4924a967404127bb46aea00d7554f8cc91cf6..114925311464b3f4c5e4523f1045994a671ef16b 100644 --- a/smtk/common/Singleton.h +++ b/smtk/common/Singleton.h @@ -15,6 +15,12 @@ namespace smtk { namespace common { + +/// This is a base class for objects that should have a single instance per process. +/// +/// The template provides an `instance()` method for creating or fetching the instance. +/// This is distinct from the the Singletons (plural) class also in `smtk/common`, which +/// is a container that can hold a single instance of any given type for access as needed. template class Singleton { diff --git a/smtk/common/Singletons.cxx b/smtk/common/Singletons.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e5e0fc6c0b5463449b1497da0621e38c2332411f --- /dev/null +++ b/smtk/common/Singletons.cxx @@ -0,0 +1,75 @@ +//========================================================================= +// 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/common/Singletons.h" + +#include "smtk/common/TypeContainer.h" + +#include + +namespace smtk +{ +namespace common +{ +namespace +{ + +// rely on default initialization to 0 for static variables +unsigned int s_singletonsCleanupCounter; +TypeContainer* s_singletons; +std::mutex* s_singletonsMutex; + +} // anonymous namespace + +namespace detail +{ + +singletonsCleanup::singletonsCleanup() +{ + if (++s_singletonsCleanupCounter == 1) + { + s_singletons = nullptr; + s_singletonsMutex = new std::mutex; + } +} + +singletonsCleanup::~singletonsCleanup() +{ + if (--s_singletonsCleanupCounter == 0) + { + finalizeSingletons(); + delete s_singletonsMutex; + } +} + +} // namespace detail + +TypeContainer& singletons() +{ + std::lock_guard guard(*s_singletonsMutex); + if (!s_singletons) + { + s_singletons = new TypeContainer; + } + return *s_singletons; +} + +void finalizeSingletons() +{ + // Per @michael.migliore this can cause exceptions on macos in cases + // when the mutex is destroyed before finalizeSingletons() is called: + // std::lock_guard guard(s_singletonsMutex); + + delete s_singletons; + s_singletons = nullptr; +} + +} // namespace common +} // namespace smtk diff --git a/smtk/common/Singletons.h b/smtk/common/Singletons.h new file mode 100644 index 0000000000000000000000000000000000000000..2b479754571f73acb4fcde133126671b852dfc72 --- /dev/null +++ b/smtk/common/Singletons.h @@ -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. +//========================================================================= + +#ifndef smtk_common_Singletons_h +#define smtk_common_Singletons_h + +#include "smtk/common/TypeContainer.h" // For API and exports. + +namespace smtk +{ +namespace common +{ + +/// Return a container of singleton objects indexed by their type. +/// +/// Because the index is based on the type of the object being +/// contained (it is a checksum computed on the typename-string), +/// there can be only zero or one objects of a given type in the +/// container. +SMTKCORE_EXPORT TypeContainer& singletons(); + +/// Destroy the container holding all registered singleton objects. +/// +/// The destructors of any contained objects will be called. +/// This function is invoked at exit, but if your application +/// needs to ensure objects are released before the other +/// destructors are called (since no ordering is guaranteed for +/// statically-allocated objects), you may call this at any time. +/// +/// Libraries should not invoke this function; if your library +/// uses this singleton container, your code run at exit should +/// simply remove any stored objects rather than forcing all of +/// the application's singletons to be destroyed. +SMTKCORE_EXPORT void finalizeSingletons(); + +namespace detail +{ + +// Implementation detail for Schwarz counter idiom. +class SMTKCORE_EXPORT singletonsCleanup +{ +public: + singletonsCleanup(); + singletonsCleanup(const singletonsCleanup& other) = delete; + singletonsCleanup& operator=(const singletonsCleanup& rhs) = delete; + ~singletonsCleanup(); +}; + +static singletonsCleanup singletonsCleanupInstance; + +} // namespace detail + +} // namespace common +} // namespace smtk + +#endif // smtk_common_Singletons_h diff --git a/smtk/common/pybind11/PybindCommon.cxx b/smtk/common/pybind11/PybindCommon.cxx index 8f8ffb9150814d96243d98ef495b0dd1b28a90d7..09cd9807024ff1cde7b5ef02ce774108fdf7ab4c 100644 --- a/smtk/common/pybind11/PybindCommon.cxx +++ b/smtk/common/pybind11/PybindCommon.cxx @@ -54,6 +54,13 @@ PYBIND11_MODULE(_smtkPybindCommon, common) { common.doc() = ""; + // Import modules so that return types of smtk.common.Managers' get() method are known. + py::module::import("smtk.string"); + py::module::import("smtk.resource"); + py::module::import("smtk.operation"); + py::module::import("smtk.geometry"); + py::module::import("smtk.view"); + // The order of these function calls is important! It was determined by // comparing the dependencies of each of the wrapped objects. py::class_< smtk::common::Categories > smtk_common_Categories = pybind11_init_smtk_common_Categories(common); diff --git a/smtk/common/pybind11/PybindManagers.h b/smtk/common/pybind11/PybindManagers.h index 4ee9f7c3f2b0b8d148d11a6d53474a8ecae218e4..7f62b789af0355a77a53d9340bedf6ec531c77ed 100644 --- a/smtk/common/pybind11/PybindManagers.h +++ b/smtk/common/pybind11/PybindManagers.h @@ -37,6 +37,24 @@ inline PySharedPtrClass< smtk::common::Managers > pybind11_init_smtk_common_Mana PySharedPtrClass< smtk::common::Managers > instance(m, "Managers"); instance .def_static("create", (std::shared_ptr (*)()) &smtk::common::Managers::create) + .def("__enter__", [](smtk::common::Managers& self) + { + // Keep the instance alive for the duration of the context. + return self.shared_from_this(); + }, "Enter the runtime context related to this object." + ) + .def("__exit__", [](smtk::common::Managers& self, + const std::optional& exc_type, + const std::optional& exc_value, + const std::optional& traceback + ) + { + (void)self; + (void)exc_type; + (void)exc_value; + (void)traceback; + }, "Exit the runtime context related to this object." + ) .def("insert_or_assign", [](smtk::common::Managers& managers, std::shared_ptr& operationManager) { std::cerr << "Deprecated after 23.08; use insertOrAssign() instead.\n"; diff --git a/smtk/common/testing/cxx/UnitTestTypeHierarchy.cxx b/smtk/common/testing/cxx/UnitTestTypeHierarchy.cxx index c8bf95d2cb2f837443eea8f47243f3038fb163c6..605649dedefda5350795f12ed8527a87a3aef977 100644 --- a/smtk/common/testing/cxx/UnitTestTypeHierarchy.cxx +++ b/smtk/common/testing/cxx/UnitTestTypeHierarchy.cxx @@ -14,6 +14,7 @@ #include "smtk/attribute/Attribute.h" #include "smtk/attribute/Definition.h" +#include "smtk/attribute/DoubleItem.h" #include "smtk/attribute/Resource.h" #include "smtk/common/testing/cxx/helpers.h" @@ -150,6 +151,13 @@ int UnitTestTypeHierarchy(int /*unused*/, char** const /*unused*/) "smtk::resource::PersistentObject" }), "Failed resource hierarchy"); + test( + validateHierarchy({ "smtk::attribute::DoubleItem", + "smtk::attribute::ValueItemTemplate", + "smtk::attribute::ValueItem", + "smtk::attribute::Item" }), + "Failed attribute-item hierarchy"); + test( validateHierarchy( { "smtk::model::Entity", "smtk::resource::Component", "smtk::resource::PersistentObject" }), diff --git a/smtk/extension/paraview/widgets/pqConePropertyWidget.cxx b/smtk/extension/paraview/widgets/pqConePropertyWidget.cxx index 40bf0d04f053db832601d07149a7fe84ea166fcc..fa40964ff8d56418fbb56f907a71b2683000e516 100644 --- a/smtk/extension/paraview/widgets/pqConePropertyWidget.cxx +++ b/smtk/extension/paraview/widgets/pqConePropertyWidget.cxx @@ -22,7 +22,11 @@ #include "vtkCommand.h" #include "vtkMath.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif class pqConePropertyWidget::Internals { diff --git a/smtk/extension/paraview/widgets/pqDiskPropertyWidget.cxx b/smtk/extension/paraview/widgets/pqDiskPropertyWidget.cxx index 175a650fd2ba6587506b3253b6ecccbac375ed74..d4089a7dffb59bff4d8cbcde0cf813cbb1f2e140 100644 --- a/smtk/extension/paraview/widgets/pqDiskPropertyWidget.cxx +++ b/smtk/extension/paraview/widgets/pqDiskPropertyWidget.cxx @@ -23,7 +23,11 @@ #include "vtkCommand.h" #include "vtkMath.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif #include diff --git a/smtk/extension/paraview/widgets/pqSMTKBoxItemWidget.cxx b/smtk/extension/paraview/widgets/pqSMTKBoxItemWidget.cxx index ea4c28625e5142f555a4c04fb69d2062904f22fb..f6506a87d1c09a48389ed2843f7165d13135ff9b 100644 --- a/smtk/extension/paraview/widgets/pqSMTKBoxItemWidget.cxx +++ b/smtk/extension/paraview/widgets/pqSMTKBoxItemWidget.cxx @@ -33,7 +33,11 @@ #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif #include diff --git a/smtk/extension/paraview/widgets/pqSMTKConeItemWidget.cxx b/smtk/extension/paraview/widgets/pqSMTKConeItemWidget.cxx index ba4f013478bb417487ec742058d3951eca97a598..c4820782974ec17fec2491e488a12c2f8bcb0764 100644 --- a/smtk/extension/paraview/widgets/pqSMTKConeItemWidget.cxx +++ b/smtk/extension/paraview/widgets/pqSMTKConeItemWidget.cxx @@ -32,7 +32,11 @@ #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif using qtItem = smtk::extension::qtItem; using qtAttributeItemInfo = smtk::extension::qtAttributeItemInfo; diff --git a/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.cxx b/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.cxx index f004ab44124c1c7424a664e99f848068f5c5e195..d8fcf88bf560550641b8c8e50a69ba48eb3445fc 100644 --- a/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.cxx +++ b/smtk/extension/paraview/widgets/pqSMTKDiskItemWidget.cxx @@ -32,7 +32,11 @@ #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif using qtItem = smtk::extension::qtItem; using qtAttributeItemInfo = smtk::extension::qtAttributeItemInfo; diff --git a/smtk/extension/paraview/widgets/pqSMTKInfiniteCylinderItemWidget.cxx b/smtk/extension/paraview/widgets/pqSMTKInfiniteCylinderItemWidget.cxx index 73c5ca6466fc52e18bba6c76ae2b4046aca5cf2e..849306c00e8f0ac61158f5798aa217742ae79200 100644 --- a/smtk/extension/paraview/widgets/pqSMTKInfiniteCylinderItemWidget.cxx +++ b/smtk/extension/paraview/widgets/pqSMTKInfiniteCylinderItemWidget.cxx @@ -32,7 +32,11 @@ #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif using qtItem = smtk::extension::qtItem; using qtAttributeItemInfo = smtk::extension::qtAttributeItemInfo; diff --git a/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx b/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx index bcc8f398db8cb575f267a89c2c52ab0f69a51173..7a7a563ca9e82c148ed42b6e321092dedc94f36c 100644 --- a/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx +++ b/smtk/extension/paraview/widgets/pqSMTKTransformWidget.cxx @@ -42,7 +42,11 @@ #include "vtkSMPropertyHelper.h" #include "vtkSMProxy.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif #include diff --git a/smtk/extension/vtk/source/vtkConeFrustum.cxx b/smtk/extension/vtk/source/vtkConeFrustum.cxx index 135fb784b1a1e2013a4c35a9d6eb7e1031908a39..da49834aee22fc82d97c8676ba13bd36ce6f24d7 100644 --- a/smtk/extension/vtk/source/vtkConeFrustum.cxx +++ b/smtk/extension/vtk/source/vtkConeFrustum.cxx @@ -23,7 +23,11 @@ #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkTransform.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif #include diff --git a/smtk/extension/vtk/source/vtkDisk.cxx b/smtk/extension/vtk/source/vtkDisk.cxx index c8663d85b9f314e1494d468456e09109ff077507..9fdb268773ec69f7f1e7cc4f0e5bca906100d4c6 100644 --- a/smtk/extension/vtk/source/vtkDisk.cxx +++ b/smtk/extension/vtk/source/vtkDisk.cxx @@ -23,7 +23,11 @@ #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkTransform.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif #include diff --git a/smtk/extension/vtk/source/vtkImplicitConeFrustum.cxx b/smtk/extension/vtk/source/vtkImplicitConeFrustum.cxx index b6c5b26b15dcc13099cb4b928924e1d97fcc0772..90660fc0c2cf02fb90b709ab3b7e1d09e283bed4 100644 --- a/smtk/extension/vtk/source/vtkImplicitConeFrustum.cxx +++ b/smtk/extension/vtk/source/vtkImplicitConeFrustum.cxx @@ -14,7 +14,11 @@ #include "vtkObjectFactory.h" #include "vtkPlane.h" #include "vtkTransform.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif #include diff --git a/smtk/extension/vtk/source/vtkImplicitDisk.cxx b/smtk/extension/vtk/source/vtkImplicitDisk.cxx index 9987a064824f008307de1bbb6c1c264b34ed8587..83f4b69039312fdd3b05e5fc4283c3452c98b50b 100644 --- a/smtk/extension/vtk/source/vtkImplicitDisk.cxx +++ b/smtk/extension/vtk/source/vtkImplicitDisk.cxx @@ -14,7 +14,11 @@ #include "vtkObjectFactory.h" #include "vtkPlane.h" #include "vtkTransform.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif #include diff --git a/smtk/extension/vtk/widgets/vtkConeRepresentation.cxx b/smtk/extension/vtk/widgets/vtkConeRepresentation.cxx index 7cc17f04a54b77c4f7a6ce3903ac61c758a8b98e..4da5b12524980dc6b25a0c2c382163ae6d6f8c5a 100644 --- a/smtk/extension/vtk/widgets/vtkConeRepresentation.cxx +++ b/smtk/extension/vtk/widgets/vtkConeRepresentation.cxx @@ -44,9 +44,13 @@ #include "vtkSphereSource.h" #include "vtkTransform.h" #include "vtkTubeFilter.h" -#include "vtkVectorOperators.h" +#include "vtkVersionMacros.h" #include "vtkWindow.h" +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) +#include "vtkVectorOperators.h" +#endif + #include #include //for FLT_EPSILON diff --git a/smtk/extension/vtk/widgets/vtkDiskRepresentation.cxx b/smtk/extension/vtk/widgets/vtkDiskRepresentation.cxx index 80d9fb99eedc29ae54f416a3b6ea7bb99e1eed9f..b8a488ecec34766a057c0842c1263e55be52c28c 100644 --- a/smtk/extension/vtk/widgets/vtkDiskRepresentation.cxx +++ b/smtk/extension/vtk/widgets/vtkDiskRepresentation.cxx @@ -45,9 +45,13 @@ #include "vtkSphereSource.h" #include "vtkTransform.h" #include "vtkTubeFilter.h" -#include "vtkVectorOperators.h" +#include "vtkVersionMacros.h" #include "vtkWindow.h" +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) +#include "vtkVectorOperators.h" +#endif + #include #include //for FLT_EPSILON diff --git a/smtk/graph/Registrar.cxx b/smtk/graph/Registrar.cxx index 77686485e1f3f3cd9098e6b58840e0bcc1c8b79c..f8d2620fe5976cdf911eac58dc9e4dc5ae45decc 100644 --- a/smtk/graph/Registrar.cxx +++ b/smtk/graph/Registrar.cxx @@ -54,6 +54,9 @@ void Registrar::registerTo(const smtk::resource::Manager::Ptr& resourceManager) auto& typeLabels = resourceManager->objectTypeLabels(); typeLabels[smtk::common::typeName()] = "graph resource"; typeLabels[smtk::common::typeName()] = "graph node"; + // We need to create an entry in the string-token manager for this type-name + // so that it exists when referenced by DeleteArc::registerDeleter(): + smtk::string::Token dummy("smtk::graph::ResourceBase"); } void Registrar::unregisterFrom(const smtk::resource::Manager::Ptr& resourceManager) diff --git a/smtk/graph/testing/cxx/TestArcs.cxx b/smtk/graph/testing/cxx/TestArcs.cxx index fca0854b2913dcc1b9f7d7a5268a0cc293c5201d..231b004095a20bcc759824b222fe3bd75d20bd76 100644 --- a/smtk/graph/testing/cxx/TestArcs.cxx +++ b/smtk/graph/testing/cxx/TestArcs.cxx @@ -863,6 +863,10 @@ void testRuntimeArcs() int TestArcs(int, char*[]) { + // We need to create an entry in the string-token manager for this type-name + // so that it exists when referenced by DeleteArc::registerDeleter(): + smtk::string::Token dummy("smtk::graph::ResourceBase"); + // Needed for log messages to appear in test output: smtk::io::Logger::instance().setFlushToStdout(true); diff --git a/smtk/graph/testing/cxx/TestRuntimeJSON.cxx b/smtk/graph/testing/cxx/TestRuntimeJSON.cxx index 691b6895b77e8409774567d8c721318ed09cba81..5de26464268b5ff8a6db01123217817940669841 100644 --- a/smtk/graph/testing/cxx/TestRuntimeJSON.cxx +++ b/smtk/graph/testing/cxx/TestRuntimeJSON.cxx @@ -74,6 +74,10 @@ int TestRuntimeJSON(int, char*[]) using namespace smtk::test; using namespace smtk::graph; + // We need to create an entry in the string-token manager for this type-name + // so that it exists when referenced by DeleteArc::registerDeleter(): + smtk::string::Token dummy("smtk::graph::ResourceBase"); + // DirectedDistinct: Notable → Comment // DirectedSelfArc: Thingy → Thingy // UndirectedSelfArc: Thingy — Thingy diff --git a/smtk/model/Registrar.cxx b/smtk/model/Registrar.cxx index 33c4718615a0fb94dd2277cca1195dfef1697ee0..21c55bb9b1238e02e21fcdd533c63a44f8ba475a 100644 --- a/smtk/model/Registrar.cxx +++ b/smtk/model/Registrar.cxx @@ -77,6 +77,7 @@ void Registrar::unregisterFrom(const smtk::operation::Manager::Ptr& operationMan void Registrar::registerTo(const smtk::resource::Manager::Ptr& resourceManager) { + smtk::string::Token dummy("smtk::model::Resource"); resourceManager->registerResource(); auto& typeLabels = resourceManager->objectTypeLabels(); diff --git a/smtk/operation/Operation.cxx b/smtk/operation/Operation.cxx index 01c3f2c585a0d933c97f48378967c3e0af8b5ab8..c47bc30494c7f8a644445a6f79a2be4519b9c330 100644 --- a/smtk/operation/Operation.cxx +++ b/smtk/operation/Operation.cxx @@ -331,6 +331,10 @@ Operation::Result Operation::operate(const BaseKey& key) smtk::attribute::IntItem::Ptr debugItem = this->parameters()->findInt("debug level"); m_debugLevel = ((debugItem && debugItem->isEnabled()) ? debugItem->value() : 0); + // We catch all exceptions here to avoid situations where resource + // locks are never released. You should never expect exceptions to + // be thrown out of an operation in any event because operations + // are run in separate threads. try { // Perform the derived operation. @@ -342,6 +346,7 @@ Operation::Result Operation::operate(const BaseKey& key) // This allows the operation to return normally so that // any threads do not continue to be blocked. result = this->createResult(Outcome::FAILED); + this->log().setFlushToStderr(true); smtkErrorMacro( this->log(), "An unhandled exception (" << e.what() << ") occurred in " << this->typeName() << "."); @@ -379,18 +384,29 @@ Operation::Result Operation::operate(const BaseKey& key) } } - // Execute post-operation observation - // Always call handlers, but based on the \a key, observers may be skipped. - // Handlers will always be invoked before other observers, but in priority order. - // In the future, we may attempt to interleave the handler and observer calls. - for (const auto& entry : instanceHandlers) + // We catch all exceptions here as well to avoid situations where resource + // locks are never released. + try { - entry.second(*this, result); + // Execute post-operation observation + // Always call handlers, but based on the \a key, observers may be skipped. + // Handlers will always be invoked before other observers, but in priority order. + // In the future, we may attempt to interleave the handler and observer calls. + for (const auto& entry : instanceHandlers) + { + entry.second(*this, result); + } + instanceHandlers.clear(); + if (key.m_observerOption == ObserverOption::InvokeObservers && observePostOperation && manager) + { + manager->observers()(*this, EventType::DID_OPERATE, result); + } } - instanceHandlers.clear(); - if (key.m_observerOption == ObserverOption::InvokeObservers && observePostOperation && manager) + catch (std::exception& e) { - manager->observers()(*this, EventType::DID_OPERATE, result); + this->log().setFlushToStderr(true); + smtkErrorMacro( + this->log(), "Caught exception \"" << e.what() << "\" during handler/observer invocation."); } // Un-manage any resources marked for removal before releasing locks. diff --git a/smtk/operation/Registrar.cxx b/smtk/operation/Registrar.cxx index ef1fb4b96381771283d3239708d449445399882e..a6c338fb1be21abb9f6a70141ecbb70bca9d3f5d 100644 --- a/smtk/operation/Registrar.cxx +++ b/smtk/operation/Registrar.cxx @@ -61,17 +61,20 @@ typedef std::tuple< void Registrar::registerTo(const smtk::common::Managers::Ptr& managers) { - if (managers->insert(smtk::operation::Manager::create())) + if (!managers->contains()) { - managers->get()->setManagers(managers); - - if (managers->contains()) + if (managers->insert(smtk::operation::Manager::create())) { - managers->get()->registerResourceManager( - managers->get()); + managers->get()->setManagers(managers); + + if (managers->contains()) + { + managers->get()->registerResourceManager( + managers->get()); + } + smtk::plugin::Manager::instance()->registerPluginsTo( + managers->get()); } - smtk::plugin::Manager::instance()->registerPluginsTo( - managers->get()); } } diff --git a/smtk/operation/pybind11/PybindManager.h b/smtk/operation/pybind11/PybindManager.h index 1066926bbbf8fe7d0df49fc04cc148a47c03a5fb..67b804d6f53ebdc9336b8548bd909fb9b9c06a4c 100644 --- a/smtk/operation/pybind11/PybindManager.h +++ b/smtk/operation/pybind11/PybindManager.h @@ -57,6 +57,10 @@ inline PySharedPtrClass< smtk::operation::Manager > pybind11_init_smtk_operation }, py::arg("module")) .def("managers", &smtk::operation::Manager::managers) .def("setManagers", &smtk::operation::Manager::setManagers, py::arg("managers")) + .def("launch", [](smtk::operation::Manager& manager, const std::shared_ptr& op) + { + manager.launchers()(op); + }, py::arg("operation")) ; return instance; } diff --git a/smtk/operation/pybind11/PybindObserver.h b/smtk/operation/pybind11/PybindObserver.h index f58768114aa05a0655a22612afff536582fb38a8..469b18b7139be1a9125ea80d650011cbd43402de 100644 --- a/smtk/operation/pybind11/PybindObserver.h +++ b/smtk/operation/pybind11/PybindObserver.h @@ -12,6 +12,7 @@ #define pybind_smtk_operation_Observer_h #include +#include #include "smtk/attribute/Attribute.h" @@ -41,6 +42,7 @@ inline py::class_< smtk::operation::Observers > pybind11_init_smtk_operation_Obs py::class_< smtk::operation::Observers::Key >(instance, "Key") .def(py::init<>()) .def("assigned", &smtk::operation::Observers::Key::assigned) + .def("release", &smtk::operation::Observers::Key::release) ; return instance; } diff --git a/smtk/operation/pybind11/PybindOperation.h b/smtk/operation/pybind11/PybindOperation.h index 5e1775d1e8b1ec6c4bd886a8bc8149b1c6d79eb6..fbad0dc2d097401e7c84bcaa45e78b73b954b4d3 100644 --- a/smtk/operation/pybind11/PybindOperation.h +++ b/smtk/operation/pybind11/PybindOperation.h @@ -137,7 +137,22 @@ inline PySharedPtrClass< smtk::operation::Operation, smtk::operation::PyOperatio .def("createResult", &smtk::operation::Operation::createResult, py::arg("arg0")) .def("manager", &smtk::operation::Operation::manager) .def("managers", &smtk::operation::Operation::managers) - .def("addHandler", &smtk::operation::Operation::addHandler, py::arg("handler"), py::arg("priority")) + .def("addHandler", + [](smtk::operation::Operation& op, smtk::operation::Handler handler, int priority) + { + // Wrap the \a handler in a new function object which obtains the GIL as the operation will + // not hold the GIL at the point handlers are invoked and the only \a handler that can be + // passed to us is a python function object (which requires the GIL). Add this wrapped handler + // to the operation rather than the one we are passed. + smtk::operation::Handler pyHandler = [handler]( + smtk::operation::Operation& op, const std::shared_ptr& result) + { + py::gil_scoped_acquire guard; + handler(op, result); + }; + op.addHandler(pyHandler, priority); + }, + py::arg("handler"), py::arg("priority") = 0) .def("removeHandler", &smtk::operation::Operation::removeHandler, py::arg("handler"), py::arg("priority")) .def("clearHandlers", &smtk::operation::Operation::clearHandlers) .def("restoreTrace", (bool (smtk::operation::Operation::*)(::std::string const &)) &smtk::operation::Operation::restoreTrace) diff --git a/smtk/operation/pybind11/PybindRegistrar.h b/smtk/operation/pybind11/PybindRegistrar.h index 2c0eec159577e52a7cfb9bed75df90dd7d7e6e95..2e8b98894ab11b972994dcd65754c34122719843 100644 --- a/smtk/operation/pybind11/PybindRegistrar.h +++ b/smtk/operation/pybind11/PybindRegistrar.h @@ -22,8 +22,12 @@ inline py::class_< smtk::operation::Registrar > pybind11_init_smtk_operation_Reg py::class_< smtk::operation::Registrar > instance(m, "Registrar"); instance .def(py::init<>()) + .def_static("registerTo", (void (*)(std::shared_ptr<::smtk::common::Managers> const &)) &smtk::operation::Registrar::registerTo) + .def_static("unregisterFrom", (void (*)(std::shared_ptr<::smtk::common::Managers> const &)) &smtk::operation::Registrar::unregisterFrom) .def_static("registerTo", (void (*)(std::shared_ptr<::smtk::operation::Manager> const &)) &smtk::operation::Registrar::registerTo) .def_static("unregisterFrom", (void (*)(std::shared_ptr<::smtk::operation::Manager> const &)) &smtk::operation::Registrar::unregisterFrom) + .def_static("registerTo", (void (*)(std::shared_ptr<::smtk::view::Manager> const &)) &smtk::operation::Registrar::registerTo) + .def_static("unregisterFrom", (void (*)(std::shared_ptr<::smtk::view::Manager> const &)) &smtk::operation::Registrar::unregisterFrom) ; return instance; } diff --git a/smtk/operation/testing/python/CMakeLists.txt b/smtk/operation/testing/python/CMakeLists.txt index 218eec617485a937b647c82ff4979ac33d1ee988..1eaf764fcb2a971d522fb2d5f165a21861ce99ac 100644 --- a/smtk/operation/testing/python/CMakeLists.txt +++ b/smtk/operation/testing/python/CMakeLists.txt @@ -9,6 +9,7 @@ set(smtkOperationPythonDataTests if (SMTK_ENABLE_POLYGON_SESSION) list(APPEND smtkOperationPythonDataTests testOperationHandler + testOperationObserver ) endif() diff --git a/smtk/operation/testing/python/testOperationObserver.py b/smtk/operation/testing/python/testOperationObserver.py new file mode 100644 index 0000000000000000000000000000000000000000..7591dead2e98ab75afa7586491049a9e60383bde --- /dev/null +++ b/smtk/operation/testing/python/testOperationObserver.py @@ -0,0 +1,110 @@ +# ============================================================================= +# +# Copyright (c) Kitware, Inc. +# All rights reserved. +# See LICENSE.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even +# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the above copyright notice for more information. +# +# ============================================================================= +import smtk +import smtk.string +import smtk.common +import smtk.io +import smtk.resource +import smtk.attribute +import smtk.operation +import smtk.session.polygon +import smtk.testing + +invokedCount = 0 +message = 'test' +addSelf = False + + +def observer(op, event, result): + global message, invokedCount + invokedCount += 1 + print(f'{message}: Observer invoked ({invokedCount}) for {op.typeName()} {str(event)}') + return 0 + + +class TestOperationObserver(smtk.testing.TestCase): + + def setUp(self): + self.ctxt = smtk.applicationContext() + self.resource_manager = smtk.resource.Manager.create() + self.operation_manager = smtk.operation.Manager.create() + self.operation_manager.registerResourceManager(self.resource_manager) + self.ctxt.insertOrAssign(self.resource_manager) + self.ctxt.insertOrAssign(self.operation_manager) + + smtk.resource.Registrar.registerTo(self.ctxt) + # smtk.resource.Registrar.registerTo(self.resource_manager) + + smtk.operation.Registrar.registerTo(self.ctxt) + smtk.operation.Registrar.registerTo(self.operation_manager) + + # smtk.attribute.Registrar.registerTo(self.ctxt) + smtk.attribute.Registrar.registerTo(self.resource_manager) + smtk.attribute.Registrar.registerTo(self.operation_manager) + + smtk.session.polygon.Registrar.registerTo(self.operation_manager) + smtk.session.polygon.Registrar.registerTo(self.resource_manager) + + self.key = self.operation_manager.observers().insert(observer, 'Test observer') + + def testFreeObserver(self): + import os + global message, invokedCount + + invokedCount = 0 + fpath = [smtk.testing.DATA_DIR, 'model', + '2d', 'smtk', 'epic-trex-drummer.smtk'] + op = self.operation_manager.createOperation( + 'smtk::session::polygon::Read') + print('op is', op) + op.parameters().find('filename').setValue(os.path.join(*fpath)) + print(os.path.join(*fpath)) + invokedCount = 0 + message = 'testFreeObserver' + res = op.operate() + if res.find('outcome').value(0) != int(smtk.operation.Operation.SUCCEEDED): + raise RuntimeError + self.assertEqual( + invokedCount, 2, 'Expected to be called exactly twice.') + + def methodObserver(self, op, event, result): + self.invokedCount += 1 + print( + f'{self.message}: Observer invoked ({self.invokedCount}) for {op.typeName()} {str(event)}') + if event == smtk.operation.EventType.WILL_OPERATE: + # Abort the operation + return 1 + return 0 + + def testMethodObserver(self): + import os + self.key = self.operation_manager.observers().insert( + self.methodObserver, 'Test method observer') + fpath = [smtk.testing.DATA_DIR, 'model', + '2d', 'smtk', 'epic-trex-drummer.smtk'] + op = self.operation_manager.createOperation( + 'smtk::session::polygon::Read') + print('op is', op) + op.parameters().find('filename').setValue(os.path.join(*fpath)) + print(os.path.join(*fpath)) + self.invokedCount = 0 + self.message = 'testFreeObserver' + res = op.operate() + self.assertEqual(smtk.operation.outcome(res), smtk.operation.Operation.CANCELED, + 'Expected to be canceled.') + self.assertEqual(self.invokedCount, 2, + 'Expected to be called exactly once.') + + +if __name__ == '__main__': + smtk.testing.process_arguments() + smtk.testing.main() diff --git a/smtk/resource/Registrar.cxx b/smtk/resource/Registrar.cxx index e6caaf38af0e37b850e7c5c6849fb960a04e48fb..be3c543409878d31b74e8cbe008f4b1d69a18d5a 100644 --- a/smtk/resource/Registrar.cxx +++ b/smtk/resource/Registrar.cxx @@ -16,18 +16,26 @@ #include "smtk/plugin/Manager.h" +namespace +{ +bool didCreateResourceManager = false; +} + namespace smtk { namespace resource { void Registrar::registerTo(const smtk::common::Managers::Ptr& managers) { - managers->insert(smtk::resource::Manager::create()); + if (!managers->contains()) + { + managers->insert(smtk::resource::Manager::create()); + didCreateResourceManager = true; + } auto resourceManager = managers->get(); smtk::plugin::Manager::instance()->registerPluginsTo(resourceManager); - managers->insert( - smtk::resource::query::Manager::create(managers->get())); + managers->insert(smtk::resource::query::Manager::create(resourceManager)); smtk::plugin::Manager::instance()->registerPluginsTo( managers->get()); @@ -35,11 +43,18 @@ void Registrar::registerTo(const smtk::common::Managers::Ptr& managers) typeLabels[smtk::common::typeName()] = "component"; typeLabels[smtk::common::typeName()] = "resource"; typeLabels[smtk::common::typeName()] = "object"; + smtk::string::Token dummy1("smtk::resource::Resource"); + smtk::string::Token dummy2("smtk::resource::Component"); + smtk::string::Token dummy3("smtk::resource::PersistentObject"); } void Registrar::unregisterFrom(const smtk::common::Managers::Ptr& managers) { - managers->erase(); + if (didCreateResourceManager) + { + managers->erase(); + } } + } // namespace resource } // namespace smtk diff --git a/smtk/resource/pybind11/PybindPersistentObject.h b/smtk/resource/pybind11/PybindPersistentObject.h index 0738bc43befeb54459c6362ee5475675feb043d6..e7ce52ff110a1238f01dbdae563e84d78b07d557 100644 --- a/smtk/resource/pybind11/PybindPersistentObject.h +++ b/smtk/resource/pybind11/PybindPersistentObject.h @@ -16,6 +16,10 @@ #include "smtk/resource/PersistentObject.h" #include "smtk/common/pybind11/PybindUUIDTypeCaster.h" +#include "smtk/view/HandleManager.h" + +#include +#include namespace py = pybind11; @@ -23,6 +27,22 @@ inline PySharedPtrClass< smtk::resource::PersistentObject > pybind11_init_smtk_r { PySharedPtrClass< smtk::resource::PersistentObject > instance(m, "PersistentObject"); instance + .def("__enter__", [](smtk::resource::PersistentObject& self) + { + return self.shared_from_this(); + }, "Enter the runtime context related to this object." + ) + .def("__exit__", [](smtk::resource::PersistentObject& self, + const std::optional& exc_type, + const std::optional& exc_value, + const std::optional& traceback) + { + (void)self; + (void)exc_type; + (void)exc_value; + (void)traceback; + }, "Exit the runtime context related to this object." + ) .def("deepcopy", (smtk::resource::PersistentObject & (smtk::resource::PersistentObject::*)(::smtk::resource::PersistentObject const &)) &smtk::resource::PersistentObject::operator=) .def("typeName", &smtk::resource::PersistentObject::typeName) .def("id", &smtk::resource::PersistentObject::id) @@ -34,6 +54,19 @@ inline PySharedPtrClass< smtk::resource::PersistentObject > pybind11_init_smtk_r .def("classHierarchy", &smtk::resource::PersistentObject::classHierarchy) .def("matchesType", &smtk::resource::PersistentObject::matchesType, py::arg("candidate")) .def("generationsFromBase", &smtk::resource::PersistentObject::generationsFromBase, py::arg("base")) + .def("pointer", [](smtk::resource::PersistentObject& self) + { + return smtk::view::HandleManager::instance()->handle(&self); + }) + .def_static("fromPointer", [](const std::string& ptrStr) + { + auto* obj = smtk::view::HandleManager::instance()->fromHandle(ptrStr); + if (!obj) + { + return smtk::resource::PersistentObject::Ptr(); + } + return obj->shared_from_this(); + }) ; return instance; } diff --git a/smtk/session/oscillator/Registrar.cxx b/smtk/session/oscillator/Registrar.cxx index e5a3f8934c8136bf5e3ec0d5c4e79766159c0dd7..c28e76e523ea90b454a615726f5e9a7371057ad9 100644 --- a/smtk/session/oscillator/Registrar.cxx +++ b/smtk/session/oscillator/Registrar.cxx @@ -40,6 +40,7 @@ typedef std::tuple Ope void Registrar::registerTo(const smtk::resource::Manager::Ptr& resourceManager) { + smtk::string::Token dummy("smtk::session::oscillator::Resource"); // Providing a write method to the resource manager is what allows // modelbuilder's pqSMTKSaveResourceBehavior to determine how to write // the resource when users click "Save Resource" or ⌘ S/⌃ S. diff --git a/smtk/session/polygon/Registrar.cxx b/smtk/session/polygon/Registrar.cxx index fc2cf92c5b84c013a3e51587b3d20e8ed90b8622..489d2bfee1654bbb96697a5ba482719bb2cb5d4c 100644 --- a/smtk/session/polygon/Registrar.cxx +++ b/smtk/session/polygon/Registrar.cxx @@ -77,6 +77,7 @@ typedef std::tuple< void Registrar::registerTo(const smtk::resource::Manager::Ptr& resourceManager) { + smtk::string::Token dummy("smtk::session::polygon::Resource"); resourceManager->registerResource(read, write); } diff --git a/smtk/session/vtk/Geometry.cxx b/smtk/session/vtk/Geometry.cxx index 7af37760cf43c41eb2d9ef89f2088c752e570cb0..2f890d1410a876c49a2c94fc063567ce8bcfeef9 100644 --- a/smtk/session/vtk/Geometry.cxx +++ b/smtk/session/vtk/Geometry.cxx @@ -33,6 +33,7 @@ #include "vtkPolyData.h" #include "vtkSmartPointer.h" #include "vtkUnstructuredGrid.h" +#include "vtkVersionMacros.h" namespace smtk { @@ -99,10 +100,12 @@ void Geometry::queryGeometry(const smtk::resource::PersistentObject::Ptr& obj, C switch (data->GetDataObjectType()) { case VTK_COMPOSITE_DATA_SET: - case VTK_MULTIGROUP_DATA_SET: case VTK_MULTIBLOCK_DATA_SET: +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) + case VTK_MULTIGROUP_DATA_SET: case VTK_HIERARCHICAL_DATA_SET: case VTK_HIERARCHICAL_BOX_DATA_SET: +#endif // The VTK session doesn't support composite data yet: entry.m_geometry = nullptr; entry.m_generation = Invalid; diff --git a/smtk/session/vtk/Registrar.cxx b/smtk/session/vtk/Registrar.cxx index 4740da475ca4d9fb70403e974ed390009db0dae5..a84cc0cedf6fcdb99263c605c2f7ab62e8a146fd 100644 --- a/smtk/session/vtk/Registrar.cxx +++ b/smtk/session/vtk/Registrar.cxx @@ -40,6 +40,7 @@ typedef std::tuple OperationList; void Registrar::registerTo(const smtk::resource::Manager::Ptr& resourceManager) { + smtk::string::Token dummy("smtk::session::vtk::Resource"); resourceManager->registerResource(read, write); RegisterVTKBackend::registerClass(); } diff --git a/smtk/session/vtk/operators/Export.cxx b/smtk/session/vtk/operators/Export.cxx index de129824c0b39e14fddeab8eff0b078ecc9cdb75..4a4f26a6c24ffc967804821d852f77c14b55fd92 100644 --- a/smtk/session/vtk/operators/Export.cxx +++ b/smtk/session/vtk/operators/Export.cxx @@ -45,7 +45,11 @@ #include "vtkXMLImageDataWriter.h" #include "vtkVector.h" +#include "vtkVersionMacros.h" + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 5, 0) #include "vtkVectorOperators.h" +#endif SMTK_THIRDPARTY_PRE_INCLUDE #include "boost/filesystem.hpp" diff --git a/smtk/view/CMakeLists.txt b/smtk/view/CMakeLists.txt index 433fcb85694b6d9de92638c8eda7cbea12499407..5115398d3a947ea1eab74ed38a8cf50bd7ea9a41 100644 --- a/smtk/view/CMakeLists.txt +++ b/smtk/view/CMakeLists.txt @@ -9,6 +9,7 @@ set(viewClasses Configuration DescriptivePhrase EmptySubphraseGenerator + HandleManager Information LockedResourceBadge Manager @@ -56,8 +57,12 @@ set(viewHeaders ) foreach(class ${viewClasses}) - list(APPEND viewHeaders ${class}.h) - list(APPEND viewSrcs ${class}.cxx) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${class}.h") + list(APPEND viewHeaders "${class}.h") + endif() + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${class}.cxx") + list(APPEND viewSrcs "${class}.cxx") + endif() endforeach() if (SMTK_ENABLE_PYTHON_WRAPPING) diff --git a/smtk/view/Configuration.h b/smtk/view/Configuration.h index fa9b94370a587e3a978326fd66bfc9231893642a..aac6c155d65226572fcea5db4bd16e427ccdf89f 100644 --- a/smtk/view/Configuration.h +++ b/smtk/view/Configuration.h @@ -17,6 +17,7 @@ #include "smtk/CoreExports.h" #include "smtk/PublicPointerDefs.h" +#include "smtk/SharedFromThis.h" #include #include @@ -25,9 +26,11 @@ namespace smtk namespace view { /// @brief Configure a view, specifying types and attributes, without specifying a UI library. -class SMTKCORE_EXPORT Configuration +class SMTKCORE_EXPORT Configuration : smtkEnableSharedPtr(Configuration) { public: + smtkTypeMacroBase(smtk::view::Configuration); + /// @brief Configure one item in a view, which may contain children. class SMTKCORE_EXPORT Component { diff --git a/smtk/view/HandleManager.cxx b/smtk/view/HandleManager.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d720a31a8c6bd628e7724ae1bdb217a6980d426f --- /dev/null +++ b/smtk/view/HandleManager.cxx @@ -0,0 +1,216 @@ +//========================================================================= +// 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/view/HandleManager.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/ComponentItem.h" +#include "smtk/attribute/ResourceItem.h" +#include "smtk/common/Singletons.h" + +#include + +namespace smtk +{ +namespace view +{ + +HandleManager::HandleManager() = default; + +HandleManager::Ptr HandleManager::instance() +{ + auto& singletons(smtk::common::singletons()); + if (!singletons.contains()) + { + singletons.insertOrAssign(std::make_shared()); + } + return singletons.get(); +} + +void HandleManager::registerOperationManager(const smtk::operation::Manager::Ptr& opMgr) +{ + if (m_operationManager == opMgr) + { + return; + } + + if (m_operationManager) + { + m_operationManager->observers().erase(m_watcher); + } + + m_operationManager = opMgr; + + if (m_operationManager) + { + m_watcher = m_operationManager->observers().insert( + [this]( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result) { + this->handleOperation(op, event, result); + return 0; + }, + std::numeric_limits::max(), + false, + "Maintain object handles."); + } +} + +bool HandleManager::typeResolves( + smtk::string::Token requestedType, + smtk::string::Token immediateType) +{ + if (requestedType == immediateType) + { + return true; + } + return this->typeResolvesRecursive(requestedType, immediateType); +} + +bool HandleManager::typeResolvesRecursive( + smtk::string::Token requestedType, + smtk::string::Token immediateType) +{ + // std::cout << " Resolve " << immediateType.data() << " to " << requestedType.data() << "\n"; + auto it = m_typeMap.find(immediateType); + if (it == m_typeMap.end()) + { + // std::cout << " No entry for " << immediateType.data() << "\n"; + return false; + } + if (it->second == requestedType) + { + // std::cout << " Matched " << immediateType.data() << "\n"; + return true; + } + if (it->first == it->second) + { + // std::cout << " No match and " << it->first.data() << " is the base-most class\n"; + return false; + } + return this->typeResolvesRecursive(requestedType, it->second); +} + +void HandleManager::handleOperation( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result) +{ + (void)op; + if (event == smtk::operation::EventType::WILL_OPERATE) + { + return; + } + HandlesByType handleGroup; + for (auto obj : *result->findComponent("expunged")) + { + if (auto att = std::dynamic_pointer_cast(obj)) + { + // Invalidate any items that exist in the manager as well. + std::vector> items; + att->filterItems( + items, [](std::shared_ptr) { return true; }, /*only active*/ false); + for (const auto& item : items) + { + auto it = m_objects.find(this->handleString(item.get())); + if (it == m_objects.end()) + { + continue; + } + handleGroup[it->second.m_type].insert(it->second.m_handle); + } + } + auto it = m_objects.find(this->handleString(obj.get())); + if (it == m_objects.end()) + { + continue; + } + handleGroup[it->second.m_type].insert(it->second.m_handle); + m_objects.erase(it); + } + for (auto obj : *result->findResource("resourcesToExpunge")) + { + auto it = m_objects.find(this->handleString(obj.get())); + if (it == m_objects.end()) + { + continue; + } + handleGroup[it->second.m_type].insert(it->second.m_handle); + m_objects.erase(it); + } + if (!handleGroup.empty()) + { + m_observers(handleGroup, EventType::Expunged); + handleGroup.clear(); + } + + // TODO: We could accept some filters and notify observers when objects + // of a particular type are created. It might make maintaining + // views easier since some views need to respond to new attributes + // being created (for example). + for (auto obj : *result->findComponent("created")) + { + // TODO: Check that handle should be created by manager. + handleGroup[obj->typeToken()].insert(this->handle(obj.get())); + } + for (auto obj : *result->findResource("resourcesCreated")) + { + // TODO: Check that handle should be created by manager. + handleGroup[obj->typeToken()].insert(this->handle(obj.get())); + } + if (!handleGroup.empty()) + { + m_observers(handleGroup, EventType::Created); + handleGroup.clear(); + } + + for (auto obj : *result->findComponent("modified")) + { + if (auto att = std::dynamic_pointer_cast(obj)) + { + // Invalidate any items that exist in the manager as well. + std::vector> items; + att->filterItems( + items, [](std::shared_ptr) { return true; }, /*only active*/ false); + for (const auto& item : items) + { + auto it = m_objects.find(this->handleString(item.get())); + if (it == m_objects.end()) + { + continue; + } + handleGroup[it->second.m_type].insert(it->second.m_handle); + } + } + auto it = m_objects.find(this->handleString(obj.get())); + if (it == m_objects.end()) + { + continue; + } + handleGroup[it->second.m_type].insert(it->second.m_handle); + } + for (auto obj : *result->findResource("resourcesModified")) + { + auto it = m_objects.find(this->handleString(obj.get())); + if (it == m_objects.end()) + { + continue; + } + handleGroup[it->second.m_type].insert(it->second.m_handle); + } + if (!handleGroup.empty()) + { + m_observers(handleGroup, EventType::Modified); + handleGroup.clear(); + } +} + +} // namespace view +} // namespace smtk diff --git a/smtk/view/HandleManager.h b/smtk/view/HandleManager.h new file mode 100644 index 0000000000000000000000000000000000000000..b62bf50c66eddc79890a02f384ae809a28d799fe --- /dev/null +++ b/smtk/view/HandleManager.h @@ -0,0 +1,256 @@ +//========================================================================= +// 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_view_HandleManager_h +#define smtk_view_HandleManager_h + +#include "smtk/CoreExports.h" +#include "smtk/PublicPointerDefs.h" +#include "smtk/SharedFromThis.h" + +#include "smtk/attribute/Attribute.h" +#include "smtk/attribute/Item.h" +#include "smtk/operation/Manager.h" +#include "smtk/operation/Observer.h" +#include "smtk/resource/Lock.h" +#include "smtk/resource/Resource.h" +#include "smtk/view/Configuration.h" + +#include + +namespace smtk +{ +namespace view +{ + +/**\brief A utility for views manage dependencies on objects of various types. + * + * This class, which behaves as a singleton, manages "handles" (unique strings) + * for objects which may or may not be persistent. Instances of any class + * that inherits `std::enable_shared_from_this<>` and uses the SMTK type macros + * (so that typeToken() and related methods are defined) may be assigned handles. + * + * The intent is for handles to be used as references during the lifetime of + * a process (usually the server process, while the client would be a browser + * which may be remote) in order to refer to items that may not be present on + * the client. Handles are not unique across multiple processes. + * Handle strings are not preserved on disk; they live only for the duration of + * the process. + * + * Remote views of data may then query the process where the objects reside to + * obtain information on the objects or to modify the objects. + */ +class SMTKCORE_EXPORT HandleManager : smtkEnableSharedPtr(HandleManager) +{ +public: + smtkTypeMacroBase(HandleManager); + smtkCreateMacro(HandleManager); + + HandleManager(); + HandleManager(const HandleManager&) = delete; + HandleManager& operator=(const HandleManager&) = delete; + + static Ptr instance(); + + /// Do not manage the given \a object, but return the handle string for it. + template + std::string handleString(const T* object) const + { + if (!object) + { + return "0x0"; + } + std::ostringstream hstr; + hstr << std::hex << object << std::dec; + return hstr.str(); + } + + /// Return true if the \a object has a pre-existing handle and false otherwise. + template + bool hasHandle(const T* object) const + { + auto key = this->handleString(object); + return m_objects.find(key) != m_objects.end(); + } + + template + std::string handle(T* object) + { + if (!object) + { + return "0x0"; + } + auto immediateType = object->typeToken(); + auto itit = m_typeMap.find(immediateType); + if (itit == m_typeMap.end()) + { + auto inh = object->classHierarchy(); + for (std::size_t ii = 1; ii < inh.size(); ++ii) + { + // std::cerr << "** Mapping " << inh[ii - 1].data() << " to " << inh[ii].data() << "\n"; + m_typeMap[inh[ii - 1]] = inh[ii]; + } + // Force the base-most type to map to itself: + m_typeMap[*inh.rbegin()] = *inh.rbegin(); + // std::cerr << "** Mapping " << inh.rbegin()->data() << " to " << inh.rbegin()->data() << "\n"; + } + std::ostringstream hstr; + hstr << std::hex << object << std::dec; + auto obit = m_objects.find(hstr.str()); + if (obit != m_objects.end()) + { + return obit->second.m_handle; + } + obit = m_objects.emplace(hstr.str(), HandleEntry(immediateType, object)).first; + return obit->second.m_handle; + } + + template + T* fromHandle(const std::string& handle) + { + auto obit = m_objects.find(handle); + if (obit == m_objects.end()) + { + std::cerr << "No object for handle " << handle << "\n"; + return nullptr; + } + if ( + obit->second.m_resource && + obit->second.m_resource->locked() == smtk::resource::LockType::Write) + { + std::cerr << "Resource owning " << handle << " is locked for writing.\n"; + return nullptr; + } + smtk::string::Token typeToken(smtk::common::typeName()); + if (!this->typeResolves(typeToken, obit->second.m_type)) + { + std::cerr << "Object for handle " << handle << " is type " << obit->second.m_type.data() + << " not " << typeToken.data() << "\n"; + return nullptr; + } + return reinterpret_cast(obit->second.m_object); + } + + /// Event types for handles. + enum EventType + { + Created, // Objects with the given handles were created. + Expunged, // Objects with the given handles were expunged. + Modified // Objects with the given handles have been modified. + }; + + /// The manager aggregates changes to handles by the type of object involved. + using HandlesByType = std::unordered_map>; + + /// Observers are passed a collection of handles (strings) grouped by the type of object + /// at the handle (a string token), and the type of event. + /// + /// Each time an operation complete, the the observer may be called twice: once + /// for expunged handles and once for modified handles. + using Observer = std::function; + + /// The type of the object which holds a collection of observers to invoke. + using Observers = smtk::common::Observers; + + /// Return the collection of observers so observers can be added or removed. + /// + /// These observers will only be called from within an operation observer, + /// at which point the locks on relevant resources will be held. + Observers& observers() { return m_observers; } + const Observers& observers() const { return m_observers; } + + /// Tell this manager to monitor operations run for \a opMgr to scrub deleted objects. + void registerOperationManager(const smtk::operation::Manager::Ptr& opMgr); + +protected: + /// This method is invoked by the operation observer to remove expunged entries. + void handleOperation( + const smtk::operation::Operation& op, + smtk::operation::EventType event, + smtk::operation::Operation::Result result); + + /// Use m_typeMap to validate that a pointer is of the requested type (or a subclass). + bool typeResolves(smtk::string::Token requestedType, smtk::string::Token immediateType); + bool typeResolvesRecursive(smtk::string::Token requestedType, smtk::string::Token immediateType); + + /// Map object->typeName() values to their superclass type-name. + /// + /// Searching this map repeatedly until no match is found will return the + /// name of the object's base-most type (i.e., to a key of m_objects). + /// This exists for type validation before casting. + std::unordered_map m_typeMap; + + /// Data held for each handle. + struct HandleEntry + { + HandleEntry(smtk::string::Token immediateType, smtk::view::Configuration* object) + : m_type(immediateType) + , m_resource(nullptr) + , m_object(object) + { + std::ostringstream ptr; + ptr << std::hex << object << std::dec; + m_handle = ptr.str(); + } + + HandleEntry(smtk::string::Token immediateType, smtk::attribute::Item* object) + : m_type(immediateType) + , m_resource(object->attribute()->parentResource()) + , m_object(object) + { + std::ostringstream ptr; + ptr << std::hex << object << std::dec; + m_handle = ptr.str(); + } + + template + HandleEntry(smtk::string::Token immediateType, T* object) + : m_type(immediateType) + , m_resource(object->parentResource()) + , m_object(object) + { + std::ostringstream ptr; + ptr << std::hex << object << std::dec; + m_handle = ptr.str(); + } + + /// The exact type of the data. + smtk::string::Token m_type; + /// The resource owning the handle. + /// + /// This is used to check the lock state. If a handle is locked, requests + /// for its underlying data (fromHandle<>) will be denied. + smtk::resource::Resource* m_resource; + /// The handle of the data. + std::string m_handle; + /// A pointer to the object. + void* m_object; + }; + + /// Map an object to a tuple holding its type and its handle-string. + std::unordered_map m_objects; + + /// Functions to call when an object underlying a handle is modified or expunged. + Observers m_observers; + + /// The currently-registered operation manager to observe for updates. + smtk::operation::Manager::Ptr m_operationManager; + + /// The observer key for a method that monitors operations + /// in order to remove handles as they are expunged. + /// + /// This manager deals with components, resources, and + /// attribute-items. + smtk::operation::Observers::Key m_watcher; +}; + +} // namespace view +} // namespace smtk + +#endif diff --git a/smtk/view/Registrar.cxx b/smtk/view/Registrar.cxx index 4da261ecefeaf564caebb348ea79c4c922c21cb5..f0bc3365fb5d4a03e6ce8c90dfe68526c904bcdc 100644 --- a/smtk/view/Registrar.cxx +++ b/smtk/view/Registrar.cxx @@ -20,6 +20,7 @@ #include "smtk/view/ComponentPhraseModel.h" #include "smtk/view/DefaultOperationIcon.h" #include "smtk/view/EmptySubphraseGenerator.h" +#include "smtk/view/HandleManager.h" #include "smtk/view/LockedResourceBadge.h" #include "smtk/view/ObjectIconBadge.h" #include "smtk/view/PhraseModel.h" @@ -67,6 +68,47 @@ void Registrar::unregisterFrom(const smtk::common::Managers::Ptr& managers) managers->erase(); } +void Registrar::registerTo(const smtk::operation::Manager::Ptr& operationManager) +{ + // Add some string hashes to the token manager to avoid + // "Hash does not exist in database" errors. + (void)smtk::string::Token("smtk::resource::PersistentObject"); + (void)smtk::string::Token("smtk::resource::Component"); + (void)smtk::string::Token("smtk::resource::Resource"); + (void)smtk::string::Token("smtk::geometry::Resource"); + (void)smtk::string::Token("smtk::attribute::Resource"); + (void)smtk::string::Token("smtk::attribute::Attribute"); + (void)smtk::string::Token("smtk::attribute::Item"); + (void)smtk::string::Token("smtk::attribute::ValueItem"); + (void)smtk::string::Token("smtk::attribute::ValueItemTemplate"); + (void)smtk::string::Token("smtk::attribute::ValueItemTemplate"); + (void)smtk::string::Token("smtk::attribute::ValueItemTemplate"); + (void)smtk::string::Token("smtk::attribute::IntItem"); + (void)smtk::string::Token("smtk::attribute::StringItem"); + (void)smtk::string::Token("smtk::attribute::DoubleItem"); + (void)smtk::string::Token("smtk::attribute::VoidItem"); + (void)smtk::string::Token("smtk::attribute::ReferenceItem"); + (void)smtk::string::Token("smtk::attribute::ResourceItem"); + (void)smtk::string::Token("smtk::attribute::ComponentItem"); + (void)smtk::string::Token("smtk::attribute::FileSystemItem"); + (void)smtk::string::Token("smtk::attribute::FileItem"); + (void)smtk::string::Token("smtk::attribute::DirectoryItem"); + (void)smtk::string::Token("smtk::attribute::DateTimeItem"); + (void)smtk::string::Token("smtk::model::Resource"); + (void)smtk::string::Token("smtk::view::Configuration"); + (void)smtk::string::Token("smtk::view::View"); + + auto handleManager = smtk::view::HandleManager::instance(); + handleManager->registerOperationManager(operationManager); +} + +void Registrar::unregisterFrom(const smtk::operation::Manager::Ptr& operationManager) +{ + (void)operationManager; + auto handleManager = smtk::view::HandleManager::instance(); + handleManager->registerOperationManager(nullptr); +} + void Registrar::registerTo(const smtk::view::Manager::Ptr& viewManager) { viewManager->phraseModelFactory().registerTypes(); diff --git a/smtk/view/Registrar.h b/smtk/view/Registrar.h index 452ec3f25fc502737d0cbc7897582ef3ecd4ccfb..ccf21ce1a4de2bd200d778f1db8d1e9b4e41522d 100644 --- a/smtk/view/Registrar.h +++ b/smtk/view/Registrar.h @@ -13,6 +13,7 @@ #include "smtk/CoreExports.h" #include "smtk/common/Managers.h" +#include "smtk/operation/Manager.h" #include "smtk/view/Manager.h" namespace smtk @@ -25,6 +26,9 @@ public: static void registerTo(const smtk::common::Managers::Ptr&); static void unregisterFrom(const smtk::common::Managers::Ptr&); + static void registerTo(const smtk::operation::Manager::Ptr&); + static void unregisterFrom(const smtk::operation::Manager::Ptr&); + static void registerTo(const smtk::view::Manager::Ptr&); static void unregisterFrom(const smtk::view::Manager::Ptr&); }; diff --git a/smtk/view/plugin/CMakeLists.txt b/smtk/view/plugin/CMakeLists.txt index 75ba5bbad45ff9787c2cb41a5fff73d4f72ba208..a0d360fa42114e79a456096aa630ee09591c5302 100644 --- a/smtk/view/plugin/CMakeLists.txt +++ b/smtk/view/plugin/CMakeLists.txt @@ -1,6 +1,7 @@ smtk_add_plugin(smtkViewPlugin REGISTRAR smtk::view::Registrar MANAGERS smtk::common::Managers + smtk::operation::Manager smtk::view::Manager PARAVIEW_PLUGIN_ARGS VERSION 1.0) diff --git a/smtk/view/pybind11/PybindHandleManager.h b/smtk/view/pybind11/PybindHandleManager.h new file mode 100644 index 0000000000000000000000000000000000000000..78463fe0dc0f8cb634719241eb71286dcd3fd498 --- /dev/null +++ b/smtk/view/pybind11/PybindHandleManager.h @@ -0,0 +1,131 @@ +//========================================================================= +// 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_view_HandleManager_h +#define pybind_smtk_view_HandleManager_h + +#include +#include +#include + +#include "smtk/attribute/Item.h" +#include "smtk/resource/PersistentObject.h" +#include "smtk/view/Configuration.h" +#include "smtk/view/HandleManager.h" + +#include +#include + +namespace py = pybind11; + +inline py::class_ pybind11_init_smtk_view_HandleManager_Observers_Key(py::module& m) +{ + py::class_ instance(m, "HandleObserversKey"); + instance + .def("assigned", &smtk::view::HandleManager::Observers::Key::assigned) + .def("release", &smtk::view::HandleManager::Observers::Key::release) + ; + return instance; +} + +inline py::class_ pybind11_init_smtk_view_HandleManager_Observers(py::module& m) +{ + py::class_ instance(m, "HandleObservers"); + instance + .def("insert", []( + smtk::view::HandleManager::Observers& observers, + std::function< + void( + const smtk::view::HandleManager::HandlesByType&, + smtk::view::HandleManager::EventType + ) + > observer, + smtk::view::HandleManager::Observers::Priority priority, + const std::string& description) + { + return observers.insert(observer, priority, false, description); + }, py::arg("observer"), py::arg("priority"), py::arg("description") + ) + .def("erase", []( + smtk::view::HandleManager::Observers& observers, + smtk::view::HandleManager::Observers::Key& key) + { + observers.erase(key); + }, + py::arg("key") + ) + ; + return instance; +} + +inline void pybind11_init_smtk_view_HandleManager_EventType(py::module &m) +{ + py::enum_(m, "HandleManagerEventType") + .value("Created", smtk::view::HandleManager::EventType::Created) + .value("Modified", smtk::view::HandleManager::EventType::Modified) + .value("Expunged", smtk::view::HandleManager::EventType::Expunged) + .export_values(); +} + +inline PySharedPtrClass pybind11_init_smtk_view_HandleManager(py::module &m) +{ + PySharedPtrClass< smtk::view::HandleManager > instance(m, "HandleManager"); // , py::module_local()); + pybind11_init_smtk_view_HandleManager_EventType(m); + instance + .def_static("instance", &smtk::view::HandleManager::instance) + .def("handle", [](smtk::view::HandleManager& handleManager, smtk::attribute::Item* item) + { + return handleManager.handle(item); + }, py::arg("item")) + .def("handle", [](smtk::view::HandleManager& handleManager, smtk::view::Configuration* view) + { + return handleManager.handle(view); + }, py::arg("view")) + .def("handle", [](smtk::view::HandleManager& handleManager, smtk::resource::PersistentObject* object) + { + return handleManager.handle(object); + }, py::arg("object")) + .def("item", [](smtk::view::HandleManager& handleManager, const std::string& ptrStr) + { + auto* item = handleManager.fromHandle(ptrStr); + if (!item) + { + return smtk::attribute::Item::Ptr(); + } + return item->shared_from_this(); + }) + .def("view", [](smtk::view::HandleManager& handleManager, const std::string& ptrStr) + { + auto* view = handleManager.fromHandle(ptrStr); + if (!view) + { + return smtk::view::Configuration::Ptr(); + } + return view->shared_from_this(); + }) + .def("object", [](smtk::view::HandleManager& handleManager, const std::string& ptrStr) + { + auto* object = handleManager.fromHandle(ptrStr); + if (!object) + { + return smtk::resource::PersistentObject::Ptr(); + } + return object->shared_from_this(); + }) + .def("observers", [](smtk::view::HandleManager& handleManager) + { + return &handleManager.observers(); + }, py::return_value_policy::reference_internal + ) + ; + return instance; +} + +#endif diff --git a/smtk/view/pybind11/PybindView.cxx b/smtk/view/pybind11/PybindView.cxx index 509c372f45b9a0e61a392c40f0a329403d4ce966..47ff6c0a2abcda3d4aa4879c6bcd6cb532f5f449 100644 --- a/smtk/view/pybind11/PybindView.cxx +++ b/smtk/view/pybind11/PybindView.cxx @@ -22,6 +22,7 @@ template using PySharedPtrClass = py::class_, Args...>; #include "PybindDescriptivePhrase.h" +#include "PybindHandleManager.h" #include "PybindSubphraseGenerator.h" #include "PybindSelection.h" #include "PybindSelectionObserver.h" @@ -50,4 +51,8 @@ PYBIND11_MODULE(_smtkPybindView, view) PySharedPtrClass< smtk::view::Configuration > smtk_view_View = pybind11_init_smtk_view_View(view); py::class_< smtk::view::SelectionObservers > smtk_view_SelectionObserver = pybind11_init_smtk_view_SelectionObservers(view); py::class_< smtk::view::Selection > smtk_view_Selection = pybind11_init_smtk_view_Selection(view); + PySharedPtrClass smtk_view_HandleManager = pybind11_init_smtk_view_HandleManager(view); + py::class_ smtk_view_HandleManager_Observers = pybind11_init_smtk_view_HandleManager_Observers(view); + py::class_ smtk_view_HandleManager_Observers_Key = pybind11_init_smtk_view_HandleManager_Observers_Key(view); + } diff --git a/smtk/view/pybind11/PybindView.h b/smtk/view/pybind11/PybindView.h index e4132bdcd4d545ccec9e4cb21b0b0eeb237d1daa..10082293708f1fb37093e1785a3e953912df4a08 100644 --- a/smtk/view/pybind11/PybindView.h +++ b/smtk/view/pybind11/PybindView.h @@ -14,6 +14,10 @@ #include #include "smtk/view/Configuration.h" +#include "smtk/view/HandleManager.h" + +#include +#include namespace py = pybind11; @@ -30,6 +34,19 @@ inline PySharedPtrClass< smtk::view::Configuration > pybind11_init_smtk_view_Vie .def("iconName", &smtk::view::Configuration::iconName) .def("setIconName", &smtk::view::Configuration::setIconName, py::arg("name")) .def("details", (smtk::view::Configuration::Component& (smtk::view::Configuration::*)()) &smtk::view::Configuration::details, py::return_value_policy::reference) + .def("pointer", [](smtk::view::Configuration& self) + { + return smtk::view::HandleManager::instance()->handle(&self); + }) + .def_static("fromPointer", [](const std::string& ptrStr) + { + auto* view = smtk::view::HandleManager::instance()->fromHandle(ptrStr); + if (!view) + { + return smtk::view::Configuration::Ptr(); + } + return view->shared_from_this(); + }) ; py::class_< smtk::view::Configuration::Component >(instance, "Component") .def(py::init<::std::string const &>())