diff --git a/doc/release/notes/component-opacity.rst b/doc/release/notes/component-opacity.rst new file mode 100644 index 0000000000000000000000000000000000000000..39ab3a6d721edeb44acc9814ebf1738d760caa3e --- /dev/null +++ b/doc/release/notes/component-opacity.rst @@ -0,0 +1,12 @@ +ParaView Extensions +=================== + +Per-component opacity +--------------------- + +Previously – while it was possible to change the opacity of a component by running +an operation that changed the color property associated with the component – it was +not possible for user-interface elements to modify the opacity of individual +components. Methods for this have now been added to SMTK's ParaView representation +and the task-control view now properly handles user adjustments to component opacity +through these methods for quick but impermanent changes to component visualizations. diff --git a/doc/release/notes/worklet-style-collision.rst b/doc/release/notes/worklet-style-collision.rst new file mode 100644 index 0000000000000000000000000000000000000000..0b90511c085c0c77f4c6313933275477ba0e7df5 --- /dev/null +++ b/doc/release/notes/worklet-style-collision.rst @@ -0,0 +1,13 @@ +Task Subsystem +============== + +Worklet style collisions +------------------------ + +When a worklet is emplaced, it is deserialized as if it were a task manager unto itself. +However, this can cause an issue when the project's task manager already contains +definitions for style tags with the same name (for example, if two worklets in the gallery +define a "show-geometry" style with different values). Previously, each worklet emplaced +would overwrite the old style definition with its own (breaking already-emplaced tasks). +Now, if name collisions between style tags already present in the task manager are detected +and the style definitions are not identical, a new style tag is created and used. diff --git a/smtk/common/StringUtil.cxx b/smtk/common/StringUtil.cxx index 995ffa9dd70d1d13e893c090142adc65bcaf4aaf..abd6b88526133e2f3d96afd45b176d7fa0ea5d6d 100644 --- a/smtk/common/StringUtil.cxx +++ b/smtk/common/StringUtil.cxx @@ -225,5 +225,33 @@ bool StringUtil::mixedAlphanumericComparator(const std::string& aa, const std::s double nb = atof(bb.substr(i).c_str()); return na < nb; } + +bool StringUtil::endsWithUnderscoreNumber(std::string& key, int& ii) +{ + std::size_t pos = key.find_last_of('_'); + if (pos == std::string::npos) + { + return false; + } + std::string num = key.substr(pos + 1); + std::size_t ep; + try + { + ii = std::stoi(num, &ep, 10); + if (ep != num.size()) + { + ii = 0; + return false; + } + key = key.substr(0, pos + 1); + } + catch (std::exception& ee) + { + ii = 0; + return false; + } + return true; +} + } // namespace common } // namespace smtk diff --git a/smtk/common/StringUtil.h b/smtk/common/StringUtil.h index 3ac0c2e2617dbd3c964c18d95c1b6b355882a898..9ddd5709c191b6f2f9eb0fb27b5f15e43a01b1f5 100644 --- a/smtk/common/StringUtil.h +++ b/smtk/common/StringUtil.h @@ -12,6 +12,7 @@ #include "smtk/CoreExports.h" +#include #include #include @@ -81,6 +82,52 @@ public: * \sa DescriptivePhrase::compareByTitle */ static bool mixedAlphanumericComparator(const std::string& aa, const std::string& bb); + + /**\brief Detect whether the \a key ends with an underscore followed by an integer number. + * + * If so, this returns true with \a key reduced to the prefix and \a ii set to the + * numeric value following the underscore. If not, this returns false and \a ii will + * be set to 0 while \a key will be unchanged. + */ + static bool endsWithUnderscoreNumber(std::string& key, int& ii); + + /**\brief Return a value of \a src that is unique within the context of \a preExisting. + * + * The returned value will not be any key of the \a preExisting dictionary (whether + * \a Dict is a set or map). No value is inserted into \a preExisting; if you choose + * to use the returned value, you are responsible for updating the dictionary. + */ + template + static std::string uniqify(const std::string& src, Dict& preExisting) + { + std::string key = src; + { + int ii; + key = src.data(); + std::string prefix; + if (StringUtil::endsWithUnderscoreNumber(key, ii)) + { + prefix = key; + } + else + { + prefix = key + "_"; + ii = 0; + } + for (++ii; true; ++ii) + { + std::ostringstream ks; + ks << prefix << ii; + key = ks.str(); + if (preExisting.find(key) == preExisting.end()) + { + break; + } + } + } + return key; + } + }; } // namespace common diff --git a/smtk/common/testing/cxx/CMakeLists.txt b/smtk/common/testing/cxx/CMakeLists.txt index 98a565978879f8679507ab7d28d55616a75df602..ab2b03aac74c46e8bb3b2c3fffe8c2bdb5c81d3a 100644 --- a/smtk/common/testing/cxx/CMakeLists.txt +++ b/smtk/common/testing/cxx/CMakeLists.txt @@ -48,6 +48,7 @@ set(unit_tests UnitTestTypeHierarchy.cxx UnitTestTypeMap.cxx UnitTestTypeName.cxx + UnitTestUniqify.cxx UnitTestUpdateFactory.cxx UnitTestVersionNumber.cxx UnitTestVisit.cxx diff --git a/smtk/common/testing/cxx/UnitTestUniqify.cxx b/smtk/common/testing/cxx/UnitTestUniqify.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1ec6ee78e2cd437fc40f867e2461c0107b5b51a1 --- /dev/null +++ b/smtk/common/testing/cxx/UnitTestUniqify.cxx @@ -0,0 +1,67 @@ +#include "smtk/common/StringUtil.h" + +#include +#include +#include +#include +#include +#include + +int UnitTestUniqify(int, char**) +{ + bool ok = true; + std::vector fails{ "foo", "foo_a", "foo_0a", "foo_lksfj_a0", "_", "_25699456738475773459358234577", "foo_" }; + std::vector passes{ "foo_0", "foo_1", "foo_42", "bar_86", "_37" }; + + int ii; + for (const auto& fail : fails) + { + ii = 0; + std::string key = fail; + if (smtk::common::StringUtil::endsWithUnderscoreNumber(key, ii)) + { + std::cerr << "ERROR: \"" << fail << "\" should not have passed. (" << key << ", " << ii << ")\n"; + ok = false; + } + else + { + if (key != fail || ii != 0) + { + std::cerr << "ERROR: \"" << fail << "\" modified inputs (" << key << ", " << ii << ")\n"; + } + else + { + std::cout << "Expected failure \"" << fail << "\" (" << key << ", " << ii << ")\n"; + } + } + } + + for (const auto& pass : passes) + { + ii = 0; + std::string key = pass; + if (!smtk::common::StringUtil::endsWithUnderscoreNumber(key, ii)) + { + std::cerr << "ERROR: \"" << pass << "\" should have passed. (" << key << ", " << ii << ")\n"; + ok = false; + } + else + { + std::cout << "\"" << key << "\" with " << ii << "\n"; + } + } + + std::map dict{ {"foo_0", "foo_2"}, {"foo_1", "foo_2"}, {"foo_37", "foo_38"}, {"bar_86", "bar_88"}, {"bar_87", "bar_87"}, {"_0", "_1"} }; + for (const auto& pass : passes) + { + std::string uu = smtk::common::StringUtil::uniqify(pass, dict); + std::cout << "\"" << pass << "\" → \"" << uu << "\"\n"; + if (dict.find(pass) != dict.end() && dict[pass] != uu) + { + std::cerr << "ERROR: \"" << pass << "\" was \"" << uu << "\", expected \"" << dict[pass] << "\".\n"; + ok = false; + } + } + + return ok ? 0 : 1; +} diff --git a/smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.cxx b/smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.cxx index b84a05179c6dc374507d92786e4d2ac68f7dd5e2..0e27394a679f510edf7e3ede6f1fae098a8a595f 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.cxx +++ b/smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.cxx @@ -114,6 +114,30 @@ bool pqSMTKResourceRepresentation::setVisibility(smtk::resource::ComponentPtr co return false; } +bool pqSMTKResourceRepresentation::setOpacity(smtk::resource::ComponentPtr comp, double opacity) +{ + return this->setOpacity(comp.get(), opacity); +} + +bool pqSMTKResourceRepresentation::setOpacity(const smtk::resource::PersistentObject* obj, double opacity) +{ + auto* pxy = this->getProxy(); + auto* mpr = pxy->GetClientSideObject(); // TODO: Remove the need for me. + auto* cmp = vtkCompositeRepresentation::SafeDownCast(mpr); + auto* spx = + cmp ? vtkSMTKResourceRepresentation::SafeDownCast(cmp->GetActiveRepresentation()) : nullptr; + if (spx) + { + if (spx->SetEntityOpacity(obj, opacity)) + { + // TODO: Emit a signal when opacity changes? + // Q_EMIT componentVisibilityChanged(obj, visible); + return true; + } + } + return false; +} + void pqSMTKResourceRepresentation::allVisibilities( std::map& visibilities) const { diff --git a/smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.h b/smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.h index cac904bb31be9c91fe1bf8e68a79ce407f9fdffe..7318d0705f3179fd3b60ad3d9a62a4e61cc3a7f5 100644 --- a/smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.h +++ b/smtk/extension/paraview/appcomponents/pqSMTKResourceRepresentation.h @@ -42,6 +42,10 @@ public: /// Populate the \a visibilities map with per-component visibility information. void allVisibilities(std::map& visibilities) const; + /// Change the opacity of the specified component. Returns true if changed, false otherwise. + bool setOpacity(smtk::resource::ComponentPtr comp, double opacity); + bool setOpacity(const smtk::resource::PersistentObject* comp, double opacity); + Q_SIGNALS: /// Emitted from within setVisibility(). void componentVisibilityChanged(smtk::resource::ComponentPtr comp, bool visible); diff --git a/smtk/extension/paraview/project/pqTaskControlView.cxx b/smtk/extension/paraview/project/pqTaskControlView.cxx index bc04bc467b9f4a49c6c5cc5b61eeac11690a139e..01a772a88ec0d760b880339690379859b5358e17 100644 --- a/smtk/extension/paraview/project/pqTaskControlView.cxx +++ b/smtk/extension/paraview/project/pqTaskControlView.cxx @@ -78,6 +78,22 @@ bool representationObjectMapContainsResource(const RepresentationObjectMap::valu return (it != entry.second.end() || entry.second.find(nullptr) != entry.second.end()); } +bool representationUpdateBlockOpacities( + const RepresentationObjectMap::value_type& entry, double opacity) +{ + bool didChange = false; + auto pvDRep = dynamic_cast(entry.first); + if (!pvDRep) + { + return didChange; + } + for (const auto& ptr : entry.second) + { + didChange |= pvDRep->setOpacity(ptr, opacity); + } + return didChange; +} + } // anonymous namespace /// Private storage for a task-control views. @@ -600,6 +616,7 @@ void pqTaskControlView::updateOpacity(const std::string& name, double opacity) entry.first->getProxy()->UpdateVTKObjects(); needRender = true; } + needRender |= representationUpdateBlockOpacities(entry, opacity); // TODO: Handle per-block opacity settings for components. // Iterate over entry.second, find component inside the // pqSMTKRepresentation and set per-block opacity. diff --git a/smtk/extension/paraview/server/vtkSMTKResourceRepresentation.cxx b/smtk/extension/paraview/server/vtkSMTKResourceRepresentation.cxx index 1a03a75f7118f6dd1a52b2cef02fd82ce3a2689b..e3e50140be81611c7450f673f684b14848151054 100644 --- a/smtk/extension/paraview/server/vtkSMTKResourceRepresentation.cxx +++ b/smtk/extension/paraview/server/vtkSMTKResourceRepresentation.cxx @@ -624,6 +624,32 @@ bool vtkSMTKResourceRepresentation::SetEntityVisibility( return didChange; } +bool vtkSMTKResourceRepresentation::SetEntityOpacity( + const smtk::resource::PersistentObject* ent, + double opacity) +{ + if (!ent || !ent->id()) + { + return false; + } + + auto rdit = this->RenderableData.find(ent->id()); + if (rdit == this->RenderableData.end()) + { // No change because the object is not renderable. + return false; + } + auto currentOpacity = this->EntityMapper->GetCompositeDataDisplayAttributes()->GetBlockOpacity(rdit->second); + if (currentOpacity == opacity) + { + return false; + } + this->EntityMapper->GetCompositeDataDisplayAttributes()->SetBlockOpacity(rdit->second, opacity); + this->GlyphMapper->GetBlockAttributes()->SetBlockOpacity(rdit->second, opacity); + this->EntityMapper->Modified(); + this->GlyphMapper->Modified(); + return true; +} + bool vtkSMTKResourceRepresentation::ApplyStyle( smtk::view::SelectionPtr seln, RenderableDataMap& renderables, diff --git a/smtk/extension/paraview/server/vtkSMTKResourceRepresentation.h b/smtk/extension/paraview/server/vtkSMTKResourceRepresentation.h index 3b3229df2cf5675475b24ade42b67e7f33a7bdee..12b57f230a17c107d9a0b5d79127b7fc1a42389e 100644 --- a/smtk/extension/paraview/server/vtkSMTKResourceRepresentation.h +++ b/smtk/extension/paraview/server/vtkSMTKResourceRepresentation.h @@ -290,6 +290,7 @@ public: void GetEntityVisibilities(std::map& visdata); bool SetEntityVisibility(smtk::resource::PersistentObjectPtr ent, bool visible); + bool SetEntityOpacity(const smtk::resource::PersistentObject* ent, double opacity); /**\brief Look for generator functions that alters a representation's appearance based on a selection. * diff --git a/smtk/extension/qt/qtGroupView.cxx b/smtk/extension/qt/qtGroupView.cxx index abb80133ca47a7727e58f349248af1acdebcf65c..5713770ca27021dd890a5d7c43b6d703c794ca9f 100644 --- a/smtk/extension/qt/qtGroupView.cxx +++ b/smtk/extension/qt/qtGroupView.cxx @@ -66,7 +66,7 @@ public: void qtGroupViewInternals::updateChildren(qtGroupView* gview, qtBaseViewMemFn mfunc) { // In the case of tiling we don't want to show the - // label for empty views + // label for empty views, nor empty labels. if (m_style == qtGroupViewInternals::TILED) { int i, size = m_ChildViews.size(); @@ -82,7 +82,14 @@ void qtGroupViewInternals::updateChildren(qtGroupView* gview, qtBaseViewMemFn mf else { child->widget()->show(); - m_Labels.at(i)->show(); + if (m_Labels.at(i)->text().isEmpty()) + { + m_Labels.at(i)->hide(); + } + else + { + m_Labels.at(i)->show(); + } } } } @@ -626,6 +633,10 @@ void qtGroupView::addTileEntry(qtBaseView* child) QObject::connect(child, &qtBaseView::modified, this, &qtGroupView::childModified); QLabel* label = new QLabel(child->configuration()->label().c_str(), this->Widget); m_internals->m_Labels.append(label); + if (label->text().isEmpty()) + { + label->hide(); + } QFont titleFont; titleFont.setBold(true); titleFont.setItalic(true); diff --git a/smtk/markup/pybind11/PybindDiscreteGeometry.h b/smtk/markup/pybind11/PybindDiscreteGeometry.h new file mode 100644 index 0000000000000000000000000000000000000000..10b5b30a52a2251f44ae7961a3908aa0d24744c9 --- /dev/null +++ b/smtk/markup/pybind11/PybindDiscreteGeometry.h @@ -0,0 +1,46 @@ +//========================================================================= +// 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_markup_DiscreteGeometry_h +#define pybind_smtk_markup_DiscreteGeometry_h + +#include + +#include "smtk/markup/DiscreteGeometry.h" + +#include "smtk/extension/vtk/pybind11/PybindVTKTypeCaster.h" + +#include "smtk/common/UUID.h" +#include "smtk/common/pybind11/PybindUUIDTypeCaster.h" + +namespace py = pybind11; + +inline PySharedPtrClass< smtk::markup::DiscreteGeometry> pybind11_init_smtk_markup_DiscreteGeometry(py::module &m) +{ + PySharedPtrClass< smtk::markup::DiscreteGeometry, smtk::markup::SpatialData> instance(m, "DiscreteGeometry"); + instance + .def_static("CastTo", [](const std::shared_ptr& obj) + { return std::dynamic_pointer_cast(obj); }) + ; + py::class_ shapeOpts(instance, "ShapeOptions"); + shapeOpts + .def(py::init<>()) + .def(py::init<::smtk::markup::DiscreteGeometry::ShapeOptions const &>()) + .def("trackedChanges", + [](smtk::markup::DiscreteGeometry::ShapeOptions& opts) + { return opts.trackedChanges; }) + .def("setTrackedChanges", + [](smtk::markup::DiscreteGeometry::ShapeOptions& opts, smtk::operation::Operation::Result res) + { opts.trackedChanges = res; }, py::arg("tracked_changes")) + ; + return instance; +} + +#endif diff --git a/smtk/markup/pybind11/PybindMarkup.cxx b/smtk/markup/pybind11/PybindMarkup.cxx index b64b91618cda9ab8b25ed77ed39fe6b3818b385b..2bdad79a27094b61de4c0a2c4473b9431e5a0382 100644 --- a/smtk/markup/pybind11/PybindMarkup.cxx +++ b/smtk/markup/pybind11/PybindMarkup.cxx @@ -25,6 +25,7 @@ using PySharedPtrClass = py::class_, Args...>; #include "PybindResource.h" #include "PybindComponent.h" #include "PybindSpatialData.h" +#include "PybindDiscreteGeometry.h" #include "PybindDomain.h" #include "PybindDomainMap.h" #include "PybindUnstructuredData.h" @@ -51,5 +52,6 @@ PYBIND11_MODULE(_smtkPybindMarkup, markup) auto smtk_markup_Domain = pybind11_init_smtk_markup_Domain(markup); auto smtk_markup_DomainMap = pybind11_init_smtk_markup_DomainMap(markup); auto smtk_markup_SpatialData = pybind11_init_smtk_markup_SpatialData(markup); + auto smtk_markup_DiscreteGeometry = pybind11_init_smtk_markup_DiscreteGeometry(markup); auto smtk_markup_UnstructuredData = pybind11_init_smtk_markup_UnstructuredData(markup); } diff --git a/smtk/markup/pybind11/PybindUnstructuredData.h b/smtk/markup/pybind11/PybindUnstructuredData.h index 0541d8de087392506a1d031811e5e4fff7fa6795..f48ae335585e6fe0a6721ee855d6b4928973977e 100644 --- a/smtk/markup/pybind11/PybindUnstructuredData.h +++ b/smtk/markup/pybind11/PybindUnstructuredData.h @@ -13,6 +13,7 @@ #include +#include "smtk/markup/AssignedIds.h" #include "smtk/markup/UnstructuredData.h" #include "smtk/extension/vtk/pybind11/PybindVTKTypeCaster.h" @@ -24,12 +25,29 @@ namespace py = pybind11; inline PySharedPtrClass< smtk::markup::UnstructuredData> pybind11_init_smtk_markup_UnstructuredData(py::module &m) { - PySharedPtrClass< smtk::markup::UnstructuredData, smtk::markup::SpatialData> instance(m, "UnstructuredData"); + PySharedPtrClass< smtk::markup::UnstructuredData, smtk::markup::DiscreteGeometry, smtk::markup::SpatialData> instance(m, "UnstructuredData"); instance .def("shape", &smtk::markup::UnstructuredData::shape) + .def("setShapeData", &smtk::markup::UnstructuredData::setShapeData) .def_static("CastTo", [](const std::shared_ptr& obj) { return std::dynamic_pointer_cast(obj); }) ; + py::class_ + shapeOpts(instance, "ShapeOptions"); + shapeOpts + .def(py::init<>()) + .def(py::init<::smtk::markup::UnstructuredData::ShapeOptions const &>()) + .def("sharedCellIds", [](const smtk::markup::UnstructuredData::ShapeOptions& opts) + { return opts.sharedCellIds; }) + .def("setSharedCellIds", []( + smtk::markup::UnstructuredData::ShapeOptions& opts, const std::shared_ptr& ids) + { opts.sharedCellIds = ids; }, py::arg("sharedCellIds")) + .def("sharedPointIds", [](const smtk::markup::UnstructuredData::ShapeOptions& opts) + { return opts.sharedPointIds; }) + .def("setSharedPointIds", []( + smtk::markup::UnstructuredData::ShapeOptions& opts, const std::shared_ptr& ids) + { opts.sharedPointIds = ids; }, py::arg("sharedPointIds")) + ; return instance; } diff --git a/smtk/string/pybind11/PybindToken.h b/smtk/string/pybind11/PybindToken.h index c973046491f407411cc5518a8dd9796fd23eab01..4d055da014afe05fdf01138e552d88bdf9bc86aa 100644 --- a/smtk/string/pybind11/PybindToken.h +++ b/smtk/string/pybind11/PybindToken.h @@ -50,6 +50,11 @@ inline py::class_ pybind11_init_smtk_string_Token(py::modul { return self == other; }) + .def("__eq__", [](const smtk::string::Token& self, const std::string& other) + { + auto otherToken = smtk::string::Token(other); + return self == otherToken; + }) .def("__lt__", [](const smtk::string::Token& self, const smtk::string::Token& other) { return self < other; diff --git a/smtk/task/Manager.cxx b/smtk/task/Manager.cxx index 8e68989f47540621ed795e48a3339f9f3994342f..c5927c0c60071a472301a81f2cde271ab54cc8f5 100644 --- a/smtk/task/Manager.cxx +++ b/smtk/task/Manager.cxx @@ -21,6 +21,8 @@ #include "smtk/resource/Resource.h" +#include "smtk/common/StringUtil.h" + #include "smtk/io/Logger.h" using namespace smtk::string::literals; @@ -269,6 +271,28 @@ nlohmann::json Manager::getStyle(const smtk::string::Token& styleClass) const return nlohmann::json(); } +void Manager::addStyles( + const nlohmann::json& styles, + std::unordered_map& styleMap) +{ + for (const auto& entry : styles.items()) + { + std::string key = entry.key(); + auto it = m_styles.find(key); + if (it != m_styles.end()) + { + if (*it != entry.value()) + { + key = smtk::common::StringUtil::uniqify(key, m_styles); + styleMap[entry.key()] = key; + smtkWarningMacro(smtk::io::Logger::instance(), + " Style \"" << entry.key() << "\" mapped to \"" << key << "\"."); + } + } + m_styles[key] = entry.value(); + } +} + smtk::resource::Resource* Manager::resource() const { return m_parent; diff --git a/smtk/task/Manager.h b/smtk/task/Manager.h index bff87969718be2ccede162e2e31ae07b0acb8c0b..93ad564dfed5150cc3fa56bf8f1d5113c779b47a 100644 --- a/smtk/task/Manager.h +++ b/smtk/task/Manager.h @@ -136,8 +136,19 @@ public: /// Given a style key, return a style config. nlohmann::json getStyle(const smtk::string::Token& styleClass) const; nlohmann::json getStyles() const { return m_styles; }; + + /// Overwrite this task-manager's styles with the provided \a styles. void setStyles(const nlohmann::json& styles) { m_styles = styles; } + /// Append \a styles into this task manager. + /// + /// In the event of a name collision (where the content for styles of the same + /// name is different), the name is made unique and an entry is added to the + /// \a styleMap from the original name to the uniquified name. + void addStyles( + const nlohmann::json& styles, + std::unordered_map& styleMap); + /// If this manager is owned by a resource (typically a project), return it. smtk::resource::Resource* resource() const; diff --git a/smtk/task/json/jsonManager.cxx b/smtk/task/json/jsonManager.cxx index 84261abcc9cb67f8f8dec0c8a9d9ff46fa5fee91..703d2eadd309e8225266b96de597425b2329dfd0 100644 --- a/smtk/task/json/jsonManager.cxx +++ b/smtk/task/json/jsonManager.cxx @@ -174,7 +174,27 @@ void from_json(const nlohmann::json& jj, Manager& taskManager) // Copy style specifications into the task manager. if (jj.contains("styles")) { - taskManager.setStyles(jj.at("styles")); + std::unordered_map styleMap; + taskManager.addStyles(jj.at("styles"), styleMap); + if (!styleMap.empty()) + { + // Update all the tasks we are deserializing with new style tags as needed. + if (jj.contains("tasks")) + { + for (const auto& jsonTask : jj.at("tasks")) + { + auto* task = helper.tasks().get(jsonTask); + if (!task) { continue; } + for (const auto& mapEntry : styleMap) + { + if (task->removeStyle(mapEntry.first)) + { + task->addStyle(mapEntry.second); + } + } + } + } + } } // Read in the worklet gallery if one exists. diff --git a/smtk/task/pybind11/PybindPortForwardingAgent.h b/smtk/task/pybind11/PybindPortForwardingAgent.h new file mode 100644 index 0000000000000000000000000000000000000000..30fc08d064923a1cc76133cb36409765bb8358f6 --- /dev/null +++ b/smtk/task/pybind11/PybindPortForwardingAgent.h @@ -0,0 +1,47 @@ +//========================================================================= +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//========================================================================= + +#ifndef pybind_smtk_task_PortForwardingAgent_h +#define pybind_smtk_task_PortForwardingAgent_h + +#include + +#include "smtk/task/PortForwardingAgent.h" + +#include "smtk/task/Port.h" + +namespace py = pybind11; + +inline py::class_< smtk::task::PortForwardingAgent > pybind11_init_smtk_task_PortForwardingAgent(py::module &m) +{ + py::class_< smtk::task::PortForwardingAgent, smtk::task::Agent > instance(m, "PortForwardingAgent"); + instance + .def("typeToken", &smtk::task::PortForwardingAgent::typeToken) + .def("classHierarchy", &smtk::task::PortForwardingAgent::classHierarchy) + .def("matchesType", &smtk::task::PortForwardingAgent::matchesType, py::arg("candidate")) + .def("generationsFromBase", &smtk::task::PortForwardingAgent::generationsFromBase, py::arg("base")) + .def("state", &smtk::task::PortForwardingAgent::state) + .def("configure", [](smtk::task::PortForwardingAgent& self, const std::string& jsonConfig) + { + auto config = nlohmann::json::parse(jsonConfig); + self.configure(config); + }) + .def("configuration", &smtk::task::PortForwardingAgent::configuration) + .def("name", &smtk::task::PortForwardingAgent::name) + .def("portData", [](smtk::task::PortForwardingAgent& self, smtk::task::Port::Ptr port) + { + return self.portData(port.get()); + }, py::arg("port")) + .def("parent", &smtk::task::PortForwardingAgent::parent, py::return_value_policy::reference_internal) + ; + return instance; +} + +#endif diff --git a/smtk/task/pybind11/PybindTask.cxx b/smtk/task/pybind11/PybindTask.cxx index 3e0944bf7590c88d91c3c2935e8f494668c5fe94..5e6a6de20fbf7a7544e9d9da632ef22340d5440d 100644 --- a/smtk/task/pybind11/PybindTask.cxx +++ b/smtk/task/pybind11/PybindTask.cxx @@ -9,6 +9,7 @@ //========================================================================= #include +#include #include #include "nlohmann/json.hpp" @@ -36,6 +37,7 @@ using namespace nlohmann; #include "PybindSubmitOperationAgent.h" #include "PybindTask.h" #include "PybindTrivialProducerAgent.h" +#include "PybindPortForwardingAgent.h" #include "PybindWorklet.h" #include "PybindInstances.h" @@ -60,6 +62,7 @@ PYBIND11_MODULE(_smtkPybindTask, m) auto smtk_task_SubmitOperationAgent = pybind11_init_smtk_task_SubmitOperationAgent(m); auto smtk_task_GatherObjectsAgent = pybind11_init_smtk_task_GatherObjectsAgent(m); auto smtk_task_TrivialProducerAgent = pybind11_init_smtk_task_TrivialProducerAgent(m); + auto smtk_task_PortForwardingAgent = pybind11_init_smtk_task_PortForwardingAgent(m); auto smtk_task_Task = pybind11_init_smtk_task_Task(m); pybind11_init_smtk_task_State(m); pybind11_init_smtk_task_stateEnum(m);