Implement and test extension events

This commit is contained in:
Stefan Tröger
2020-02-12 12:16:12 +01:00
committed by wwmayer
parent 9960b67399
commit 52c64d8a85
5 changed files with 95 additions and 2 deletions

View File

@@ -48,6 +48,7 @@ class DocumentObject;
class ApplicationObserver;
class Property;
class AutoTransaction;
class ExtensionContainer;
enum GetLinkOption {
/// Get all links (both directly and in directly) linked to the given object
@@ -261,6 +262,18 @@ public:
/// signal on about changing the editor mode of a property
boost::signals2::signal<void (const App::Document&, const App::Property&)> signalChangePropertyEditor;
//@}
/** @name Signals of extension changes
* These signals are emitted on dynamic extension addition. Dynamic extensions are the ones added by python (c++ ones are part
* of the class definition, hence not dynamic)
* The extension in question is provided as parameter.
*/
//@{
/// signal before adding the extension
boost::signals2::signal<void (const App::ExtensionContainer&, std::string extension)> signalBeforeAddingDynamicExtension;
/// signal after the extension was added
boost::signals2::signal<void (const App::ExtensionContainer&, std::string extension)> signalAddedDynamicExtension;
//@}
/** @name methods for parameter handling */

View File

@@ -111,6 +111,8 @@ DocumentObserverPython::DocumentObserverPython(const Py::Object& obj) : inst(obj
FC_PY_ELEMENT_ARG1(AppendDynamicProperty, AppendDynamicProperty)
FC_PY_ELEMENT_ARG1(RemoveDynamicProperty, RemoveDynamicProperty)
FC_PY_ELEMENT_ARG2(ChangePropertyEditor, ChangePropertyEditor)
FC_PY_ELEMENT_ARG2(BeforeAddingDynamicExtension, BeforeAddingDynamicExtension)
FC_PY_ELEMENT_ARG2(AddedDynamicExtension, AddedDynamicExtension)
}
DocumentObserverPython::~DocumentObserverPython()
@@ -541,3 +543,34 @@ void DocumentObserverPython::slotFinishSaveDocument(const App::Document& doc, co
e.ReportException();
}
}
void DocumentObserverPython::slotBeforeAddingDynamicExtension(const App::ExtensionContainer& extcont, std::string extension)
{
Base::PyGILStateLocker lock;
try {
Py::Tuple args(2);
args.setItem(0, Py::Object(const_cast<App::ExtensionContainer&>(extcont).getPyObject()));
args.setItem(1, Py::String(extension));
Base::pyCall(pyBeforeAddingDynamicExtension.ptr(),args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
e.ReportException();
}
}
void DocumentObserverPython::slotAddedDynamicExtension(const App::ExtensionContainer& extcont, std::string extension)
{
Base::PyGILStateLocker lock;
try {
Py::Tuple args(2);
args.setItem(0, Py::Object(const_cast<App::ExtensionContainer&>(extcont).getPyObject()));
args.setItem(1, Py::String(extension));
Base::pyCall(pyAddedDynamicExtension.ptr(),args.ptr());
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
e.ReportException();
}
}

View File

@@ -28,6 +28,8 @@
#include <boost/signals2.hpp>
#include <boost/bind.hpp>
#include <string>
namespace App
{
@@ -105,6 +107,11 @@ private:
void slotStartSaveDocument(const App::Document&, const std::string&);
/** Called when an document has been saved*/
void slotFinishSaveDocument(const App::Document&, const std::string&);
/** Called before an object gets a new extension added*/
void slotBeforeAddingDynamicExtension(const App::ExtensionContainer&, std::string extension);
/** Called when an object gets a dynamic extension added*/
void slotAddedDynamicExtension(const App::ExtensionContainer&, std::string extension);
private:
Py::Object inst;
@@ -145,6 +152,8 @@ private:
Connection pyAppendDynamicProperty;
Connection pyRemoveDynamicProperty;
Connection pyChangePropertyEditor;
Connection pyBeforeAddingDynamicExtension;
Connection pyAddedDynamicExtension;
};
} //namespace App

View File

