Extensions: Make Python Integration work

This commit is contained in:
Stefan Tröger 2016-06-03 19:19:54 +02:00 committed by wmayer
parent 93222098f0
commit 7bcb6519cc
19 changed files with 622 additions and 369 deletions

View File

@ -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();

View File

@ -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

View File

@ -24,11 +24,11 @@
#ifndef APP_DOCUMENTOBJECT_H
#define APP_DOCUMENTOBJECT_H
<<<<<<< 51e23d854e59ab0a23a9ea9b19e74e5f02753d82
<<<<<<< 75964436541d26c663d0ad51ae81c5ab359a539c
#include <App/TransactionalObject.h>
=======
#include <App/Extension.h>
>>>>>>> Introduce extensions and port App groups
#include <App/ExtensionContainer.h>
>>>>>>> Extensions: Make Python Integration work
#include <App/PropertyStandard.h>
#include <App/PropertyLinks.h>
#include <App/PropertyExpressionEngine.h>
@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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<Extension*> 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<Extension>;
}

View File

@ -26,11 +26,9 @@
#include "PropertyContainer.h"
#include "PropertyPythonObject.h"
#include "DynamicProperty.h"
#include "Base/Interpreter.h"
#include <CXX/Objects.hxx>
#include <boost/preprocessor/seq/for_each.hpp>
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 ExtensionT>
class ExtensionPythonT : public ExtensionT
{
PROPERTY_HEADER(App::ExtensionPythonT<ExtensionT>);
#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<typename ExtensionT>
class ExtensionPython : public ExtensionT {
PROPERTY_HEADER(App::ExtensionPython<ExtensionT>);
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<App::Extension> 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<PropertyPythonObject*>(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<Base::Type, App::Extension*>::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<typename ExtensionT>
ExtensionT* getExtensionByType() {
return dynamic_cast<ExtensionT*>(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<Extension*> getExtensionsDerivedFrom(Base::Type type) const;
template<typename ExtensionT>
std::vector<ExtensionT*> getExtensionsDerivedFromType() const {
auto vec = getExtensionsDerivedFrom(ExtensionT::getClassTypeId());
std::vector<ExtensionT*> typevec;
for(auto ext : vec)
typevec.push_back(dynamic_cast<ExtensionT*>(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<std::string,Property*> &Map) const override;
/// get all properties of the class (including properties of the parent)
virtual void getPropertyList(std::vector<Property*> &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<Base::Type, App::Extension*> _extensions;
};
} //App
}; //App
#endif // APP_EXTENSION_H

View File

@ -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 <cassert>
# include <algorithm>
#endif
#include "Extension.h"
#include "DocumentObject.h"
#include "Base/Exception.h"
#include <Base/Console.h>
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<Extension*> 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<const PropertyPythonObject*>(prop)->getValue());
}*/
App::PropertyContainer::onChanged(prop);
}

View File

@ -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 <CXX/Objects.hxx>
#include <boost/preprocessor/seq/for_each.hpp>
namespace App {
class AppExport ExtensionContainer : public virtual App::PropertyContainer
{
TYPESYSTEM_HEADER();
public:
typedef std::map<Base::Type, App::Extension*>::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<typename ExtensionT>
ExtensionT* getExtensionByType() {
return dynamic_cast<ExtensionT*>(getExtension(ExtensionT::getClassTypeId()));
};
//get all extensions which have the given base class
std::vector<Extension*> getExtensionsDerivedFrom(Base::Type type) const;
template<typename ExtensionT>
std::vector<ExtensionT*> getExtensionsDerivedFromType() const {
auto vec = getExtensionsDerivedFrom(ExtensionT::getClassTypeId());
std::vector<ExtensionT*> typevec;
for(auto ext : vec)
typevec.push_back(dynamic_cast<ExtensionT*>(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<std::string,Property*> &Map) const override;
/// get all properties of the class (including properties of the parent)
virtual void getPropertyList(std::vector<Property*> &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<Base::Type, App::Extension*> _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

View File

@ -17,7 +17,8 @@
</Documentation>
<Methode Name="addExtension">
<Documentation>
<UserDocu>Adds an extension to the object</UserDocu>
<UserDocu>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)</UserDocu>
</Documentation>
</Methode>
<Methode Name="hasExtension">

View File

@ -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<App::Extension*>(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<App::DocumentObject*>(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<PropertyPythonObject*>(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;

View File

@ -147,6 +147,9 @@ DocumentObject* GeoFeatureGroupExtension::getGroupOfObject(const DocumentObject*
// Python feature ---------------------------------------------------------
// explicit template instantiation
//template class AppExport DocumentObjectExtensionPython<App::GeoFeatureGroupExtension>;
namespace App {
PROPERTY_SOURCE_TEMPLATE(App::GeoFeatureGroupExtensionPython, App::GeoFeatureGroupExtension)
// explicit template instantiation
template class AppExport ExtensionPythonT<GroupExtensionPythonT<GeoFeatureGroupExtension>>;
}

View File

@ -75,7 +75,8 @@ public:
}
};
typedef App::ExtensionPython<GeoFeatureGroupExtension> GeoFeatureGroupExtensionPython;
typedef ExtensionPythonT<GroupExtensionPythonT<GeoFeatureGroupExtension>> GeoFeatureGroupExtensionPython;
} //namespace App

View File

@ -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<DocumentObject*> 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<GroupExtensionPythonT<GroupExtension>>;
}

View File

@ -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<typename ExtensionT>
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<GroupExtensionPythonT<GroupExtension>> GroupExtensionPython;
} //namespace App

View File

@ -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<GroupExtensionPythonT<OriginGroupExtension>>;
}

View File

@ -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<GroupExtensionPythonT<OriginGroupExtension>> OriginGroupExtensionPython;
} /* App */
#endif /* end of include guard: ORIGINGROUP_H_QHTU73IF */

View File

@ -24,7 +24,7 @@
#ifndef APP_TRANSACTIONALOBJECT_H
#define APP_TRANSACTIONALOBJECT_H
#include <App/Extension.h>
#include <App/ExtensionContainer.h>
namespace App
{

View File

@ -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