From 4c42181e344ad42ea436d42db3bd7deab5991f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Tr=C3=B6ger?= Date: Wed, 1 Jun 2016 08:37:28 +0200 Subject: [PATCH] Extensions: Show up in the python interface --- src/App/CMakeLists.txt | 9 + src/App/DocumentObjectGroup.cpp | 26 ++- src/App/DocumentObjectGroup.h | 6 +- src/App/DocumentObjectGroupPy.xml | 34 ---- src/App/DocumentObjectGroupPyImp.cpp | 160 ------------------ src/App/DocumentObjectPy.xml | 4 +- src/App/Extension.cpp | 94 ++++++++-- src/App/Extension.h | 66 +++++--- src/App/ExtensionContainerPy.xml | 20 +++ src/App/ExtensionContainerPyImp.cpp | 93 ++++++++++ src/App/ExtensionPy.xml | 18 ++ src/App/ExtensionPyImp.cpp | 52 ++++++ src/App/GeoFeatureGroup.cpp | 2 +- src/App/OriginGroup.cpp | 2 +- src/App/Part.cpp | 4 +- src/App/Part.h | 2 +- src/App/PropertyContainer.cpp | 34 ++-- src/App/PropertyContainer.h | 13 +- src/App/PropertyContainerPyImp.cpp | 4 +- .../generateBase/generateModel_Module.py | 28 ++- .../templateClassPyExport.py | 22 +++ 21 files changed, 435 insertions(+), 258 deletions(-) create mode 100644 src/App/ExtensionContainerPy.xml create mode 100644 src/App/ExtensionContainerPyImp.cpp create mode 100644 src/App/ExtensionPy.xml create mode 100644 src/App/ExtensionPyImp.cpp diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt index fbba57953d..30ce7369f9 100644 --- a/src/App/CMakeLists.txt +++ b/src/App/CMakeLists.txt @@ -53,6 +53,9 @@ set(FreeCADApp_LIBS generate_from_xml(DocumentPy) generate_from_xml(DocumentObjectPy) +generate_from_xml(ExtensionPy) +generate_from_xml(ExtensionContainerPy) +generate_from_xml(GroupExtensionPy) generate_from_xml(DocumentObjectGroupPy) generate_from_xml(GeoFeaturePy) #generate_from_xml(GeoFeatureGroupPy) @@ -67,6 +70,9 @@ generate_from_py(FreeCADInit InitScript.h) generate_from_py(FreeCADTest TestScript.h) SET(FreeCADApp_XML_SRCS + ExtensionPy.xml + ExtensionContainerPy.xml + GroupExtensionPy.xml DocumentObjectGroupPy.xml DocumentObjectPy.xml GeoFeaturePy.xml @@ -86,6 +92,9 @@ SET(Document_CPP_SRCS Document.cpp DocumentObject.cpp Extension.cpp + ExtensionPyImp.cpp + ExtensionContainerPyImp.cpp + GroupExtensionPyImp.cpp DocumentObjectFileIncluded.cpp DocumentObjectGroup.cpp DocumentObjectGroupPyImp.cpp diff --git a/src/App/DocumentObjectGroup.cpp b/src/App/DocumentObjectGroup.cpp index a2036e07f1..946d039a7e 100644 --- a/src/App/DocumentObjectGroup.cpp +++ b/src/App/DocumentObjectGroup.cpp @@ -28,6 +28,7 @@ #include "DocumentObjectGroup.h" #include "DocumentObjectGroupPy.h" +#include "GroupExtensionPy.h" #include "Document.h" #include "FeaturePythonPyImp.h" @@ -37,7 +38,7 @@ PROPERTY_SOURCE(App::GroupExtension, App::Extension) GroupExtension::GroupExtension() { - m_extensionType = GroupExtension::getClassTypeId(); + initExtension(GroupExtension::getClassTypeId()); ADD_PROPERTY_TYPE(Group,(0),"Base",(App::PropertyType)(Prop_Output),"List of referenced objects"); } @@ -178,18 +179,37 @@ DocumentObject* GroupExtension::getGroupOfObject(const DocumentObject* obj) return 0; } +PyObject* GroupExtension::getExtensionPyObject(void) { + + if (ExtensionPythonObject.is(Py::_None())){ + // ref counter is set to 1 + auto grp = new GroupExtensionPy(this); + ExtensionPythonObject = Py::Object(grp,true); + } + return Py::new_reference_to(ExtensionPythonObject); +} -PROPERTY_SOURCE(App::DocumentObjectGroup, App::DocumentObject) + + +PROPERTY_SOURCE_WITH_EXTENSIONS(App::DocumentObjectGroup, App::DocumentObject, (App::GroupExtension)) DocumentObjectGroup::DocumentObjectGroup(void): DocumentObject(), GroupExtension() { - setExtendedObject(this); + GroupExtension::initExtension(this); } DocumentObjectGroup::~DocumentObjectGroup() { } +PyObject *DocumentObjectGroup::getPyObject() +{ + if (PythonObject.is(Py::_None())){ + // ref counter is set to 1 + PythonObject = Py::Object(new DocumentObjectGroupPy(this),true); + } + return Py::new_reference_to(PythonObject); +} // Python feature --------------------------------------------------------- diff --git a/src/App/DocumentObjectGroup.h b/src/App/DocumentObjectGroup.h index 2c7b7204af..6af8fcd5c6 100644 --- a/src/App/DocumentObjectGroup.h +++ b/src/App/DocumentObjectGroup.h @@ -89,6 +89,8 @@ public: */ static DocumentObject* getGroupOfObject(const DocumentObject* obj); //@} + + virtual PyObject* getExtensionPyObject(void); /// Properties PropertyLinkList Group; @@ -102,7 +104,7 @@ typedef App::ExtensionPython GroupExtensionPython; class DocumentObjectGroup : public DocumentObject, public GroupExtension { - PROPERTY_HEADER(App::DocumentObjectGroup); + PROPERTY_HEADER_WITH_EXTENSIONS(App::DocumentObjectGroup); public: /// Constructor @@ -113,6 +115,8 @@ public: virtual const char* getViewProviderName(void) const { return "Gui::ViewProviderDocumentObjectGroup"; }; + + virtual PyObject *getPyObject(void); }; typedef App::FeaturePythonT DocumentObjectGroupPython; diff --git a/src/App/DocumentObjectGroupPy.xml b/src/App/DocumentObjectGroupPy.xml index a39ee8c217..07b37a5caf 100644 --- a/src/App/DocumentObjectGroupPy.xml +++ b/src/App/DocumentObjectGroupPy.xml @@ -13,40 +13,6 @@ This class handles document objects in group - - - Create and add an object with given type and name to the group - - - - - Add an object to the group - - - - - Remove an object from the group - - - - - Remove all child objects from the group and document - - - - - Return the object with the given name - - - - - hasObject(obj, recursive=false) - Checks if the group has a given object - @param obj the object to check for. - @param recursive if true check also if the obj is child of some sub group (default is false). - - - diff --git a/src/App/DocumentObjectGroupPyImp.cpp b/src/App/DocumentObjectGroupPyImp.cpp index bb290ee443..821fc99785 100644 --- a/src/App/DocumentObjectGroupPyImp.cpp +++ b/src/App/DocumentObjectGroupPyImp.cpp @@ -39,166 +39,6 @@ std::string DocumentObjectGroupPy::representation(void) const return std::string(""); } -PyObject* DocumentObjectGroupPy::newObject(PyObject *args) -{ - char *sType,*sName=0; - if (!PyArg_ParseTuple(args, "s|s", &sType,&sName)) // convert args: Python->C - return NULL; - - DocumentObject *object = getDocumentObjectGroupPtr()->addObject(sType, sName); - if ( object ) { - return object->getPyObject(); - } - else { - PyErr_Format(Base::BaseExceptionFreeCADError, "Cannot create object of type '%s'", sType); - return NULL; - } -} - -PyObject* DocumentObjectGroupPy::addObject(PyObject *args) -{ - PyObject *object; - if (!PyArg_ParseTuple(args, "O!", &(DocumentObjectPy::Type), &object)) // convert args: Python->C - return NULL; // NULL triggers exception - - DocumentObjectPy* docObj = static_cast(object); - if (!docObj->getDocumentObjectPtr() || !docObj->getDocumentObjectPtr()->getNameInDocument()) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot add an invalid object"); - return NULL; - } - if (docObj->getDocumentObjectPtr()->getDocument() != getDocumentObjectGroupPtr()->getDocument()) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot add an object from another document to this group"); - return NULL; - } - if (docObj->getDocumentObjectPtr() == this->getDocumentObjectGroupPtr()) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot add a group object to itself"); - return NULL; - } - if (docObj->getDocumentObjectPtr()->getTypeId().isDerivedFrom(DocumentObjectGroup::getClassTypeId())) { - App::DocumentObjectGroup* docGrp = static_cast(docObj->getDocumentObjectPtr()); - if (this->getDocumentObjectGroupPtr()->isChildOf(docGrp)) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot add a group object to a child group"); - return NULL; - } - } - - DocumentObjectGroup* grp = getDocumentObjectGroupPtr(); - - if (grp->getTypeId().isDerivedFrom(App::DocumentObjectGroupPython::getClassTypeId())) { - DocumentObjectGroupPython* grppy = static_cast(grp); - App::Property* proxy = grppy->getPropertyByName("Proxy"); - if (proxy && proxy->getTypeId() == App::PropertyPythonObject::getClassTypeId()) { - Py::Object vp = static_cast(proxy)->getValue(); - if (vp.hasAttr(std::string("addObject"))) { - Py::Callable method(vp.getAttr(std::string("addObject"))); - // check which this method belongs to to avoid an infinite recursion - if (method.getAttr(std::string("__self__")) != Py::Object(this)) { - Py::Tuple args(1); - args[0] = Py::Object(object); - method.apply(args); - Py_Return; - } - } - } - } - - grp->addObject(docObj->getDocumentObjectPtr()); - Py_Return; -} - -PyObject* DocumentObjectGroupPy::removeObject(PyObject *args) -{ - PyObject *object; - if (!PyArg_ParseTuple(args, "O!", &(DocumentObjectPy::Type), &object)) // convert args: Python->C - return NULL; // NULL triggers exception - - DocumentObjectPy* docObj = static_cast(object); - if (!docObj->getDocumentObjectPtr() || !docObj->getDocumentObjectPtr()->getNameInDocument()) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot remove an invalid object"); - return NULL; - } - if (docObj->getDocumentObjectPtr()->getDocument() != getDocumentObjectGroupPtr()->getDocument()) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot remove an object from another document from this group"); - return NULL; - } - - DocumentObjectGroup* grp = getDocumentObjectGroupPtr(); - - if (grp->getTypeId().isDerivedFrom(App::DocumentObjectGroupPython::getClassTypeId())) { - DocumentObjectGroupPython* grppy = static_cast(grp); - App::Property* proxy = grppy->getPropertyByName("Proxy"); - if (proxy && proxy->getTypeId() == App::PropertyPythonObject::getClassTypeId()) { - Py::Object vp = static_cast(proxy)->getValue(); - if (vp.hasAttr(std::string("removeObject"))) { - Py::Callable method(vp.getAttr(std::string("removeObject"))); - // check which this method belongs to to avoid an infinite recursion - if (method.getAttr(std::string("__self__")) != Py::Object(this)) { - Py::Tuple args(1); - args[0] = Py::Object(object); - method.apply(args); - Py_Return; - } - } - } - } - - grp->removeObject(docObj->getDocumentObjectPtr()); - Py_Return; -} - -PyObject* DocumentObjectGroupPy::removeObjectsFromDocument(PyObject *args) -{ - if (!PyArg_ParseTuple(args, "")) // convert args: Python->C - return NULL; // NULL triggers exception - - getDocumentObjectGroupPtr()->removeObjectsFromDocument(); - Py_Return; -} - -PyObject* DocumentObjectGroupPy::getObject(PyObject *args) -{ - char* pcName; - if (!PyArg_ParseTuple(args, "s", &pcName)) // convert args: Python->C - return NULL; // NULL triggers exception - - DocumentObject* obj = getDocumentObjectGroupPtr()->getObject(pcName); - if ( obj ) { - return obj->getPyObject(); - } else { - Py_Return; - } -} - -PyObject* DocumentObjectGroupPy::hasObject(PyObject *args) -{ - PyObject *object; - PyObject *recursivePy = 0; - int recursive = 0; - if (!PyArg_ParseTuple(args, "O!|O", &(DocumentObjectPy::Type), &object, &recursivePy)) - return NULL; // NULL triggers exception - - DocumentObjectPy* docObj = static_cast(object); - if (!docObj->getDocumentObjectPtr() || !docObj->getDocumentObjectPtr()->getNameInDocument()) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot check an invalid object"); - return NULL; - } - if (docObj->getDocumentObjectPtr()->getDocument() != getDocumentObjectGroupPtr()->getDocument()) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot check an object from another document with this group"); - return NULL; - } - if (recursivePy) { - recursive = PyObject_IsTrue(recursivePy); - if ( recursive == -1) { - // Note: shouldn't happen - PyErr_SetString(PyExc_ValueError, "The recursive parameter should be of boolean type"); - return 0; - } - } - - bool v = getDocumentObjectGroupPtr()->hasObject(docObj->getDocumentObjectPtr(), recursive); - return PyBool_FromLong(v ? 1 : 0); -} - PyObject *DocumentObjectGroupPy::getCustomAttributes(const char* /*attr*/) const { return 0; diff --git a/src/App/DocumentObjectPy.xml b/src/App/DocumentObjectPy.xml index 4d8b2e972f..55a0f57adf 100644 --- a/src/App/DocumentObjectPy.xml +++ b/src/App/DocumentObjectPy.xml @@ -1,13 +1,13 @@ diff --git a/src/App/Extension.cpp b/src/App/Extension.cpp index 7465d3e36f..07ef8af4cd 100644 --- a/src/App/Extension.cpp +++ b/src/App/Extension.cpp @@ -31,29 +31,69 @@ #include "Extension.h" #include "DocumentObject.h" #include "Base/Exception.h" +#include -TYPESYSTEM_SOURCE(App::Extension, App::PropertyContainer); +/* We do not use a standart property macro for type initiation. The reason is that we want to expose all property functions, + * to allow the derived classes to access the private property data, but we do not want to have our + * property data a reference to the parent data. That is because the extension is used in a multi + * inheritance way, and hence our propertydata partent data would point to the same property data + * as any other parent of the inherited class. It makes more sense to create a total unrelated line + * of property datas which are added as additional parent to the extended class. + */ +TYPESYSTEM_SOURCE_P(App::Extension); +const App::PropertyData * App::Extension::getPropertyDataPtr(void){return &propertyData;} +const App::PropertyData & App::Extension::getPropertyData(void) const{return propertyData;} +App::PropertyData App::Extension::propertyData; +void App::Extension::init(void){ + initSubclass(App::Extension::classTypeId, "App::Extension" , "App::PropertyContainer", &(App::Extension::create) ); +} using namespace App; -//************************************************************************** -// Construction/Destruction - -// here the implemataion! description should take place in the header file! -Extension::Extension() {} +Extension::Extension() +{ + +} Extension::~Extension() { } -void Extension::setExtendedObject(DocumentObject* obj) { +void Extension::initExtension(Base::Type type) { + m_extensionType = type; + if(m_extensionType.isBad()) + throw Base::Exception("Extension: Extension type not set"); +} + +void Extension::initExtension(DocumentObject* obj) { + + if(m_extensionType.isBad()) + throw Base::Exception("Extension: Extension type not set"); + + m_base = obj; + m_base->registerExtension( m_extensionType, this ); +} + + +PyObject* Extension::getExtensionPyObject(void) { + + return nullptr; +} + +const char* Extension::name() { + if(m_extensionType.isBad()) throw Base::Exception("Extension::setExtendedObject: Extension type not set"); - m_base = obj; - obj->registerExtension( m_extensionType, this ); + std::string temp(m_extensionType.getName()); + std::string::size_type pos = temp.find_last_of(":"); + + if(pos != std::string::npos) + return temp.substr(pos+1).c_str(); + else + return std::string().c_str(); } @@ -70,12 +110,19 @@ ExtensionContainer::~ExtensionContainer() { void ExtensionContainer::registerExtension(Base::Type extension, Extension* ext) { - if(hasExtension(extension)) - throw Base::Exception("ExtensionContainer::registerExtension: Such a extension is already registered"); - 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; } @@ -94,6 +141,17 @@ bool ExtensionContainer::hasExtension(Base::Type t) const { 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); @@ -109,3 +167,13 @@ Extension* ExtensionContainer::getExtension(Base::Type t) { 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; +} diff --git a/src/App/Extension.h b/src/App/Extension.h index e2a4642b6d..f8875f4974 100644 --- a/src/App/Extension.h +++ b/src/App/Extension.h @@ -28,6 +28,8 @@ #include "PropertyPythonObject.h" #include +#include + namespace App { /** @@ -37,30 +39,53 @@ namespace App { class AppExport Extension : public virtual App::PropertyContainer { - TYPESYSTEM_HEADER(); + //The cass does not have properties itself, but it is important to provide the property access + //functions. see cpp file for details + PROPERTY_HEADER(App::Extension); public: - /** - * A constructor. - * A more elaborate description of the constructor. - */ - Extension(); - /** - * A destructor. - * A more elaborate description of the destructor. - */ + Extension(); virtual ~Extension(); App::DocumentObject* getExtendedObject() {return m_base;}; const App::DocumentObject* getExtendedObject() const {return m_base;}; - void setExtendedObject(App::DocumentObject* obj); + const char* name(); + + virtual PyObject* getExtensionPyObject(void); + protected: + void initExtension(Base::Type type); + void initExtension(App::DocumentObject* obj); + Py::Object ExtensionPythonObject; + +private: Base::Type m_extensionType; - App::DocumentObject* m_base; + App::DocumentObject* m_base = nullptr; }; + + +#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 { @@ -83,26 +108,25 @@ class AppExport ExtensionContainer : public virtual App::PropertyContainer TYPESYSTEM_HEADER(); public: - /** - * A constructor. - * A more elaborate description of the constructor. - */ - ExtensionContainer(); + + typedef std::map::iterator ExtensionIterator; - /** - * A destructor. - * A more elaborate description of the destructor. - */ + 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 Extension* getExtensionByType() { return dynamic_cast(getExtension(Extension::getClassTypeId())); }; + ExtensionIterator extensionBegin() {return _extensions.begin();}; + ExtensionIterator extensionEnd() {return _extensions.end();}; + private: //stored extensions std::map _extensions; diff --git a/src/App/ExtensionContainerPy.xml b/src/App/ExtensionContainerPy.xml new file mode 100644 index 0000000000..209a7743ad --- /dev/null +++ b/src/App/ExtensionContainerPy.xml @@ -0,0 +1,20 @@ + + + + + + Base class for all objects which can be extended + + + + diff --git a/src/App/ExtensionContainerPyImp.cpp b/src/App/ExtensionContainerPyImp.cpp new file mode 100644 index 0000000000..5e15b42a3b --- /dev/null +++ b/src/App/ExtensionContainerPyImp.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * 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 +#endif + +#include "Application.h" + +// inclution of the generated files (generated out of PropertyContainerPy.xml) +#include "ExtensionContainerPy.h" +#include "ExtensionContainerPy.cpp" + +using namespace App; + +// returns a string which represent the object e.g. when printed in python +std::string ExtensionContainerPy::representation(void) const +{ + return std::string(""); +} + +int ExtensionContainerPy::initialisation() { + + if (this->ob_type->tp_dict == NULL) { + if (PyType_Ready(this->ob_type) < 0) + return 0; + } + + ExtensionContainer::ExtensionIterator it = this->getExtensionContainerPtr()->extensionBegin(); + for(; it != this->getExtensionContainerPtr()->extensionEnd(); ++it) { + + PyObject* obj = (*it).second->getExtensionPyObject(); + PyMethodDef* tmpptr = (PyMethodDef*)obj->ob_type->tp_methods; + while(tmpptr->ml_name) { + //Note: to add methods the call to PyMethod_New is required. However, than the PyObject + // self is added to the functions arguments list. FreeCAD py implementations are not + // made to handle this, the do not accept self as argument. Hence we only use function + PyObject *func = PyCFunction_New(tmpptr,obj); + //PyObject *method = PyMethod_New(func, (PyObject*)this, PyObject_Type((PyObject*)this)); + PyDict_SetItem(this->ob_type->tp_dict, PyString_FromString(tmpptr->ml_name), func); + Py_DECREF(func); + //Py_DECREF(method); + ++tmpptr; + } + } + return 0; +} + +PyObject* ExtensionContainerPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + // create a new instance of @self.export.Name@ and the Twin object + Base::Console().Message("Make\n"); + return 0; +} + +// constructor method +int ExtensionContainerPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + Base::Console().Message("Init\n"); + return 0; +} + +PyObject *ExtensionContainerPy::getCustomAttributes(const char* attr) const +{ + return 0; +} + +int ExtensionContainerPy::setCustomAttributes(const char* attr, PyObject *obj) +{ + return 0; +} diff --git a/src/App/ExtensionPy.xml b/src/App/ExtensionPy.xml new file mode 100644 index 0000000000..8bc059ce78 --- /dev/null +++ b/src/App/ExtensionPy.xml @@ -0,0 +1,18 @@ + + + + + + Base class for all document object extensions + + + + diff --git a/src/App/ExtensionPyImp.cpp b/src/App/ExtensionPyImp.cpp new file mode 100644 index 0000000000..323a430c7e --- /dev/null +++ b/src/App/ExtensionPyImp.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** + * 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 +#endif + +#include "Application.h" + +// inclution of the generated files (generated out of PropertyContainerPy.xml) +#include "ExtensionPy.h" +#include "ExtensionPy.cpp" + +using namespace App; + +// returns a string which represent the object e.g. when printed in python +std::string ExtensionPy::representation(void) const +{ + return std::string(""); +} + +PyObject *ExtensionPy::getCustomAttributes(const char* attr) const +{ + return 0; +} + +int ExtensionPy::setCustomAttributes(const char* attr, PyObject *obj) +{ + return 0; +} diff --git a/src/App/GeoFeatureGroup.cpp b/src/App/GeoFeatureGroup.cpp index c1229be6ff..4db4da2287 100644 --- a/src/App/GeoFeatureGroup.cpp +++ b/src/App/GeoFeatureGroup.cpp @@ -45,7 +45,7 @@ PROPERTY_SOURCE(App::GeoFeatureGroupExtension, App::GroupExtension) GeoFeatureGroupExtension::GeoFeatureGroupExtension(void) { - m_extensionType = GeoFeatureGroupExtension::getClassTypeId(); + initExtension(GeoFeatureGroupExtension::getClassTypeId()); ADD_PROPERTY(Placement,(Base::Placement())); } diff --git a/src/App/OriginGroup.cpp b/src/App/OriginGroup.cpp index b7106b7a68..a04498e414 100644 --- a/src/App/OriginGroup.cpp +++ b/src/App/OriginGroup.cpp @@ -39,7 +39,7 @@ PROPERTY_SOURCE(App::OriginGroupExtension, App::GeoFeatureGroupExtension); OriginGroupExtension::OriginGroupExtension () { - m_extensionType = OriginGroupExtension::getClassTypeId(); + initExtension(OriginGroupExtension::getClassTypeId()); ADD_PROPERTY_TYPE ( Origin, (0), 0, App::Prop_Hidden, "Origin linked to the group" ); } diff --git a/src/App/Part.cpp b/src/App/Part.cpp index 3585b8af41..656e49e116 100644 --- a/src/App/Part.cpp +++ b/src/App/Part.cpp @@ -35,7 +35,7 @@ using namespace App; -PROPERTY_SOURCE(App::Part, App::DocumentObject) +PROPERTY_SOURCE_WITH_EXTENSIONS(App::Part, App::DocumentObject, (App::OriginGroupExtension)) //=========================================================================== @@ -60,7 +60,7 @@ Part::Part(void) // color and apperance ADD_PROPERTY(Color, (1.0, 1.0, 1.0, 1.0)); // set transparent -> not used - setExtendedObject(this); + GroupExtension::initExtension(this); } Part::~Part(void) diff --git a/src/App/Part.h b/src/App/Part.h index f59899ed73..2b3f12a31d 100644 --- a/src/App/Part.h +++ b/src/App/Part.h @@ -37,7 +37,7 @@ namespace App */ class AppExport Part : public App::DocumentObject, public App::OriginGroupExtension { - PROPERTY_HEADER(App::Part); + PROPERTY_HEADER_WITH_EXTENSIONS(App::Part); public: /// type of the part diff --git a/src/App/PropertyContainer.cpp b/src/App/PropertyContainer.cpp index 5dea4b36bb..3d4353a864 100644 --- a/src/App/PropertyContainer.cpp +++ b/src/App/PropertyContainer.cpp @@ -51,7 +51,6 @@ TYPESYSTEM_SOURCE(App::PropertyContainer,Base::Persistence); // here the implemataion! description should take place in the header file! PropertyContainer::PropertyContainer() { - propertyData.parentPropertyData = 0; } PropertyContainer::~PropertyContainer() @@ -279,14 +278,24 @@ void PropertyData::addProperty(const PropertyContainer *container,const char* Pr } } +void PropertyData::addParentPropertyData(const PropertyData* data) { + + if(data) + parentPropertyData.push_back(data); +} + + const PropertyData::PropertySpec *PropertyData::findProperty(const PropertyContainer *container,const char* PropName) const { for (vector::const_iterator It = propertyData.begin(); It != propertyData.end(); ++It) if(strcmp(It->Name,PropName)==0) return &(*It); - if(parentPropertyData) - return parentPropertyData->findProperty(container,PropName); + for(auto data : parentPropertyData) { + auto res = data->findProperty(container,PropName); + if(res) + return res; + } return 0; } @@ -299,9 +308,12 @@ const PropertyData::PropertySpec *PropertyData::findProperty(const PropertyConta if(diff == It->Offset) return &(*It); - if(parentPropertyData) - return parentPropertyData->findProperty(container,prop); - + for(auto data : parentPropertyData) { + auto res = data->findProperty(container,prop); + if(res) + return res; + } + return 0; } @@ -450,9 +462,9 @@ void PropertyData::getPropertyMap(const PropertyContainer *container,std::mapgetPropertyMap(container,Map); - + for(auto data : parentPropertyData) + data->getPropertyMap(container,Map); + } void PropertyData::getPropertyList(const PropertyContainer *container,std::vector &List) const @@ -466,8 +478,8 @@ void PropertyData::getPropertyList(const PropertyContainer *container,std::vecto { List.push_back((Property *) (pos->second.Offset + (char *)container) ); }*/ - if(parentPropertyData) - parentPropertyData->getPropertyList(container,List); + for(auto data : parentPropertyData) + data->getPropertyList(container,List); } diff --git a/src/App/PropertyContainer.h b/src/App/PropertyContainer.h index b458865fdf..512680ec92 100644 --- a/src/App/PropertyContainer.h +++ b/src/App/PropertyContainer.h @@ -57,11 +57,12 @@ struct AppExport PropertyData short Offset,Type; }; // vector of all properties - std::vector propertyData; - const PropertyData *parentPropertyData; + std::vector propertyData; + std::vector parentPropertyData; void addProperty(const PropertyContainer *container,const char* PropName, Property *Prop, const char* PropertyGroup= 0, PropertyType = Prop_None, const char* PropertyDocu= 0 ); - + void addParentPropertyData(const PropertyData* data); + const PropertySpec *findProperty(const PropertyContainer *container,const char* PropName) const; const PropertySpec *findProperty(const PropertyContainer *container,const Property* prop) const; @@ -218,7 +219,7 @@ const App::PropertyData & _class_::getPropertyData(void) const{return propertyDa App::PropertyData _class_::propertyData; \ void _class_::init(void){\ initSubclass(_class_::classTypeId, #_class_ , #_parentclass_, &(_class_::create) ); \ - _class_::propertyData.parentPropertyData = _parentclass_::getPropertyDataPtr();\ + _class_::propertyData.addParentPropertyData(_parentclass_::getPropertyDataPtr());\ } #define PROPERTY_SOURCE_ABSTRACT(_class_, _parentclass_) \ @@ -228,7 +229,7 @@ const App::PropertyData & _class_::getPropertyData(void) const{return propertyDa App::PropertyData _class_::propertyData; \ void _class_::init(void){\ initSubclass(_class_::classTypeId, #_class_ , #_parentclass_, &(_class_::create) ); \ - _class_::propertyData.parentPropertyData = _parentclass_::getPropertyDataPtr();\ + _class_::propertyData.addParentPropertyData(_parentclass_::getPropertyDataPtr());\ } #define TYPESYSTEM_SOURCE_TEMPLATE(_class_) \ @@ -246,7 +247,7 @@ template<> const App::PropertyData * _class_::getPropertyDataPtr(void){return &p template<> const App::PropertyData & _class_::getPropertyData(void) const{return propertyData;} \ template<> void _class_::init(void){\ initSubclass(_class_::classTypeId, #_class_ , #_parentclass_, &(_class_::create) ); \ - _class_::propertyData.parentPropertyData = _parentclass_::getPropertyDataPtr();\ + _class_::propertyData.addParentPropertyData(_parentclass_::getPropertyDataPtr());\ } diff --git a/src/App/PropertyContainerPyImp.cpp b/src/App/PropertyContainerPyImp.cpp index 3fed9a4fa1..63db3001f5 100644 --- a/src/App/PropertyContainerPyImp.cpp +++ b/src/App/PropertyContainerPyImp.cpp @@ -243,8 +243,10 @@ PyObject *PropertyContainerPy::getCustomAttributes(const char* attr) const getPropertyContainerPtr()->getPropertyMap(Map); PyObject *dict = PyDict_New(); if (dict) { - for ( std::map::iterator it = Map.begin(); it != Map.end(); ++it ) + for ( std::map::iterator it = Map.begin(); it != Map.end(); ++it ){ + Base::Console().Message("Dict add property: %s\n",it->first.c_str()); PyDict_SetItem(dict, PyString_FromString(it->first.c_str()), PyString_FromString("")); + } if (PyErr_Occurred()) { Py_DECREF(dict); dict = NULL; diff --git a/src/Tools/generateBase/generateModel_Module.py b/src/Tools/generateBase/generateModel_Module.py index e256e44d02..c067a4ece6 100644 --- a/src/Tools/generateBase/generateModel_Module.py +++ b/src/Tools/generateBase/generateModel_Module.py @@ -216,7 +216,7 @@ class GenerateModel: class PythonExport: subclass = None - def __init__(self, FatherNamespace='', RichCompare=0, Name='', Reference=0, FatherInclude='', Father='', Namespace='', Twin='', Constructor=0, TwinPointer='', Include='', NumberProtocol=0, Delete=0, Documentation=None, Methode=None, Attribute=None, Sequence=None, CustomAttributes='', ClassDeclarations=''): + def __init__(self, FatherNamespace='', RichCompare=0, Name='', Reference=0, FatherInclude='', Father='', Namespace='', Twin='', Constructor=0, TwinPointer='', Include='', NumberProtocol=0, Delete=0, Documentation=None, Methode=None, Attribute=None, Sequence=None, CustomAttributes='', ClassDeclarations='', Initialisation=0): self.FatherNamespace = FatherNamespace self.RichCompare = RichCompare self.Name = Name @@ -231,6 +231,7 @@ class PythonExport: self.NumberProtocol = NumberProtocol self.Delete = Delete self.Documentation = Documentation + self.Initialisation = Initialisation if Methode is None: self.Methode = [] else: @@ -248,6 +249,8 @@ class PythonExport: else: return PythonExport(*args_, **kwargs_) factory = staticmethod(factory) + def getInitialisation(self): return self.Initialisation + def setInitialisation(self, Initialisation): self.Initialisation = Initialisation def getDocumentation(self): return self.Documentation def setDocumentation(self, Documentation): self.Documentation = Documentation def getMethode(self): return self.Methode @@ -311,6 +314,8 @@ class PythonExport: outfile.write(' Twin="%s"' % (self.getTwin(), )) if self.getConstructor() is not None: outfile.write(' Constructor="%s"' % (self.getConstructor(), )) + if self.getInitialisation() is not None: + outfile.write(' Initialisation="%s"' % (self.getInitialisation(), )) outfile.write(' TwinPointer="%s"' % (self.getTwinpointer(), )) outfile.write(' Include="%s"' % (self.getInclude(), )) if self.getNumberprotocol() is not None: @@ -354,6 +359,7 @@ class PythonExport: showIndent(outfile, level) outfile.write('Constructor = "%s",\n' % (self.getConstructor(),)) showIndent(outfile, level) + outfile.write('Initialisation = "%s",\n' % (self.getInitialisation(),)) outfile.write('TwinPointer = "%s",\n' % (self.getTwinpointer(),)) showIndent(outfile, level) outfile.write('Include = "%s",\n' % (self.getInclude(),)) @@ -442,6 +448,13 @@ class PythonExport: self.Constructor = 0 else: raise ValueError('Bad boolean attribute (Constructor)') + if attrs.get('Initialisation'): + if attrs.get('Initialisation').value in ('true', '1'): + self.Initialisation = 1 + elif attrs.get('Initialisation').value in ('false', '0'): + self.Initialisation = 0 + else: + raise ValueError('Bad boolean attribute (Initialisation)') if attrs.get('TwinPointer'): self.TwinPointer = attrs.get('TwinPointer').value if attrs.get('Include'): @@ -493,6 +506,11 @@ class PythonExport: for text__content_ in child_.childNodes: ClassDeclarations_ += text__content_.nodeValue self.ClassDeclarations = ClassDeclarations_ + elif child_.nodeType == Node.ELEMENT_NODE and \ + nodeName_ == 'Initialisation': + obj_ = Documentation.factory() + obj_.build(child_) + self.setDocumentation(obj_) # end class PythonExport @@ -1827,6 +1845,14 @@ class SaxGeneratemodelHandler(handler.ContentHandler): obj.setConstructor(0) else: self.reportError('"Constructor" attribute must be boolean ("true", "1", "false", "0")') + val = attrs.get('Initialisation', None) + if val is not None: + if val in ('true', '1'): + obj.setInitialisation(1) + elif val in ('false', '0'): + obj.setInitialisation(0) + else: + self.reportError('"Initialisation" attribute must be boolean ("true", "1", "false", "0")') val = attrs.get('TwinPointer', None) if val is not None: obj.setTwinpointer(val) diff --git a/src/Tools/generateTemplates/templateClassPyExport.py b/src/Tools/generateTemplates/templateClassPyExport.py index 292ca1eb63..e2240d1345 100644 --- a/src/Tools/generateTemplates/templateClassPyExport.py +++ b/src/Tools/generateTemplates/templateClassPyExport.py @@ -67,6 +67,10 @@ public: static PyObject *PyMake(struct _typeobject *, PyObject *, PyObject *); virtual int PyInit(PyObject* args, PyObject*k); ~@self.export.Name@(); + ++ if (self.export.Initialisation): + int initialisation(); +- typedef @self.export.TwinPointer@* PointerType ; @@ -606,6 +610,10 @@ int @self.export.Name@::staticCallback_set@i.Name@ (PyObject *self, PyObject *va + if (self.export.Reference): pcObject->ref(); - + ++ if (self.export.Initialisation): + initialisation(); +- } + if not (self.export.Constructor): @@ -808,6 +816,13 @@ int @self.export.Name@::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) } - ++ if (self.export.Initialisation): +int @self.export.Name@::initialisation() +{ + return 0; +} +- + // returns a string which represents the object e.g. when printed in python std::string @self.export.Name@::representation(void) const { @@ -1117,6 +1132,13 @@ int @self.export.Name@::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) return 0; } - ++ if (self.export.Initialisation): +int @self.export.Name@::initialisation() +{ + PyErr_SetString(PyExc_NotImplementedError, "Not yet implemented"); + return 0; +} +- + for i in self.export.Methode: