2222#include < pybind11/cast.h>
2323#include < sofa/core/visual/VisualParams.h>
2424#include < SofaPython3/Sofa/Core/Binding_Base.h>
25- #include < SofaPython3/Sofa/Core/Binding_Controller .h>
26- #include < SofaPython3/Sofa/Core/Binding_Controller_doc .h>
25+ #include < SofaPython3/Sofa/Core/Binding_Component .h>
26+ #include < SofaPython3/Sofa/Core/Binding_Component_doc .h>
2727
2828#include < SofaPython3/PythonFactory.h>
2929#include < SofaPython3/PythonEnvironment.h>
@@ -38,20 +38,20 @@ namespace sofapython3
3838using sofa::core::objectmodel::Event;
3939using sofa::core::objectmodel::BaseComponent;
4040
41- Controller_Trampoline::Controller_Trampoline () = default ;
41+ Component_Trampoline::Component_Trampoline () = default ;
4242
43- Controller_Trampoline ::~Controller_Trampoline ()
43+ Component_Trampoline ::~Component_Trampoline ()
4444{
4545 // Clean up Python objects while holding the GIL
4646 if (m_cacheInitialized)
4747 {
48- PythonEnvironment::gil acquire {" ~Controller_Trampoline " };
48+ PythonEnvironment::gil acquire {" ~Component_Trampoline " };
4949 m_methodCache.clear ();
5050 m_pySelf = py::object ();
5151 }
5252}
5353
54- void Controller_Trampoline ::initializePythonCache ()
54+ void Component_Trampoline ::initializePythonCache ()
5555{
5656 if (m_cacheInitialized)
5757 return ;
@@ -65,7 +65,7 @@ void Controller_Trampoline::initializePythonCache()
6565 m_cacheInitialized = true ;
6666}
6767
68- py::object Controller_Trampoline ::getCachedMethod (const std::string& methodName)
68+ py::object Component_Trampoline ::getCachedMethod (const std::string& methodName)
6969{
7070 // Must be called with GIL held and cache initialized
7171
@@ -92,7 +92,7 @@ py::object Controller_Trampoline::getCachedMethod(const std::string& methodName)
9292 return method;
9393}
9494
95- bool Controller_Trampoline ::callCachedMethod (const py::object& method, Event* event)
95+ bool Component_Trampoline ::callCachedMethod (const py::object& method, Event* event)
9696{
9797 // Must be called with GIL held
9898 if (f_printLog.getValue ())
@@ -108,7 +108,7 @@ bool Controller_Trampoline::callCachedMethod(const py::object& method, Event* ev
108108 return py::cast<bool >(result);
109109}
110110
111- void Controller_Trampoline ::invalidateMethodCache (const std::string& methodName)
111+ void Component_Trampoline ::invalidateMethodCache (const std::string& methodName)
112112{
113113 if (!m_cacheInitialized)
114114 return ;
@@ -117,7 +117,7 @@ void Controller_Trampoline::invalidateMethodCache(const std::string& methodName)
117117 m_methodCache.erase (methodName);
118118}
119119
120- std::string Controller_Trampoline ::getClassName () const
120+ std::string Component_Trampoline ::getClassName () const
121121{
122122 PythonEnvironment::gil acquire {" getClassName" };
123123
@@ -130,32 +130,32 @@ std::string Controller_Trampoline::getClassName() const
130130 return py::str (py::type::of (py::cast (this )).attr (" __name__" ));
131131}
132132
133- void Controller_Trampoline ::draw (const sofa::core::visual::VisualParams* params)
133+ void Component_Trampoline ::draw (const sofa::core::visual::VisualParams* params)
134134{
135135 PythonEnvironment::executePython (this , [this , params](){
136- PYBIND11_OVERLOAD (void , Controller , draw, params);
136+ PYBIND11_OVERLOAD (void , Component , draw, params);
137137 });
138138}
139139
140- void Controller_Trampoline ::init ()
140+ void Component_Trampoline ::init ()
141141{
142142 PythonEnvironment::executePython (this , [this ](){
143143 // Initialize the Python object cache on first init
144144 initializePythonCache ();
145- PYBIND11_OVERLOAD (void , Controller , init, );
145+ PYBIND11_OVERLOAD (void , Component , init, );
146146 });
147147}
148148
149- void Controller_Trampoline ::reinit ()
149+ void Component_Trampoline ::reinit ()
150150{
151151 PythonEnvironment::executePython (this , [this ](){
152- PYBIND11_OVERLOAD (void , Controller , reinit, );
152+ PYBIND11_OVERLOAD (void , Component , reinit, );
153153 });
154154}
155155
156156// / If a method named "methodName" exists in the python controller,
157157// / methodName is called, with the Event's dict as argument
158- bool Controller_Trampoline ::callScriptMethod (
158+ bool Component_Trampoline ::callScriptMethod (
159159 const py::object& self, Event* event, const std::string & methodName)
160160{
161161 if (f_printLog.getValue ())
@@ -177,7 +177,7 @@ bool Controller_Trampoline::callScriptMethod(
177177 return false ;
178178}
179179
180- void Controller_Trampoline ::handleEvent (Event* event)
180+ void Component_Trampoline ::handleEvent (Event* event)
181181{
182182 PythonEnvironment::executePython (this , [this , event](){
183183 // Ensure cache is initialized (in case init() wasn't called or
@@ -204,59 +204,87 @@ void Controller_Trampoline::handleEvent(Event* event)
204204 });
205205}
206206
207- void moduleAddController (py::module &m) {
208- py::class_<Controller,
209- Controller_Trampoline,
207+
208+
209+ sofa::core::sptr<Component_Trampoline> Component_Trampoline::_init_ (pybind11::args& /* args*/ , pybind11::kwargs& kwargs)
210+ {
211+ auto c = sofa::core::sptr<Component_Trampoline> (new Component_Trampoline ());
212+ c->f_listening .setValue (true );
213+
214+ for (auto kv : kwargs)
215+ {
216+ std::string key = py::cast<std::string>(kv.first );
217+ py::object value = py::reinterpret_borrow<py::object>(kv.second );
218+
219+ if ( key == " name" )
220+ c->setName (py::cast<std::string>(kv.second ));
221+ try {
222+ BindingBase::SetAttr (*c, key, value);
223+ } catch (py::attribute_error& /* e*/ ) {
224+ // / kwargs are used to set datafields to their initial values,
225+ // / but they can also be used as simple python attributes, unrelated to SOFA.
226+ // / thus we catch & ignore the py::attribute_error thrown by SetAttr
227+ }
228+ }
229+ return c;
230+ }
231+
232+ void Component_Trampoline::_setattr_ (pybind11::object self, const std::string& s, pybind11::object value)
233+ {
234+ // If the attribute starts with "on" and the new value is callable, invalidate the cached method
235+ if (s.rfind (" on" , 0 ) == 0 && PyCallable_Check (value.ptr ()))
236+ {
237+ auto * trampoline = dynamic_cast <Component_Trampoline*>(py::cast<Component*>(self));
238+ if (trampoline)
239+ {
240+ trampoline->invalidateMethodCache (s);
241+ }
242+ }
243+
244+ // Delegate to the base class __setattr__
245+ BindingBase::__setattr__ (self, s, value);
246+ }
247+
248+
249+
250+ void moduleAddComponent (py::module &m) {
251+ py::class_<Component,
252+ Component_Trampoline,
210253 BaseComponent,
211- py_shared_ptr<Controller >> f (m, " Controller " ,
254+ py_shared_ptr<Component >> f (m, " Component " ,
212255 py::dynamic_attr (),
213256 sofapython3::doc::controller::controllerClass);
214257
215- f.def (py::init ([](py::args& /* args*/ , py::kwargs& kwargs)
216- {
217- auto c = sofa::core::sptr<Controller_Trampoline> (new Controller_Trampoline ());
218- c->f_listening .setValue (true );
219-
220- for (auto kv : kwargs)
221- {
222- std::string key = py::cast<std::string>(kv.first );
223- py::object value = py::reinterpret_borrow<py::object>(kv.second );
224-
225- if ( key == " name" )
226- c->setName (py::cast<std::string>(kv.second ));
227- try {
228- BindingBase::SetAttr (*c, key, value);
229- } catch (py::attribute_error& /* e*/ ) {
230- // / kwargs are used to set datafields to their initial values,
231- // / but they can also be used as simple python attributes, unrelated to SOFA.
232- // / thus we catch & ignore the py::attribute_error thrown by SetAttr
233- }
234- }
235- return c;
236- }));
237-
238- f.def (" init" , &Controller::init);
239- f.def (" reinit" , &Controller::reinit);
240- f.def (" draw" , [](Controller& self, sofa::core::visual::VisualParams* params){
258+ f.def (py::init (&Component_Trampoline::_init_));
259+ f.def (" __setattr__" ,&Component_Trampoline::_setattr_);
260+
261+ f.def (" init" , &Component::init);
262+ f.def (" reinit" , &Component::reinit);
263+ f.def (" draw" , [](Component& self, sofa::core::visual::VisualParams* params){
241264 self.draw (params);
242265 }, pybind11::return_value_policy::reference);
243266
244- // Override __setattr__ to invalidate the method cache when an "on*" attribute is reassigned
245- f.def (" __setattr__" , [](py::object self, const std::string& s, py::object value) {
246- // If the attribute starts with "on" and the new value is callable, invalidate the cached method
247- if (s.rfind (" on" , 0 ) == 0 && PyCallable_Check (value.ptr ()))
248- {
249- auto * trampoline = dynamic_cast <Controller_Trampoline*>(py::cast<Controller*>(self));
250- if (trampoline)
251- {
252- trampoline->invalidateMethodCache (s);
253- }
254- }
267+ }
268+
269+ void moduleAddController (py::module &m) {
270+ py::class_<Component,
271+ Component_Trampoline,
272+ BaseComponent,
273+ py_shared_ptr<Component>> f (m, " Controller" ,
274+ py::dynamic_attr (),
275+ sofapython3::doc::controller::controllerClass);
276+
277+ f.def (py::init (&Component_Trampoline::_init_));
278+ f.def (" __setattr__" ,&Component_Trampoline::_setattr_);
279+
280+ f.def (" init" , &Component::init);
281+ f.def (" reinit" , &Component::reinit);
282+ f.def (" draw" , [](Component& self, sofa::core::visual::VisualParams* params){
283+ self.draw (params);
284+ }, pybind11::return_value_policy::reference);
255285
256- // Delegate to the base class __setattr__
257- BindingBase::__setattr__ (self, s, value);
258- });
259286}
260287
261288
289+
262290}
0 commit comments