Extensions: Make Python Integration work
This commit is contained in:
parent
93222098f0
commit
7bcb6519cc
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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>;
|
||||
}
|
|
@ -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
|
||||
|
|
296
src/App/ExtensionContainer.cpp
Normal file
296
src/App/ExtensionContainer.cpp
Normal 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);
|
||||
}
|
129
src/App/ExtensionContainer.h
Normal file
129
src/App/ExtensionContainer.h
Normal 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
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>>;
|
||||
}
|
|
@ -75,7 +75,8 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
typedef App::ExtensionPython<GeoFeatureGroupExtension> GeoFeatureGroupExtensionPython;
|
||||
typedef ExtensionPythonT<GroupExtensionPythonT<GeoFeatureGroupExtension>> GeoFeatureGroupExtensionPython;
|
||||
|
||||
|
||||
} //namespace App
|
||||
|
||||
|
|
|
@ -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>>;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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>>;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#ifndef APP_TRANSACTIONALOBJECT_H
|
||||
#define APP_TRANSACTIONALOBJECT_H
|
||||
|
||||
#include <App/Extension.h>
|
||||
#include <App/ExtensionContainer.h>
|
||||
|
||||
namespace App
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user