diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node.cpp index 5ea0cd51..2660171a 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node.cpp @@ -220,7 +220,7 @@ py::object addKwargs(Node* self, const py::object& callable, const py::kwargs& k { BaseObject* obj = py::cast(callable); - self->addObject(obj); + self->addObject(obj); return py::cast(obj); } @@ -326,24 +326,41 @@ py::object removeChildByName(Node& n, const std::string name) NodeIterator* property_children(Node* node) { - return new NodeIterator(node, [](Node* n) -> size_t { return n->child.size(); }, - [](Node* n, unsigned int index) -> Base::SPtr { return n->child[index]; }); + return new NodeIterator(node, + [](Node* n) -> size_t { return n->child.size(); }, + [](Node* n, unsigned int index) -> Base::SPtr { return n->child[index]; }, + [](const Node* n, const std::string& name) { return n->getChild(name); }, + [](Node* n, unsigned int index) { n->removeChild(n->child[index]); } + ); } NodeIterator* property_parents(Node* node) { - return new NodeIterator(node, [](Node* n) -> size_t { return n->getNbParents(); }, - [](Node* n, unsigned int index) -> Node::SPtr { - auto p = n->getParents(); - return static_cast(p[index]); -}); + return new NodeIterator(node, + [](Node* n) -> size_t { return n->getNbParents(); }, + [](Node* n, unsigned int index) -> Node::SPtr { + auto p = n->getParents(); + return static_cast(p[index]); + }, + [](const Node* n, const std::string& name) -> sofa::core::Base* { + const auto& parents = n->getParents(); + return *std::find_if(parents.begin(), + parents.end(), + [name](BaseNode* child){ return child->getName() == name; }); + }, + [](Node*, unsigned int) { + throw std::runtime_error("Removing a parent is not a supported operation. Please detach the node from the corresponding graph node."); + }); } NodeIterator* property_objects(Node* node) { - return new NodeIterator(node, [](Node* n) -> size_t { return n->object.size(); }, - [](Node* n, unsigned int index) -> Base::SPtr { return (n->object[index]); -}); + return new NodeIterator(node, + [](Node* n) -> size_t { return n->object.size(); }, + [](Node* n, unsigned int index) -> Base::SPtr { return (n->object[index]);}, + [](const Node* n, const std::string& name) { return n->getObject(name); }, + [](Node* n, unsigned int index) { n->removeObject(n->object[index]);} + ); } py::object __getattr__(Node& self, const std::string& name) diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.cpp index 7cb25343..c10da1c1 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.cpp @@ -43,7 +43,7 @@ void moduleAddNodeIterator(py::module &m) d.def("__getitem__", [](NodeIterator& d, const std::string& name) -> py::object { - BaseObject* obj =d.owner->getObject(name); + sofa::core::objectmodel::Base* obj = d.get_by_name(d.owner.get(), name); if(obj==nullptr) throw py::index_error("No existing object '"+name+"'"); return PythonFactory::toPython(obj); @@ -71,10 +71,11 @@ void moduleAddNodeIterator(py::module &m) }); d.def("remove_at", [](NodeIterator& d, size_t index) { - sofa::core::sptr n( - dynamic_cast(d.get(d.owner.get(), index).get()) - ); - d.owner->removeChild(n); + return d.remove_at(d.owner.get(), index); + }); + d.def("__contains__", [](NodeIterator& d, const std::string& name) + { + return d.get_by_name(d.owner.get(), name) != nullptr; }); } diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.h index 07775e4d..9ff8c268 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.h +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_NodeIterator.h @@ -26,19 +26,42 @@ namespace sofapython3 { -class NodeIterator { +/// Generic implementation to iterator over the different structures of a Node. +/// This allows to implement pythonic solution as in: +/// for object in node.objects: +/// print(object.name) +/// +/// or +/// for child in node.children: +/// print(child.name) +/// +/// there is also support for several function like: +/// len(node.children) +/// if "MyName" in node.children +/// node.children.remove_at(index) +/// +/// The implementation is generic by the use of lamda function allowing to specify which +/// data structure is actually iterated. +class NodeIterator +{ public: sofa::core::sptr owner; size_t index=0; std::function size ; std::function (sofa::simulation::Node *, size_t)> get ; + std::function get_by_name ; + std::function remove_at; NodeIterator(sofa::core::sptr owner_, std::function size_, - std::function (sofa::simulation::Node *, size_t)> get_) + std::function (sofa::simulation::Node *, size_t)> get_, + std::function get_by_name_, + std::function remove_at_) { size = size_; get = get_; + get_by_name = get_by_name_; + remove_at = remove_at_; owner=owner_; index=0; } diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node_doc.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node_doc.h index 094ffa0f..4c540c1f 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node_doc.h +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_Node_doc.h @@ -245,29 +245,52 @@ static auto getLinkPath = static auto children = R"( Field interface to acces the children of a node. + The returned object is a iteratable featuring the following operations: + len, remove_at, __contains__, get_at :Example: >>> n = Sofa.Core.Node("MyNode") + >>> n.addChild("child1") >>> for child in n.children: >>> print(child.name) + >>> + >>> if "child1" in n.children: + >>> print("Yes") + >>> print(len(n.children)) )"; static auto parents = R"( Field interface to acces the parents of a node. + The returned object is a iteratable featuring the following operations: + len, remove_at, __contains__, get_at + :Example: - >>> n = Sofa.Core.Node("MyNode") - >>> for parent in n.parents: + >>> n = Sofa.Core.Node("parent1") + >>> c = n.addChild("child1") + >>> for parent in c.parents: >>> print(parent.name) + >>> + >>> if "parent1" in c.parents: + >>> print("Yes") + >>> print(len(n.parents)) )"; static auto objects = R"( Field interface to acces the objects of a node. + The returned object is a iteratable featuring the following operations: + len, remove_at, __contains__, get_at :Example: >>> n = Sofa.Core.Node("MyNode") + >>> n.addObject("MechanicalObject", name="object1") + >>> n.addObject("MechanicalObject", name="object2") >>> for object in n.objects: >>> print(object.name) + >>> + >>> if "object2" in c.objects: + >>> print("Yes") + >>> print(len(n.objects)) )"; static auto removeObject = R"( diff --git a/bindings/Sofa/tests/Simulation/Node.py b/bindings/Sofa/tests/Simulation/Node.py index cb9f0df7..7f21c14a 100644 --- a/bindings/Sofa/tests/Simulation/Node.py +++ b/bindings/Sofa/tests/Simulation/Node.py @@ -124,6 +124,30 @@ def test_objects_property(self): child.addObject("MechanicalObject", name="name2") self.assertEqual(len(child.objects), 3) + def test_objects_property_contains_method(self): + root = Sofa.Core.Node("rootNode") + root.addObject("RequiredPlugin", name="SofaBaseMechanics") + child = root.addChild(Sofa.Core.Node("child1")) + child.addObject("MechanicalObject", name="name1") + child.addObject("MechanicalObject", name="name2") + self.assertTrue("child1" in root.children) + self.assertFalse("child2" in root.children) + self.assertTrue("name1" in child.objects) + self.assertTrue("name2" in child.objects) + self.assertFalse("name3" in child.objects) + + def test_objects_property_remove_at_method(self): + root = Sofa.Core.Node("rootNode") + root.addObject("RequiredPlugin", name="SofaBaseMechanics") + child = root.addChild(Sofa.Core.Node("child1")) + child.addObject("MechanicalObject", name="name1") + child.addObject("MechanicalObject", name="name2") + self.assertTrue("name1" in child.objects) + self.assertTrue("name2" in child.objects) + child.objects.remove_at(1) + self.assertTrue("name1" in child.objects) + self.assertFalse("name2" in child.objects) + def test_data_property(self): root = Sofa.Core.Node("rootNode") self.assertTrue(hasattr(root, "__data__"))