From fb95e3247d69f69aee5e19dffe1ca39d2cd717a1 Mon Sep 17 00:00:00 2001 From: ickby Date: Tue, 2 Oct 2018 08:21:56 +0200 Subject: [PATCH] Expose dynamic property signals to python observer --- src/App/DocumentObserverPython.cpp | 83 ++++++++++++++++++++++++++++++ src/App/DocumentObserverPython.h | 9 ++++ src/Mod/Test/Document.py | 39 ++++++++++++++ 3 files changed, 131 insertions(+) diff --git a/src/App/DocumentObserverPython.cpp b/src/App/DocumentObserverPython.cpp index 27cddbebc5..2161961532 100644 --- a/src/App/DocumentObserverPython.cpp +++ b/src/App/DocumentObserverPython.cpp @@ -96,6 +96,13 @@ DocumentObserverPython::DocumentObserverPython(const Py::Object& obj) : inst(obj (&DocumentObserverPython::slotCommitTransaction, this, _1)); this->connectDocumentAbortTransaction = App::GetApplication().signalAbortTransaction.connect(boost::bind (&DocumentObserverPython::slotAbortTransaction, this, _1)); + + this->connectObjectAppendDynamicProperty = App::GetApplication().signalAppendDynamicProperty.connect(boost::bind + (&DocumentObserverPython::slotAppendDynamicProperty, this, _1)); + this->connectObjectRemoveDynamicProperty = App::GetApplication().signalRemoveDynamicProperty.connect(boost::bind + (&DocumentObserverPython::slotRemoveDynamicProperty, this, _1)); + this->connectObjectChangePropertyEditor = App::GetApplication().signalChangePropertyEditor.connect(boost::bind + (&DocumentObserverPython::slotChangePropertyEditor, this, _1)); } DocumentObserverPython::~DocumentObserverPython() @@ -118,6 +125,10 @@ DocumentObserverPython::~DocumentObserverPython() this->connectDocumentOpenTransaction.disconnect(); this->connectDocumentCommitTransaction.disconnect(); this->connectDocumentAbortTransaction.disconnect(); + + this->connectObjectAppendDynamicProperty.disconnect(); + this->connectObjectRemoveDynamicProperty.disconnect(); + this->connectObjectChangePropertyEditor.disconnect(); } void DocumentObserverPython::slotCreatedDocument(const App::Document& Doc) @@ -435,3 +446,75 @@ void DocumentObserverPython::slotAbortTransaction(const App::Document& doc) e.ReportException(); } } + +void DocumentObserverPython::slotAppendDynamicProperty(const App::Property& Prop) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("slotAppendDynamicProperty"))) { + auto container = Prop.getContainer(); + Py::Callable method(this->inst.getAttr(std::string("slotAppendDynamicProperty"))); + Py::Tuple args(2); + args.setItem(0, Py::Object(static_cast(container)->getPyObject(), true)); + // If a property is touched but not part of a document object then its name is null. + // In this case the slot function must not be called. + const char* prop_name = container->getPropertyName(&Prop); + if (prop_name) { + args.setItem(1, Py::String(prop_name)); + method.apply(args); + } + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + +void DocumentObserverPython::slotRemoveDynamicProperty(const App::Property& Prop) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("slotRemoveDynamicProperty"))) { + auto container = Prop.getContainer(); + Py::Callable method(this->inst.getAttr(std::string("slotRemoveDynamicProperty"))); + Py::Tuple args(2); + args.setItem(0, Py::Object(static_cast(container)->getPyObject(), true)); + // If a property is touched but not part of a document object then its name is null. + // In this case the slot function must not be called. + const char* prop_name = container->getPropertyName(&Prop); + if (prop_name) { + args.setItem(1, Py::String(prop_name)); + method.apply(args); + } + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} + +void DocumentObserverPython::slotChangePropertyEditor(const App::Property& Prop) +{ + Base::PyGILStateLocker lock; + try { + if (this->inst.hasAttr(std::string("slotChangePropertyEditor"))) { + auto container = Prop.getContainer(); + Py::Callable method(this->inst.getAttr(std::string("slotChangePropertyEditor"))); + Py::Tuple args(2); + args.setItem(0, Py::Object(static_cast(container)->getPyObject(), true)); + // If a property is touched but not part of a document object then its name is null. + // In this case the slot function must not be called. + const char* prop_name = container->getPropertyName(&Prop); + if (prop_name) { + args.setItem(1, Py::String(prop_name)); + method.apply(args); + } + } + } + catch (Py::Exception&) { + Base::PyException e; // extract the Python error text + e.ReportException(); + } +} diff --git a/src/App/DocumentObserverPython.h b/src/App/DocumentObserverPython.h index 455f3a7625..85e796755e 100644 --- a/src/App/DocumentObserverPython.h +++ b/src/App/DocumentObserverPython.h @@ -85,6 +85,12 @@ private: void slotCommitTransaction(const App::Document& Doc); /** Called when a observed document aborts a transaction */ void slotAbortTransaction(const App::Document& Doc); + /** Called when a object get a new dynamic property added*/ + void slotAppendDynamicProperty(const App::Property& Prop); + /** Called when a object get a dynamic property removed*/ + void slotRemoveDynamicProperty(const App::Property& Prop); + /** Called when a object property get a new editor relevant status like hidden or read only*/ + void slotChangePropertyEditor(const App::Property& Prop); private: Py::Object inst; @@ -108,6 +114,9 @@ private: Connection connectDocumentOpenTransaction; Connection connectDocumentCommitTransaction; Connection connectDocumentAbortTransaction; + Connection connectObjectAppendDynamicProperty; + Connection connectObjectRemoveDynamicProperty; + Connection connectObjectChangePropertyEditor; }; } //namespace App diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index 9fd5e6f88c..79873a59c5 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -1392,6 +1392,21 @@ class DocumentObserverCases(unittest.TestCase): self.signal.append('ObjRecomputed'); self.parameter.append(obj) + def slotAppendDynamicProperty(self, obj, prop): + self.signal.append('ObjAddDynProp'); + self.parameter.append(obj) + self.parameter2.append(prop) + + def slotRemoveDynamicProperty(self, obj, prop): + self.signal.append('ObjRemoveDynProp'); + self.parameter.append(obj) + self.parameter2.append(prop) + + def slotChangePropertyEditor(self, obj, prop): + self.signal.append('ObjChangePropEdit'); + self.parameter.append(obj) + self.parameter2.append(prop) + def setUp(self): self.Obs = self.Observer(); @@ -1529,8 +1544,32 @@ class DocumentObserverCases(unittest.TestCase): self.failUnless(self.Obs.parameter.pop() is obj) self.failUnless(not self.Obs.signal and not self.Obs.parameter and not self.Obs.parameter2) + pyobj = self.Doc1.addObject("App::FeaturePython","pyobj") + self.Obs.signal = [] + self.Obs.parameter = [] + self.Obs.parameter2 = [] + pyobj.addProperty("App::PropertyLength","Prop","Group","test property") + self.failUnless(self.Obs.signal.pop() == 'ObjAddDynProp') + self.failUnless(self.Obs.parameter.pop() is pyobj) + self.failUnless(self.Obs.parameter2.pop() == 'Prop') + self.failUnless(not self.Obs.signal and not self.Obs.parameter and not self.Obs.parameter2) + + pyobj.setEditorMode('Prop', ['ReadOnly']) + self.failUnless(self.Obs.signal.pop() == 'ObjChangePropEdit') + self.failUnless(self.Obs.parameter.pop() is pyobj) + self.failUnless(self.Obs.parameter2.pop() == 'Prop') + self.failUnless(not self.Obs.signal and not self.Obs.parameter and not self.Obs.parameter2) + + pyobj.removeProperty('Prop') + self.failUnless(self.Obs.signal.pop() == 'ObjRemoveDynProp') + self.failUnless(self.Obs.parameter.pop() is pyobj) + self.failUnless(self.Obs.parameter2.pop() == 'Prop') + self.failUnless(not self.Obs.signal and not self.Obs.parameter and not self.Obs.parameter2) + FreeCAD.closeDocument('Observer1') self.Obs.signal = [] + self.Obs.parameter = [] + self.Obs.parameter2 = [] def testUndoDisabledDocument(self):