From 7bcb6519cc0c7636807c147ac96dca7e1872209b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Fri, 3 Jun 2016 19:19:54 +0200 Subject: [PATCH] Extensions: Make Python Integration work --- src/App/Application.cpp | 1 + src/App/CMakeLists.txt | 2 + src/App/DocumentObject.h.orig | 16 +- src/App/DocumentObjectExtension.cpp | 4 +- src/App/DocumentObjectExtension.h | 2 +- src/App/Extension.cpp | 251 +---------------------- src/App/Extension.h | 173 +++++++--------- src/App/ExtensionContainer.cpp | 296 +++++++++++++++++++++++++++ src/App/ExtensionContainer.h | 129 ++++++++++++ src/App/ExtensionContainerPy.xml | 3 +- src/App/ExtensionContainerPyImp.cpp | 25 ++- src/App/GeoFeatureGroupExtension.cpp | 7 +- src/App/GeoFeatureGroupExtension.h | 3 +- src/App/GroupExtension.cpp | 15 ++ src/App/GroupExtension.h | 30 +++ src/App/OriginGroupExtension.cpp | 12 +- src/App/OriginGroupExtension.h | 4 +- src/App/TransactionalObject.h | 2 +- src/Mod/Test/Document.py | 16 ++ 19 files changed, 622 insertions(+), 369 deletions(-) create mode 100644 src/App/ExtensionContainer.cpp create mode 100644 src/App/ExtensionContainer.h diff --git a/src/App/Application.cpp b/src/App/Application.cpp index a7641aa18..72e956f3c 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -1139,6 +1139,7 @@ void Application::initTypes(void) App ::Extension ::init(); App ::DocumentObjectExtension ::init(); App ::GroupExtension ::init(); + App ::GroupExtensionPython ::init(); App ::GeoFeatureGroupExtension ::init(); App ::OriginGroupExtension ::init(); diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt index fd6bf5ee6..8c4247e5e 100644 --- a/src/App/CMakeLists.txt +++ b/src/App/CMakeLists.txt @@ -97,6 +97,7 @@ SET(Document_CPP_SRCS ExtensionPyImp.cpp DocumentObjectExtension.cpp DocumentObjectExtensionPyImp.cpp + ExtensionContainer.cpp ExtensionContainerPyImp.cpp GroupExtension.cpp GroupExtensionPyImp.cpp @@ -137,6 +138,7 @@ SET(Document_HPP_SRCS Document.h DocumentObject.h Extension.h + ExtensionContainer.h GroupExtension.h DocumentObjectExtension.h DocumentObjectFileIncluded.h diff --git a/src/App/DocumentObject.h.orig b/src/App/DocumentObject.h.orig index b91dfdd9c..62f268615 100644 --- a/src/App/DocumentObject.h.orig +++ b/src/App/DocumentObject.h.orig @@ -24,11 +24,11 @@ #ifndef APP_DOCUMENTOBJECT_H #define APP_DOCUMENTOBJECT_H -<<<<<<< 51e23d854e59ab0a23a9ea9b19e74e5f02753d82 +<<<<<<< 75964436541d26c663d0ad51ae81c5ab359a539c #include ======= -#include ->>>>>>> Introduce extensions and port App groups +#include +>>>>>>> Extensions: Make Python Integration work #include #include #include @@ -80,11 +80,7 @@ public: /** Base class of all Classes handled in the Document */ -<<<<<<< 51e23d854e59ab0a23a9ea9b19e74e5f02753d82 class AppExport DocumentObject: public App::TransactionalObject -======= -class AppExport DocumentObject : public App::ExtensionContainer ->>>>>>> Introduce extensions and port App groups { PROPERTY_HEADER(App::DocumentObject); @@ -241,11 +237,11 @@ protected: /// get called after a document has been fully restored virtual void onDocumentRestored() {} /// get called after setting the document - virtual void onSettingDocument() {} + virtual void onSettingDocument(); /// get called after a brand new object was created - virtual void setupObject() {} + virtual void setupObject(); /// get called when object is going to be removed from the document - virtual void unsetupObject() {} + virtual void unsetupObject(); /// python object of this class and all descendend protected: // attributes diff --git a/src/App/DocumentObjectExtension.cpp b/src/App/DocumentObjectExtension.cpp index c3071880c..71fae6492 100644 --- a/src/App/DocumentObjectExtension.cpp +++ b/src/App/DocumentObjectExtension.cpp @@ -45,7 +45,7 @@ DocumentObjectExtension::~DocumentObjectExtension() } -short int DocumentObjectExtension::extensionMustExecute(void) const { +short int DocumentObjectExtension::extensionMustExecute(void) { return 0; } @@ -75,5 +75,3 @@ PyObject* DocumentObjectExtension::getExtensionPyObject(void) { } return Py::new_reference_to(ExtensionPythonObject); } - - diff --git a/src/App/DocumentObjectExtension.h b/src/App/DocumentObjectExtension.h index ea67518d3..aa0122cf7 100644 --- a/src/App/DocumentObjectExtension.h +++ b/src/App/DocumentObjectExtension.h @@ -46,7 +46,7 @@ public: virtual ~DocumentObjectExtension (); //override if execution is nesseccary - virtual short extensionMustExecute(void) const; + virtual short extensionMustExecute(void); virtual App::DocumentObjectExecReturn *extensionExecute(void); diff --git a/src/App/Extension.cpp b/src/App/Extension.cpp index aff75fc80..fb5b08603 100644 --- a/src/App/Extension.cpp +++ b/src/App/Extension.cpp @@ -52,7 +52,6 @@ using namespace App; Extension::Extension() { - } Extension::~Extension() @@ -96,249 +95,9 @@ const char* Extension::name() { return std::string().c_str(); } +namespace App { +PROPERTY_SOURCE_TEMPLATE(App::ExtensionPython, App::ExtensionPython::Inherited) - -TYPESYSTEM_SOURCE(App::ExtensionContainer, App::PropertyContainer); - -ExtensionContainer::ExtensionContainer() { - -}; - -ExtensionContainer::~ExtensionContainer() { - -}; - -void ExtensionContainer::registerExtension(Base::Type extension, Extension* ext) { - - if(ext->getExtendedObject() != this) - throw Base::Exception("ExtensionContainer::registerExtension: Extension has not this as base object"); - - //no duplicate extensions (including base classes) - if(hasExtension(extension)) { - for(auto entry : _extensions) { - if(entry.first == extension || entry.first.isDerivedFrom(extension)) { - _extensions.erase(entry.first); - break; - } - } - } - - _extensions[extension] = ext; -} - -bool ExtensionContainer::hasExtension(Base::Type t) const { - - //check for the exact type - bool found = _extensions.find(t) != _extensions.end(); - if(!found) { - //and for types derived from it, as they can be cast to the extension - for(auto entry : _extensions) { - if(entry.first.isDerivedFrom(t)) - return true; - } - return false; - } - return true; -} - -bool ExtensionContainer::hasExtension(const char* name) const { - - //and for types derived from it, as they can be cast to the extension - for(auto entry : _extensions) { - if(strcmp(entry.second->name(), name) == 0) - return true; - } - return false; -} - - -Extension* ExtensionContainer::getExtension(Base::Type t) { - - auto result = _extensions.find(t); - if(result == _extensions.end()) { - //we need to check for derived types - for(auto entry : _extensions) { - if(entry.first.isDerivedFrom(t)) - return entry.second; - } - //if we arive hear we don't have anything matching - throw Base::Exception("ExtensionContainer::getExtension: No extension of given type available"); - } - - return result->second; -} - -Extension* ExtensionContainer::getExtension(const char* name) { - - //and for types derived from it, as they can be cast to the extension - for(auto entry : _extensions) { - if(strcmp(entry.second->name(), name) == 0) - return entry.second; - } - return nullptr; -} - -std::vector< Extension* > ExtensionContainer::getExtensionsDerivedFrom(Base::Type type) const { - - std::vector vec; - //and for types derived from it, as they can be cast to the extension - for(auto entry : _extensions) { - if(entry.first.isDerivedFrom(type)) - vec.push_back(entry.second); - } - return vec; -} - -void ExtensionContainer::getPropertyList(std::vector< Property* >& List) const { - App::PropertyContainer::getPropertyList(List); - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()) - entry.second->getPropertyList(List); - } -} - -void ExtensionContainer::getPropertyMap(std::map< std::string, Property* >& Map) const { - App::PropertyContainer::getPropertyMap(Map); - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()) - entry.second->getPropertyMap(Map); - } -} - -Property* ExtensionContainer::getPropertyByName(const char* name) const { - auto prop = App::PropertyContainer::getPropertyByName(name); - if(prop) - return prop; - - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()){ - auto prop = entry.second->getPropertyByName(name); - if(prop) - return prop; - } - } - - return nullptr; -} - - -short int ExtensionContainer::getPropertyType(const Property* prop) const { - short int res = App::PropertyContainer::getPropertyType(prop); - if(res != 0) - return res; - - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()) { - res = entry.second->getPropertyType(prop); - if(res != 0) - return res; - } - } - - return 0; -} - -short int ExtensionContainer::getPropertyType(const char* name) const { - - short int res = App::PropertyContainer::getPropertyType(name); - if(res != 0) - return res; - - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()) { - res = entry.second->getPropertyType(name); - if(res != 0) - return res; - } - } - - return 0; -} - - -const char* ExtensionContainer::getPropertyName(const Property* prop) const { - - const char* res = App::PropertyContainer::getPropertyName(prop); - if(res != 0) - return res; - - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()) { - res = entry.second->getPropertyName(prop); - if(res != 0) - return res; - } - } - - return 0; -} - -const char* ExtensionContainer::getPropertyGroup(const Property* prop) const { - - const char* res = App::PropertyContainer::getPropertyGroup(prop); - if(res != 0) - return res; - - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()) { - res = entry.second->getPropertyGroup(prop); - if(res != 0) - return res; - } - } - - return 0; -} - -const char* ExtensionContainer::getPropertyGroup(const char* name) const { - - const char* res = App::PropertyContainer::getPropertyGroup(name); - if(res != 0) - return res; - - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()) { - res = entry.second->getPropertyGroup(name); - if(res != 0) - return res; - } - } - - return 0; -} - - -const char* ExtensionContainer::getPropertyDocumentation(const Property* prop) const { - - const char* res = App::PropertyContainer::getPropertyDocumentation(prop); - if(res != 0) - return res; - - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()) { - res = entry.second->getPropertyDocumentation(prop); - if(res != 0) - return res; - } - } - - return 0; -} - -const char* ExtensionContainer::getPropertyDocumentation(const char* name) const { - - const char* res = App::PropertyContainer::getPropertyDocumentation(name); - if(res != 0) - return res; - - for(auto entry : _extensions) { - if(entry.second->isPythonExtension()) { - res = entry.second->getPropertyDocumentation(name); - if(res != 0) - return res; - } - } - - return 0; -} - +// explicit template instantiation +template class AppExport ExtensionPythonT; +} \ No newline at end of file diff --git a/src/App/Extension.h b/src/App/Extension.h index 359bcc643..c576c8936 100644 --- a/src/App/Extension.h +++ b/src/App/Extension.h @@ -26,11 +26,9 @@ #include "PropertyContainer.h" #include "PropertyPythonObject.h" -#include "DynamicProperty.h" +#include "Base/Interpreter.h" #include -#include - namespace App { /** @@ -56,127 +54,108 @@ public: //get extension name without namespace const char* name(); - //store if this extension is created from python or not (hence c++ multiple inheritance) - void setPythonExtension(bool val) {m_isPythonExtension = val;}; + bool isPythonExtension() {return m_isPythonExtension;}; virtual PyObject* getExtensionPyObject(void); protected: - void initExtension(Base::Type type); - Py::Object ExtensionPythonObject; + void initExtension(Base::Type type); + bool m_isPythonExtension = false; + Py::Object ExtensionPythonObject; private: Base::Type m_extensionType; App::DocumentObject* m_base = nullptr; - bool m_isPythonExtension = false; }; +/** + * Generic Python extension class which allows to behave every extension + * derived class as Python extension -- simply by subclassing. + */ +template +class ExtensionPythonT : public ExtensionT +{ + PROPERTY_HEADER(App::ExtensionPythonT); -#define PROPERTY_HEADER_WITH_EXTENSIONS(_class_) \ - PROPERTY_HEADER(_class) - -//helper macro to add parent to property data -#define ADD_PARENT(r, data, elem)\ - data::propertyData.parentPropertyData.push_back(elem::getPropertyDataPtr()); - -/// -#define PROPERTY_SOURCE_WITH_EXTENSIONS(_class_, _parentclass_, _extensions_) \ -TYPESYSTEM_SOURCE_P(_class_);\ -const App::PropertyData * _class_::getPropertyDataPtr(void){return &propertyData;} \ -const App::PropertyData & _class_::getPropertyData(void) const{return propertyData;} \ -App::PropertyData _class_::propertyData; \ -void _class_::init(void){\ - initSubclass(_class_::classTypeId, #_class_ , #_parentclass_, &(_class_::create) ); \ - ADD_PARENT(0, _class_, _parentclass_)\ - BOOST_PP_SEQ_FOR_EACH(ADD_PARENT, _class_, _extensions_)\ -} - -template -class ExtensionPython : public ExtensionT { - - PROPERTY_HEADER(App::ExtensionPython); - public: - ExtensionPython() { + typedef ExtensionT Inherited; + + ExtensionPythonT() { + ExtensionT::m_isPythonExtension = true; + ADD_PROPERTY(Proxy,(Py::Object())); } - - //we actually don't need to override any extension methods by default as dynamic properties - //should not be supportet. -protected: + virtual ~ExtensionPythonT() { + } + PropertyPythonObject Proxy; }; -class AppExport ExtensionContainer : public virtual App::PropertyContainer -{ +typedef ExtensionPythonT ExtensionPython; - TYPESYSTEM_HEADER(); +//helper macros to define python extensions +#define EXTENSION_PROXY_FIRST(function) \ + Base::PyGILStateLocker lock;\ + Py::Object result;\ + try {\ + Property* proxy = this->getPropertyByName("Proxy");\ + if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {\ + Py::Object feature = static_cast(proxy)->getValue();\ + if (feature.hasAttr(std::string("function"))) {\ + if (feature.hasAttr("__object__")) {\ + Py::Callable method(feature.getAttr(std::string("function"))); + + + -public: +#define EXTENSION_PROXY_SECOND(function)\ + result = method.apply(args);\ + }\ + else {\ + Py::Callable method(feature.getAttr(std::string("function"))); + +#define EXTENSION_PROXY_THIRD()\ + result = method.apply(args);\ + }\ + }\ + }\ + }\ + catch (Py::Exception&) {\ + Base::PyException e;\ + e.ReportException();\ + } - typedef std::map::iterator ExtensionIterator; +#define EXTENSION_PROXY_NOARG(function)\ + EXTENSION_PROXY_FIRST(function) \ + Py::Tuple args;\ + EXTENSION_PROXY_SECOND(function) \ + Py::Tuple args(1);\ + args.setItem(0, Py::Object(this->getExtensionPyObject(), true));\ + EXTENSION_PROXY_THIRD() - ExtensionContainer(); - virtual ~ExtensionContainer(); +#define EXTENSION_PROXY_ONEARG(function, arg)\ + EXTENSION_PROXY_FIRST(function) \ + Py::Tuple args;\ + args.setItem(0, arg); \ + EXTENSION_PROXY_SECOND(function) \ + Py::Tuple args(2);\ + args.setItem(0, Py::Object(this->getExtensionPyObject(), true));\ + args.setItem(1, arg); \ + EXTENSION_PROXY_THIRD() - void registerExtension(Base::Type extension, App::Extension* ext); - bool hasExtension(Base::Type) const; - bool hasExtension(const char* name) const; //this version does not check derived classes - App::Extension* getExtension(Base::Type); - App::Extension* getExtension(const char* name); //this version does not check derived classes - template - ExtensionT* getExtensionByType() { - return dynamic_cast(getExtension(ExtensionT::getClassTypeId())); +#define EXTENSION_PYTHON_OVERRIDE_VOID_NOARGS(function)\ + virtual void function() override {\ + EXTENSION_PROXY_NOARGS(function)\ }; - //get all extensions which have the given base class - std::vector getExtensionsDerivedFrom(Base::Type type) const; - template - std::vector getExtensionsDerivedFromType() const { - auto vec = getExtensionsDerivedFrom(ExtensionT::getClassTypeId()); - std::vector typevec; - for(auto ext : vec) - typevec.push_back(dynamic_cast(ext)); - - return typevec; +#define EXTENSION_PYTHON_OVERRIDE_OBJECT_NOARGS(function)\ + virtual PyObject* function() override {\ + EXTENSION_PROXY_NOARGS(function)\ + return res.ptr();\ }; - ExtensionIterator extensionBegin() {return _extensions.begin();}; - ExtensionIterator extensionEnd() {return _extensions.end();}; - - - /** @name Access properties */ - //@{ - /// find a property by its name - virtual Property *getPropertyByName(const char* name) const override; - /// get the name of a property - virtual const char* getPropertyName(const Property* prop) const override; - /// get all properties of the class (including properties of the parent) - virtual void getPropertyMap(std::map &Map) const override; - /// get all properties of the class (including properties of the parent) - virtual void getPropertyList(std::vector &List) const override; - - /// get the Type of a Property - virtual short getPropertyType(const Property* prop) const override; - /// get the Type of a named Property - virtual short getPropertyType(const char *name) const override; - /// get the Group of a Property - virtual const char* getPropertyGroup(const Property* prop) const override; - /// get the Group of a named Property - virtual const char* getPropertyGroup(const char *name) const override; - /// get the Group of a Property - virtual const char* getPropertyDocumentation(const Property* prop) const override; - /// get the Group of a named Property - virtual const char* getPropertyDocumentation(const char *name) const override; - //@} - -private: - //stored extensions - std::map _extensions; -}; - -} //App +}; //App #endif // APP_EXTENSION_H diff --git a/src/App/ExtensionContainer.cpp b/src/App/ExtensionContainer.cpp new file mode 100644 index 000000000..96efcecc4 --- /dev/null +++ b/src/App/ExtensionContainer.cpp @@ -0,0 +1,296 @@ +/*************************************************************************** + * Copyright (c) Stefan Tröger (stefantroeger@gmx.net) 2016 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#ifndef _PreComp_ +# include +# include +#endif + +#include "Extension.h" +#include "DocumentObject.h" +#include "Base/Exception.h" +#include + +using namespace App; + +TYPESYSTEM_SOURCE(App::ExtensionContainer, App::PropertyContainer); + +ExtensionContainer::ExtensionContainer() { + +}; + +ExtensionContainer::~ExtensionContainer() { + +}; + +void ExtensionContainer::registerExtension(Base::Type extension, Extension* ext) { + + if(ext->getExtendedObject() != this) + throw Base::Exception("ExtensionContainer::registerExtension: Extension has not this as base object"); + + //no duplicate extensions (including base classes) + if(hasExtension(extension)) { + for(auto entry : _extensions) { + if(entry.first == extension || entry.first.isDerivedFrom(extension)) { + _extensions.erase(entry.first); + break; + } + } + } + + _extensions[extension] = ext; +} + +bool ExtensionContainer::hasExtension(Base::Type t) const { + + //check for the exact type + bool found = _extensions.find(t) != _extensions.end(); + if(!found) { + //and for types derived from it, as they can be cast to the extension + for(auto entry : _extensions) { + if(entry.first.isDerivedFrom(t)) + return true; + } + return false; + } + return true; +} + +bool ExtensionContainer::hasExtension(const char* name) const { + + //and for types derived from it, as they can be cast to the extension + for(auto entry : _extensions) { + if(strcmp(entry.second->name(), name) == 0) + return true; + } + return false; +} + + +Extension* ExtensionContainer::getExtension(Base::Type t) { + + auto result = _extensions.find(t); + if(result == _extensions.end()) { + //we need to check for derived types + for(auto entry : _extensions) { + if(entry.first.isDerivedFrom(t)) + return entry.second; + } + //if we arive hear we don't have anything matching + throw Base::Exception("ExtensionContainer::getExtension: No extension of given type available"); + } + + return result->second; +} + +Extension* ExtensionContainer::getExtension(const char* name) { + + //and for types derived from it, as they can be cast to the extension + for(auto entry : _extensions) { + if(strcmp(entry.second->name(), name) == 0) + return entry.second; + } + return nullptr; +} + +std::vector< Extension* > ExtensionContainer::getExtensionsDerivedFrom(Base::Type type) const { + + std::vector vec; + //and for types derived from it, as they can be cast to the extension + for(auto entry : _extensions) { + if(entry.first.isDerivedFrom(type)) + vec.push_back(entry.second); + } + return vec; +} + +void ExtensionContainer::getPropertyList(std::vector< Property* >& List) const { + App::PropertyContainer::getPropertyList(List); + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()) + entry.second->getPropertyList(List); + } +} + +void ExtensionContainer::getPropertyMap(std::map< std::string, Property* >& Map) const { + App::PropertyContainer::getPropertyMap(Map); + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()) + entry.second->getPropertyMap(Map); + } +} + +Property* ExtensionContainer::getPropertyByName(const char* name) const { + auto prop = App::PropertyContainer::getPropertyByName(name); + if(prop) + return prop; + + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()){ + auto prop = entry.second->getPropertyByName(name); + if(prop) + return prop; + } + } + + return nullptr; +} + + +short int ExtensionContainer::getPropertyType(const Property* prop) const { + short int res = App::PropertyContainer::getPropertyType(prop); + if(res != 0) + return res; + + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()) { + res = entry.second->getPropertyType(prop); + if(res != 0) + return res; + } + } + + return 0; +} + +short int ExtensionContainer::getPropertyType(const char* name) const { + + short int res = App::PropertyContainer::getPropertyType(name); + if(res != 0) + return res; + + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()) { + res = entry.second->getPropertyType(name); + if(res != 0) + return res; + } + } + + return 0; +} + + +const char* ExtensionContainer::getPropertyName(const Property* prop) const { + + const char* res = App::PropertyContainer::getPropertyName(prop); + if(res != 0) + return res; + + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()) { + res = entry.second->getPropertyName(prop); + if(res != 0) + return res; + } + } + + return 0; +} + +const char* ExtensionContainer::getPropertyGroup(const Property* prop) const { + + const char* res = App::PropertyContainer::getPropertyGroup(prop); + if(res != 0) + return res; + + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()) { + res = entry.second->getPropertyGroup(prop); + if(res != 0) + return res; + } + } + + return 0; +} + +const char* ExtensionContainer::getPropertyGroup(const char* name) const { + + const char* res = App::PropertyContainer::getPropertyGroup(name); + if(res != 0) + return res; + + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()) { + res = entry.second->getPropertyGroup(name); + if(res != 0) + return res; + } + } + + return 0; +} + + +const char* ExtensionContainer::getPropertyDocumentation(const Property* prop) const { + + const char* res = App::PropertyContainer::getPropertyDocumentation(prop); + if(res != 0) + return res; + + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()) { + res = entry.second->getPropertyDocumentation(prop); + if(res != 0) + return res; + } + } + + return 0; +} + +const char* ExtensionContainer::getPropertyDocumentation(const char* name) const { + + const char* res = App::PropertyContainer::getPropertyDocumentation(name); + if(res != 0) + return res; + + for(auto entry : _extensions) { + if(entry.second->isPythonExtension()) { + res = entry.second->getPropertyDocumentation(name); + if(res != 0) + return res; + } + } + + return 0; +} + +void ExtensionContainer::onChanged(const Property* prop) { + /* + //we need to make sure that the proxy we use is always the proxy of the extended class. This + //is needed as only the extended class proxy (either c++ or python) is managed and ensured to + //be the python implementation class + //Note: this property only exist if the created object is FeaturePythonT<>, this won't work for + //any default document object. But this doesnt matter much as there is a proxy object set anyway + //if a extension gets registered from python. This is only for synchronisation. + if(strcmp(prop->getName(), "Proxy")) { + for(auto entry : _extensions) + entry.second->getExtensionPyObject().setValue(static_cast(prop)->getValue()); + }*/ + + App::PropertyContainer::onChanged(prop); +} diff --git a/src/App/ExtensionContainer.h b/src/App/ExtensionContainer.h new file mode 100644 index 000000000..b7a80b56d --- /dev/null +++ b/src/App/ExtensionContainer.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright (c) Stefan Tröger (stefantroeger@gmx.net) 2016 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef APP_EXTENSIONCONTAINER_H +#define APP_EXTENSIONCONTAINER_H + +#include "Extension.h" +#include "PropertyContainer.h" +#include "PropertyPythonObject.h" +#include "DynamicProperty.h" +#include + +#include + +namespace App { + +class AppExport ExtensionContainer : public virtual App::PropertyContainer +{ + + TYPESYSTEM_HEADER(); + +public: + + typedef std::map::iterator ExtensionIterator; + + ExtensionContainer(); + virtual ~ExtensionContainer(); + + void registerExtension(Base::Type extension, App::Extension* ext); + bool hasExtension(Base::Type) const; + bool hasExtension(const char* name) const; //this version does not check derived classes + App::Extension* getExtension(Base::Type); + App::Extension* getExtension(const char* name); //this version does not check derived classes + template + ExtensionT* getExtensionByType() { + return dynamic_cast(getExtension(ExtensionT::getClassTypeId())); + }; + + //get all extensions which have the given base class + std::vector getExtensionsDerivedFrom(Base::Type type) const; + template + std::vector getExtensionsDerivedFromType() const { + auto vec = getExtensionsDerivedFrom(ExtensionT::getClassTypeId()); + std::vector typevec; + for(auto ext : vec) + typevec.push_back(dynamic_cast(ext)); + + return typevec; + }; + + ExtensionIterator extensionBegin() {return _extensions.begin();}; + ExtensionIterator extensionEnd() {return _extensions.end();}; + + + /** @name Access properties */ + //@{ + /// find a property by its name + virtual Property *getPropertyByName(const char* name) const override; + /// get the name of a property + virtual const char* getPropertyName(const Property* prop) const override; + /// get all properties of the class (including properties of the parent) + virtual void getPropertyMap(std::map &Map) const override; + /// get all properties of the class (including properties of the parent) + virtual void getPropertyList(std::vector &List) const override; + + /// get the Type of a Property + virtual short getPropertyType(const Property* prop) const override; + /// get the Type of a named Property + virtual short getPropertyType(const char *name) const override; + /// get the Group of a Property + virtual const char* getPropertyGroup(const Property* prop) const override; + /// get the Group of a named Property + virtual const char* getPropertyGroup(const char *name) const override; + /// get the Group of a Property + virtual const char* getPropertyDocumentation(const Property* prop) const override; + /// get the Group of a named Property + virtual const char* getPropertyDocumentation(const char *name) const override; + //@} + + virtual void onChanged(const Property*); + +private: + //stored extensions + std::map _extensions; +}; + + +#define PROPERTY_HEADER_WITH_EXTENSIONS(_class_) \ + PROPERTY_HEADER(_class) + +//helper macro to add parent to property data +#define ADD_PARENT(r, data, elem)\ + data::propertyData.parentPropertyData.push_back(elem::getPropertyDataPtr()); + +/// +#define PROPERTY_SOURCE_WITH_EXTENSIONS(_class_, _parentclass_, _extensions_) \ +TYPESYSTEM_SOURCE_P(_class_);\ +const App::PropertyData * _class_::getPropertyDataPtr(void){return &propertyData;} \ +const App::PropertyData & _class_::getPropertyData(void) const{return propertyData;} \ +App::PropertyData _class_::propertyData; \ +void _class_::init(void){\ + initSubclass(_class_::classTypeId, #_class_ , #_parentclass_, &(_class_::create) ); \ + ADD_PARENT(0, _class_, _parentclass_)\ + BOOST_PP_SEQ_FOR_EACH(ADD_PARENT, _class_, _extensions_)\ +} + +} //App + +#endif // APP_EXTENSIONCONTAINER_H diff --git a/src/App/ExtensionContainerPy.xml b/src/App/ExtensionContainerPy.xml index 5d460ce5e..465f879a9 100644 --- a/src/App/ExtensionContainerPy.xml +++ b/src/App/ExtensionContainerPy.xml @@ -17,7 +17,8 @@ - Adds an extension to the object + Adds an extension to the object. Requires the string identifier as well as the python object + used to check for overridden functions (most likely self) diff --git a/src/App/ExtensionContainerPyImp.cpp b/src/App/ExtensionContainerPyImp.cpp index 515de5946..1c47bd35a 100644 --- a/src/App/ExtensionContainerPyImp.cpp +++ b/src/App/ExtensionContainerPyImp.cpp @@ -70,13 +70,13 @@ int ExtensionContainerPy::initialisation() { } int ExtensionContainerPy::deinitialisation() { - +/* //we need to delete all added python extensions, as we are the owner! ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin(); for(; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) { if((*it).second->isPythonExtension()) delete (*it).second; - } + }*/ return 1; }; @@ -127,7 +127,8 @@ PyObject* ExtensionContainerPy::hasExtension(PyObject *args) { PyObject* ExtensionContainerPy::addExtension(PyObject *args) { char *type; - if (!PyArg_ParseTuple(args, "s", &type)) + PyObject* proxy; + if (!PyArg_ParseTuple(args, "sO", &type, &proxy)) return NULL; // NULL triggers exception //get the extension type asked for @@ -140,10 +141,24 @@ PyObject* ExtensionContainerPy::addExtension(PyObject *args) { //register the extension App::Extension* ext = static_cast(extension.createInstance()); + //check if this really is a python extension! + if(!ext->isPythonExtension()) { + delete ext; + std::stringstream str; + str << "Extension is not a python addable version: '" << type << "'" << std::ends; + throw Py::Exception(Base::BaseExceptionFreeCADError,str.str()); + } + ext->initExtension(dynamic_cast(getExtensionContainerPtr())); - //we are responsible for deleting the extension once done with it! - ext->setPythonExtension(true); + //set the proxy to allow python overrides + App::Property* pp = ext->getPropertyByName("Proxy"); + if(!pp) { + std::stringstream str; + str << "Accessing the proxy property failed!" << std::ends; + throw Py::Exception(Base::BaseExceptionFreeCADError,str.str()); + } + static_cast(pp)->setValue(Py::asObject(proxy)); //make sure all functions of the extension are acessible through this object PyMethodDef* tmpptr = (PyMethodDef*)ext->getExtensionPyObject()->ob_type->tp_methods; diff --git a/src/App/GeoFeatureGroupExtension.cpp b/src/App/GeoFeatureGroupExtension.cpp index fa18b92c4..180368f94 100644 --- a/src/App/GeoFeatureGroupExtension.cpp +++ b/src/App/GeoFeatureGroupExtension.cpp @@ -147,6 +147,9 @@ DocumentObject* GeoFeatureGroupExtension::getGroupOfObject(const DocumentObject* // Python feature --------------------------------------------------------- -// explicit template instantiation -//template class AppExport DocumentObjectExtensionPython; +namespace App { +PROPERTY_SOURCE_TEMPLATE(App::GeoFeatureGroupExtensionPython, App::GeoFeatureGroupExtension) +// explicit template instantiation +template class AppExport ExtensionPythonT>; +} \ No newline at end of file diff --git a/src/App/GeoFeatureGroupExtension.h b/src/App/GeoFeatureGroupExtension.h index 4003388a6..6f3bbeab8 100644 --- a/src/App/GeoFeatureGroupExtension.h +++ b/src/App/GeoFeatureGroupExtension.h @@ -75,7 +75,8 @@ public: } }; -typedef App::ExtensionPython GeoFeatureGroupExtensionPython; +typedef ExtensionPythonT> GeoFeatureGroupExtensionPython; + } //namespace App diff --git a/src/App/GroupExtension.cpp b/src/App/GroupExtension.cpp index abd670a4a..deb83361a 100644 --- a/src/App/GroupExtension.cpp +++ b/src/App/GroupExtension.cpp @@ -50,12 +50,19 @@ GroupExtension::~GroupExtension() DocumentObject* GroupExtension::addObject(const char* sType, const char* pObjectName) { DocumentObject* obj = getExtendedObject()->getDocument()->addObject(sType, pObjectName); + if(!allowObject(obj)) { + getExtendedObject()->getDocument()->remObject(obj->getNameInDocument()); + return nullptr; + } if (obj) addObject(obj); return obj; } void GroupExtension::addObject(DocumentObject* obj) { + if(!allowObject(obj)) + return; + if (!hasObject(obj)) { std::vector grp = Group.getValues(); grp.push_back(obj); @@ -191,3 +198,11 @@ PyObject* GroupExtension::getExtensionPyObject(void) { } return Py::new_reference_to(ExtensionPythonObject); } + + +namespace App { +PROPERTY_SOURCE_TEMPLATE(App::GroupExtensionPython, App::GroupExtension) + +// explicit template instantiation +template class AppExport ExtensionPythonT>; +} diff --git a/src/App/GroupExtension.h b/src/App/GroupExtension.h index 2eee37a42..2da27c3a0 100644 --- a/src/App/GroupExtension.h +++ b/src/App/GroupExtension.h @@ -54,6 +54,10 @@ public: /* Adds the object \a obj to this group. */ void addObject(DocumentObject* obj); + /*override this function if you want only special objects + */ + virtual bool allowObject(DocumentObject* obj) {return true;}; + /** Removes an object from this group. */ void removeObject(DocumentObject* obj); @@ -99,6 +103,32 @@ private: void removeObjectFromDocument(DocumentObject*); }; + +template +class GroupExtensionPythonT : public ExtensionT { + +public: + + GroupExtensionPythonT() {} + virtual ~GroupExtensionPythonT() {} + + //override the documentobjectextension functions to make them available in python + virtual bool allowObject(DocumentObject* obj) override { + Py::Object pyobj = Py::asObject(obj->getPyObject()); + EXTENSION_PROXY_ONEARG(allowObject, pyobj); + + if(result.isNone()) + ExtensionT::allowObject(obj); + + if(result.isBoolean()) + return result.isTrue(); + + return false; + }; +}; + +typedef ExtensionPythonT> GroupExtensionPython; + } //namespace App diff --git a/src/App/OriginGroupExtension.cpp b/src/App/OriginGroupExtension.cpp index 4cc984f2e..d98efdf18 100644 --- a/src/App/OriginGroupExtension.cpp +++ b/src/App/OriginGroupExtension.cpp @@ -84,7 +84,7 @@ App::DocumentObject *OriginGroupExtension::getGroupOfObject (const DocumentObjec return 0; } -short OriginGroupExtension::extensionMustExecute() const { +short OriginGroupExtension::extensionMustExecute() { if (Origin.isTouched ()) { return 1; } else { @@ -123,4 +123,14 @@ void OriginGroupExtension::onExtendedUnsetupObject () { } GeoFeatureGroupExtension::onExtendedUnsetupObject (); +} + + +// Python feature --------------------------------------------------------- + +namespace App { +PROPERTY_SOURCE_TEMPLATE(App::OriginGroupExtensionPython, App::OriginGroupExtension) + +// explicit template instantiation +template class AppExport ExtensionPythonT>; } \ No newline at end of file diff --git a/src/App/OriginGroupExtension.h b/src/App/OriginGroupExtension.h index 7a258e666..e254648f1 100644 --- a/src/App/OriginGroupExtension.h +++ b/src/App/OriginGroupExtension.h @@ -57,7 +57,7 @@ public: static DocumentObject* getGroupOfObject (const DocumentObject* obj, bool indirect=true); /// Returns true on changing OriginFeature set - virtual short extensionMustExecute () const override; + virtual short extensionMustExecute () override; /// Origin linked to the group PropertyLink Origin; @@ -71,6 +71,8 @@ protected: virtual void onExtendedUnsetupObject () override; }; +typedef ExtensionPythonT> OriginGroupExtensionPython; + } /* App */ #endif /* end of include guard: ORIGINGROUP_H_QHTU73IF */ diff --git a/src/App/TransactionalObject.h b/src/App/TransactionalObject.h index 47589b7fc..ecdcb8269 100644 --- a/src/App/TransactionalObject.h +++ b/src/App/TransactionalObject.h @@ -24,7 +24,7 @@ #ifndef APP_TRANSACTIONALOBJECT_H #define APP_TRANSACTIONALOBJECT_H -#include +#include namespace App { diff --git a/src/Mod/Test/Document.py b/src/Mod/Test/Document.py index 2a751f982..98ebbd326 100644 --- a/src/Mod/Test/Document.py +++ b/src/Mod/Test/Document.py @@ -181,6 +181,22 @@ class DocumentBasicCases(unittest.TestCase): else: self.failUnless(False) del L2 + + def testExtensions(self): + #we try to create a normal python object and add a extension to it + obj = self.Doc.addObject("App::DocumentObject", "Extension_1") + grp = self.Doc.addObject("App::DocumentObject", "Extension_2") + #we should have all methods we need to handle extensions + try: + self.failUnless(not grp.hasExtension("App::GroupExtension")) + grp.addExtension("App::GroupExtension", self) + grp.addObject(obj) + self.failUnless(grp.Group[0] == obj) + except: + self.failUnless(True) + + self.Doc.remove(obj) + del obj def tearDown(self): #closing doc