@@ -207,7 +207,7 @@ PyObject* ExtensionContainerPy::addExtension(PyObject *args) {
str << "No extension found of type '" << typeId << "'" << std::ends;
throw Py::Exception(Base::BaseExceptionFreeCADError,str.str());
}
//register the extension
App::Extension* ext = static_cast<App::Extension*>(extension.createInstance());
//check if this really is a python extension!
@@ -217,7 +217,8 @@ PyObject* ExtensionContainerPy::addExtension(PyObject *args) {
str << "Extension is not a python addable version: '" << typeId << "'" << std::ends;
throw Py::Exception(Base::BaseExceptionFreeCADError,str.str());
}
GetApplication().signalBeforeAddingDynamicExtension(*getExtensionContainerPtr(), typeId);
ext->initExtension(getExtensionContainerPtr());
//set the proxy to allow python overrides
@@ -260,6 +261,9 @@ PyObject* ExtensionContainerPy::addExtension(PyObject *args) {
}
Py_DECREF(obj);
//throw the appropriate event
GetApplication().signalAddedDynamicExtension(*getExtensionContainerPtr(), typeId);
Py_Return;
}

View File

@@ -1504,6 +1504,16 @@ class DocumentObserverCases(unittest.TestCase):
self.signal.append('DocFinishSave')
self.parameter.append(obj)
self.parameter2.append(name)
def slotBeforeAddingDynamicExtension(self, obj, extension):
self.signal.append('ObjBeforeDynExt')
self.parameter.append(obj)
self.parameter2.append(extension)
def slotAddedDynamicExtension(self, obj, extension):
self.signal.append('ObjDynExt')
self.parameter.append(obj)
self.parameter2.append(extension)
class GuiObserver():
@@ -1777,6 +1787,18 @@ class DocumentObserverCases(unittest.TestCase):
self.failUnless(self.Obs.parameter2.pop() == 'Prop')
self.failUnless(not self.Obs.signal and not self.Obs.parameter and not self.Obs.parameter2)
pyobj.addExtension("App::GroupExtensionPython", None)
self.failUnless(self.Obs.signal.pop() == 'ObjDynExt')
self.failUnless(self.Obs.parameter.pop() is pyobj)
self.failUnless(self.Obs.parameter2.pop() == 'App::GroupExtensionPython')
self.failUnless(self.Obs.signal.pop(0) == 'ObjBeforeDynExt')
self.failUnless(self.Obs.parameter.pop(0) is pyobj)
self.failUnless(self.Obs.parameter2.pop(0) == 'App::GroupExtensionPython')
#a proxy property was changed, hence those events are also in the signal list
self.Obs.signal = []
self.Obs.parameter = []
self.Obs.parameter2 = []
FreeCAD.closeDocument(self.Doc1.Name)
self.Obs.signal = []
self.Obs.parameter = []
@@ -1907,6 +1929,18 @@ class DocumentObserverCases(unittest.TestCase):
self.failUnless(self.GuiObs.parameter.pop(0) is obj.ViewObject)
self.failUnless(not self.GuiObs.signal and not self.GuiObs.parameter and not self.GuiObs.parameter2)
obj.ViewObject.addExtension("Gui::ViewProviderGroupExtensionPython", None)
self.failUnless(self.Obs.signal.pop() == 'ObjDynExt')
self.failUnless(self.Obs.parameter.pop() is obj.ViewObject)
self.failUnless(self.Obs.parameter2.pop() == 'Gui::ViewProviderGroupExtensionPython')
self.failUnless(self.Obs.signal.pop() == 'ObjBeforeDynExt')
self.failUnless(self.Obs.parameter.pop() is obj.ViewObject)
self.failUnless(self.Obs.parameter2.pop() == 'Gui::ViewProviderGroupExtensionPython')
#a proxy property was changed, hence those events are also in the signal list (but of GUI observer)
self.GuiObs.signal = []
self.GuiObs.parameter = []
self.GuiObs.parameter2 = []
vo = obj.ViewObject
FreeCAD.ActiveDocument.removeObject(obj.Name)
self.failUnless(self.Obs.signal.pop(0) == 'ObjDeleted')