Python feature/observer related changes
* Add new API and signal handler in document observer * Pre initialize python handler function to improve performance. In case Python code use dynamic patching, i.e. add class method at runtime (which is rare and should be discouraged), the python feature can be re-initialized by simply assign proeprty Proxy again. * Add property tracking in DocumentObjectT * WidgetFactory adds support for accepting python QIcon, which is used by ViewProviderPythonFeature
This commit is contained in:
@@ -30,15 +30,16 @@
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include <Base/Reader.h>
|
||||
#include <Base/MatrixPy.h>
|
||||
#include <Base/Tools.h>
|
||||
|
||||
#include <App/DocumentObjectPy.h>
|
||||
#include "FeaturePython.h"
|
||||
#include "FeaturePythonPyImp.h"
|
||||
|
||||
using namespace App;
|
||||
|
||||
FeaturePythonImp::FeaturePythonImp(App::DocumentObject* o) : object(o)
|
||||
FeaturePythonImp::FeaturePythonImp(App::DocumentObject* o)
|
||||
: object(o), has__object__(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -46,43 +47,40 @@ FeaturePythonImp::~FeaturePythonImp()
|
||||
{
|
||||
}
|
||||
|
||||
void FeaturePythonImp::init(PyObject *pyobj) {
|
||||
Base::PyGILStateLocker lock;
|
||||
has__object__ = !!PyObject_HasAttrString(pyobj, "__object__");
|
||||
|
||||
#undef FC_PY_ELEMENT
|
||||
#define FC_PY_ELEMENT(_name) FC_PY_ELEMENT_INIT(_name)
|
||||
|
||||
FC_PY_FEATURE_PYTHON
|
||||
}
|
||||
|
||||
#define FC_PY_CALL_CHECK(_name) _FC_PY_CALL_CHECK(_name,return(false))
|
||||
|
||||
/*!
|
||||
Calls the execute() method of the Python feature class. If the Python feature class doesn't have an execute()
|
||||
method or if it returns False this method also return false and true otherwise.
|
||||
*/
|
||||
bool FeaturePythonImp::execute()
|
||||
{
|
||||
// avoid recursive calls of execute()
|
||||
if (object->testStatus(App::PythonCall))
|
||||
return false;
|
||||
|
||||
// Run the execute method of the proxy object.
|
||||
FC_PY_CALL_CHECK(execute)
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Property* proxy = object->getPropertyByName("Proxy");
|
||||
if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {
|
||||
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
|
||||
if (feature.hasAttr(std::string("execute"))) {
|
||||
if (feature.hasAttr("__object__")) {
|
||||
Base::ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::PythonCall, object);
|
||||
Py::Callable method(feature.getAttr(std::string("execute")));
|
||||
Py::Tuple args;
|
||||
Py::Object res = method.apply(args);
|
||||
if (res.isBoolean() && !res.isTrue())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Base::ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::PythonCall, object);
|
||||
Py::Callable method(feature.getAttr(std::string("execute")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Object res = method.apply(args);
|
||||
if (res.isBoolean() && !res.isTrue())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (has__object__) {
|
||||
Py::Object res = Base::pyCall(py_execute.ptr());
|
||||
if (res.isBoolean() && !res.isTrue())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Object res = Base::pyCall(py_execute.ptr(),args.ptr());
|
||||
if (res.isBoolean() && !res.isTrue())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -96,35 +94,51 @@ bool FeaturePythonImp::execute()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::mustExecute() const
|
||||
{
|
||||
FC_PY_CALL_CHECK(mustExecute)
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
if (has__object__) {
|
||||
Py::Object res(Base::pyCall(py_mustExecute.ptr()));
|
||||
return res.isTrue();
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Object res(Base::pyCall(py_mustExecute.ptr(),args.ptr()));
|
||||
return res.isTrue();
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void FeaturePythonImp::onBeforeChange(const Property* prop)
|
||||
{
|
||||
if(py_onBeforeChange.isNone())
|
||||
return;
|
||||
|
||||
// Run the execute method of the proxy object.
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Property* proxy = object->getPropertyByName("Proxy");
|
||||
if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {
|
||||
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
|
||||
if (feature.hasAttr(std::string("onBeforeChange"))) {
|
||||
if (feature.hasAttr("__object__")) {
|
||||
Py::Callable method(feature.getAttr(std::string("onBeforeChange")));
|
||||
Py::Tuple args(1);
|
||||
const char* prop_name = object->getPropertyName(prop);
|
||||
if (prop_name) {
|
||||
args.setItem(0, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Py::Callable method(feature.getAttr(std::string("onBeforeChange")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
const char* prop_name = object->getPropertyName(prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
const char *prop_name = object->getPropertyName(prop);
|
||||
if(prop_name == 0)
|
||||
return;
|
||||
if (has__object__) {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(prop_name));
|
||||
Base::pyCall(py_onBeforeChange.ptr(),args.ptr());
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(py_onBeforeChange.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -133,35 +147,52 @@ void FeaturePythonImp::onBeforeChange(const Property* prop)
|
||||
}
|
||||
}
|
||||
|
||||
void FeaturePythonImp::onChanged(const Property* prop)
|
||||
bool FeaturePythonImp::onBeforeChangeLabel(std::string &newLabel)
|
||||
{
|
||||
if(py_onBeforeChangeLabel.isNone())
|
||||
return false;
|
||||
|
||||
// Run the execute method of the proxy object.
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Property* proxy = object->getPropertyByName("Proxy");
|
||||
if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {
|
||||
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
|
||||
if (feature.hasAttr(std::string("onChanged"))) {
|
||||
if (feature.hasAttr("__object__")) {
|
||||
Py::Callable method(feature.getAttr(std::string("onChanged")));
|
||||
Py::Tuple args(1);
|
||||
const char* prop_name = object->getPropertyName(prop);
|
||||
if (prop_name) {
|
||||
args.setItem(0, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Py::Callable method(feature.getAttr(std::string("onChanged")));
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
const char* prop_name = object->getPropertyName(prop);
|
||||
if (prop_name) {
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::String(newLabel));
|
||||
Py::Object ret(Base::pyCall(py_onBeforeChangeLabel.ptr(),args.ptr()));
|
||||
if(!ret.isNone()) {
|
||||
if(!ret.isString())
|
||||
throw Base::TypeError("onBeforeChangeLabel expects to return a string");
|
||||
newLabel = ret.as_string();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FeaturePythonImp::onChanged(const Property* prop)
|
||||
{
|
||||
if(py_onChanged.isNone())
|
||||
return;
|
||||
// Run the execute method of the proxy object.
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
const char *prop_name = object->getPropertyName(prop);
|
||||
if(prop_name == 0)
|
||||
return;
|
||||
if (has__object__) {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::String(prop_name));
|
||||
Base::pyCall(py_onChanged.ptr(),args.ptr());
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1, Py::String(prop_name));
|
||||
Base::pyCall(py_onChanged.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -172,25 +203,18 @@ void FeaturePythonImp::onChanged(const Property* prop)
|
||||
|
||||
void FeaturePythonImp::onDocumentRestored()
|
||||
{
|
||||
_FC_PY_CALL_CHECK(onDocumentRestored,return);
|
||||
|
||||
// Run the execute method of the proxy object.
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Property* proxy = object->getPropertyByName("Proxy");
|
||||
if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {
|
||||
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
|
||||
if (feature.hasAttr(std::string("onDocumentRestored"))) {
|
||||
if (feature.hasAttr("__object__")) {
|
||||
Py::Callable method(feature.getAttr(std::string("onDocumentRestored")));
|
||||
Py::Tuple args;
|
||||
method.apply(args);
|
||||
}
|
||||
else {
|
||||
Py::Callable method(feature.getAttr(std::string("onDocumentRestored")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
method.apply(args);
|
||||
}
|
||||
}
|
||||
if (has__object__) {
|
||||
Base::pyCall(py_onDocumentRestored.ptr());
|
||||
}
|
||||
else {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Base::pyCall(py_onDocumentRestored.ptr(),args.ptr());
|
||||
}
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
@@ -199,12 +223,283 @@ void FeaturePythonImp::onDocumentRestored()
|
||||
}
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::getSubObject(DocumentObject *&ret, const char *subname,
|
||||
PyObject **pyObj, Base::Matrix4D *_mat, bool transform, int depth) const
|
||||
{
|
||||
FC_PY_CALL_CHECK(getSubObject);
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(6);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
if(!subname) subname = "";
|
||||
args.setItem(1,Py::String(subname));
|
||||
args.setItem(2,Py::Int(pyObj?2:1));
|
||||
Base::MatrixPy *pyMat = new Base::MatrixPy(new Base::Matrix4D);
|
||||
if(_mat) *pyMat->getMatrixPtr() = *_mat;
|
||||
args.setItem(3,Py::Object(pyMat));
|
||||
args.setItem(4,Py::Boolean(transform));
|
||||
args.setItem(5,Py::Int(depth));
|
||||
|
||||
Py::Object res(Base::pyCall(py_getSubObject.ptr(),args.ptr()));
|
||||
if(res.isNone()) {
|
||||
ret = 0;
|
||||
return true;
|
||||
}
|
||||
if(!res.isTrue())
|
||||
return false;
|
||||
if(!res.isSequence())
|
||||
throw Base::TypeError("getSubObject expects return type of tuple");
|
||||
Py::Sequence seq(res);
|
||||
if(seq.length() < 2 ||
|
||||
(!seq.getItem(0).isNone() &&
|
||||
!PyObject_TypeCheck(seq.getItem(0).ptr(),&DocumentObjectPy::Type)) ||
|
||||
!PyObject_TypeCheck(seq.getItem(1).ptr(),&Base::MatrixPy::Type))
|
||||
{
|
||||
throw Base::TypeError("getSubObject expects return type of (obj,matrix,pyobj)");
|
||||
}
|
||||
if(_mat)
|
||||
*_mat = *static_cast<Base::MatrixPy*>(seq.getItem(1).ptr())->getMatrixPtr();
|
||||
if(pyObj) {
|
||||
if(seq.length()>2)
|
||||
*pyObj = Py::new_reference_to(seq.getItem(2));
|
||||
else
|
||||
*pyObj = Py::new_reference_to(Py::None());
|
||||
}
|
||||
if(seq.getItem(0).isNone())
|
||||
ret = 0;
|
||||
else
|
||||
ret = static_cast<DocumentObjectPy*>(seq.getItem(0).ptr())->getDocumentObjectPtr();
|
||||
return true;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
ret = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::getSubObjects(std::vector<std::string> &ret, int reason) const {
|
||||
FC_PY_CALL_CHECK(getSubObjects);
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1, Py::Int(reason));
|
||||
Py::Object res(Base::pyCall(py_getSubObjects.ptr(),args.ptr()));
|
||||
if(!res.isTrue())
|
||||
return true;
|
||||
if(!res.isSequence())
|
||||
throw Base::TypeError("getSubObjects expects return type of tuple");
|
||||
Py::Sequence seq(res);
|
||||
for(size_t i=0;i<seq.length();++i) {
|
||||
Py::Object name(seq[i].ptr());
|
||||
if(!name.isString())
|
||||
throw Base::TypeError("getSubObjects expects string in returned sequence");
|
||||
ret.push_back(name.as_string());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::getLinkedObject(DocumentObject *&ret, bool recurse,
|
||||
Base::Matrix4D *_mat, bool transform, int depth) const
|
||||
{
|
||||
FC_PY_CALL_CHECK(getLinkedObject);
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(5);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::Boolean(recurse));
|
||||
Base::MatrixPy *pyMat = new Base::MatrixPy(new Base::Matrix4D);
|
||||
if(_mat) *pyMat->getMatrixPtr() = *_mat;
|
||||
args.setItem(2,Py::Object(pyMat));
|
||||
args.setItem(3,Py::Boolean(transform));
|
||||
args.setItem(4,Py::Int(depth));
|
||||
|
||||
Py::Object res(Base::pyCall(py_getLinkedObject.ptr(),args.ptr()));
|
||||
if(!res.isTrue()) {
|
||||
ret = object;
|
||||
return true;
|
||||
}
|
||||
if(!res.isSequence())
|
||||
throw Base::TypeError("getLinkedObject expects return type of (object,matrix)");
|
||||
Py::Sequence seq(res);
|
||||
if(seq.length() != 2 ||
|
||||
(!seq.getItem(0).isNone() &&
|
||||
!PyObject_TypeCheck(seq.getItem(0).ptr(),&DocumentObjectPy::Type)) ||
|
||||
!PyObject_TypeCheck(seq.getItem(1).ptr(),&Base::MatrixPy::Type))
|
||||
{
|
||||
throw Base::TypeError("getLinkedObject expects return type of (object,matrix)");
|
||||
}
|
||||
if(_mat)
|
||||
*_mat = *static_cast<Base::MatrixPy*>(seq.getItem(1).ptr())->getMatrixPtr();
|
||||
if(seq.getItem(0).isNone())
|
||||
ret = object;
|
||||
else
|
||||
ret = static_cast<DocumentObjectPy*>(seq.getItem(0).ptr())->getDocumentObjectPtr();
|
||||
return true;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
ret = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *FeaturePythonImp::getPyObject(void)
|
||||
{
|
||||
// ref counter is set to 1
|
||||
return new FeaturePythonPyT<DocumentObjectPy>(object);
|
||||
}
|
||||
|
||||
int FeaturePythonImp::hasChildElement() const {
|
||||
_FC_PY_CALL_CHECK(hasChildElement,return(-1));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Boolean ok(Base::pyCall(py_hasChildElement.ptr(),args.ptr()));
|
||||
return static_cast<bool>(ok) ? 1 : 0;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FeaturePythonImp::isElementVisible(const char *element) const {
|
||||
_FC_PY_CALL_CHECK(isElementVisible,return(-2));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(2);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::String(element?element:""));
|
||||
return Py::Int(Base::pyCall(py_isElementVisible.ptr(),args.ptr()));
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
int FeaturePythonImp::setElementVisible(const char *element, bool visible) {
|
||||
_FC_PY_CALL_CHECK(setElementVisible,return(-2));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(3);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::String(element?element:""));
|
||||
args.setItem(2,Py::Boolean(visible));
|
||||
return Py::Int(Base::pyCall(py_setElementVisible.ptr(),args.ptr()));
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
std::string FeaturePythonImp::getViewProviderName()
|
||||
{
|
||||
_FC_PY_CALL_CHECK(getViewProviderName,return(std::string()));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::TupleN args(Py::Object(object->getPyObject(), true));
|
||||
Py::String ret(Base::pyCall(py_getViewProviderName.ptr(),args.ptr()));
|
||||
return ret.as_string();
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
int FeaturePythonImp::canLinkProperties() const {
|
||||
_FC_PY_CALL_CHECK(canLinkProperties,return(-1));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Boolean ok(Base::pyCall(py_canLinkProperties.ptr(),args.ptr()));
|
||||
return ok?1:0;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int FeaturePythonImp::allowDuplicateLabel() const {
|
||||
_FC_PY_CALL_CHECK(allowDuplicateLabel,return(-1));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Boolean ok(Base::pyCall(py_allowDuplicateLabel.ptr(),args.ptr()));
|
||||
return ok?1:0;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int FeaturePythonImp::canLoadPartial() const {
|
||||
_FC_PY_CALL_CHECK(canLoadPartial,return(-1));
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
Py::Int ret(Base::pyCall(py_canLoadPartial.ptr(),args.ptr()));
|
||||
return ret;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool FeaturePythonImp::redirectSubName(std::ostringstream &ss,
|
||||
App::DocumentObject *topParent, App::DocumentObject *child) const
|
||||
{
|
||||
FC_PY_CALL_CHECK(redirectSubName);
|
||||
Base::PyGILStateLocker lock;
|
||||
try {
|
||||
Py::Tuple args(4);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
args.setItem(1,Py::String(ss.str()));
|
||||
args.setItem(2,topParent?Py::Object(topParent->getPyObject(),true):Py::Object());
|
||||
args.setItem(3,child?Py::Object(child->getPyObject(),true):Py::Object());
|
||||
Py::Object ret(Base::pyCall(py_redirectSubName.ptr(),args.ptr()));
|
||||
if(ret.isNone())
|
||||
return false;
|
||||
ss.str("");
|
||||
ss << ret.as_string();
|
||||
return true;
|
||||
}
|
||||
catch (Py::Exception&) {
|
||||
Base::PyException e; // extract the Python error text
|
||||
e.ReportException();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
|
||||
namespace App {
|
||||
|
||||
Reference in New Issue
Block a user