Implement and test extension events
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user