From e97336f214ed7e597b3d0e08d92032966660ec8b Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Mon, 10 Feb 2020 06:20:42 +0800 Subject: [PATCH] App: add proxy execute support to Link While executing, Link will look for a special Python callable defined in the linked proxy object, and run it. This allows for customized link behavior without defining a specialized Link. --- src/App/Link.cpp | 75 +++++++++++++++++++++++++++++++++++------------- src/App/Link.h | 8 +++++- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/App/Link.cpp b/src/App/Link.cpp index 90acca1b39..a07c967c7c 100644 --- a/src/App/Link.cpp +++ b/src/App/Link.cpp @@ -182,8 +182,60 @@ App::DocumentObjectExecReturn *LinkBaseExtension::extensionExecute(void) { // recomputed. _LinkTouched.touch(); - if(getLinkedObjectProperty() && !getTrueLinkedObject(true)) - return new App::DocumentObjectExecReturn("Link broken"); + if(getLinkedObjectProperty()) { + DocumentObject *linked = getTrueLinkedObject(true); + if(!linked) + return new App::DocumentObjectExecReturn("Link broken"); + + App::DocumentObject *container = getContainer(); + PropertyPythonObject *proxy = 0; + if(getLinkExecuteProperty() + && !boost::iequals(getLinkExecuteValue(), "none") + && (!myOwner || !container->getDocument()->getObjectByID(myOwner))) + { + // Check if this is an element link. Do not invoke appLinkExecute() + // if so, because it will be called from the link array. + proxy = Base::freecad_dynamic_cast( + linked->getPropertyByName("Proxy")); + } + if(proxy) { + Base::PyGILStateLocker lock; + const char *errMsg = "Linked proxy execute failed"; + try { + Py::Tuple args(3); + Py::Object proxyValue = proxy->getValue(); + const char *method = getLinkExecuteValue(); + if(!method || !method[0]) + method = "appLinkExecute"; + Py::Object attr = proxyValue.getAttr(method); + if(attr.ptr() && attr.isCallable()) { + Py::Tuple args(4); + args.setItem(0, Py::asObject(linked->getPyObject())); + args.setItem(1, Py::asObject(container->getPyObject())); + if(!_getElementCountValue()) { + Py::Callable(attr).apply(args); + } else { + const auto &elements = _getElementListValue(); + for(int i=0; i<_getElementCountValue(); ++i) { + args.setItem(2, Py::Int(i)); + if(i < (int)elements.size()) + args.setItem(3, Py::asObject(elements[i]->getPyObject())); + else + args.setItem(3, Py::Object()); + Py::Callable(attr).apply(args); + } + } + } + } catch (Py::Exception &) { + Base::PyException e; + e.ReportException(); + return new App::DocumentObjectExecReturn(errMsg); + } catch (Base::Exception &e) { + e.ReportException(); + return new App::DocumentObjectExecReturn(errMsg); + } + } + } return inherited::extensionExecute(); } @@ -906,17 +958,6 @@ void LinkBaseExtension::update(App::DocumentObject *parent, const Property *prop auto placementProp = getPlacementListProperty(); auto scaleProp = getScaleListProperty(); const auto &vis = getVisibilityListValue(); - auto proxy = freecad_dynamic_cast(parent->getPropertyByName("Proxy")); - Py::Callable method; - Py::Tuple args(3); - if(proxy) { - Py::Object proxyValue = proxy->getValue(); - const char *fname = "onCreateLinkElement"; - if (proxyValue.hasAttr(fname)) { - method = proxyValue.getAttr(fname); - args.setItem(0,Py::Object(parent->getPyObject(),true)); - } - } auto owner = getContainer(); long ownerID = owner?owner->getID():0; @@ -932,13 +973,7 @@ void LinkBaseExtension::update(App::DocumentObject *parent, const Property *prop if(obj && (!obj->myOwner || obj->myOwner==ownerID)) obj->Visibility.setValue(false); else { - if(!method.isNone()) { - obj = new LinkElementPython; - args.setItem(1,Py::Object(obj->getPyObject(),true)); - args.setItem(2,Py::Int((int)i)); - method.apply(args); - } else - obj = new LinkElement; + obj = new LinkElement; parent->getDocument()->addObject(obj,name.c_str()); } diff --git a/src/App/Link.h b/src/App/Link.h index 1f32a26145..503ea3a457 100644 --- a/src/App/Link.h +++ b/src/App/Link.h @@ -126,6 +126,10 @@ public: #define LINK_PARAM_MODE(...) \ (LinkMode, long, App::PropertyEnumeration, ((long)0), "Link group mode", ##__VA_ARGS__) +#define LINK_PARAM_LINK_EXECUTE(...) \ + (LinkExecute, const char*, App::PropertyString, (""),\ + "Link execute function. Default to 'appLinkExecute'. 'None' to disable.", ##__VA_ARGS__) + #define LINK_PARAM_COLORED_ELEMENTS(...) \ (ColoredElements, App::DocumentObject*, App::PropertyLinkSubHidden, \ 0, "Link colored elements", ##__VA_ARGS__) @@ -155,7 +159,8 @@ public: LINK_PARAM(ELEMENTS)\ LINK_PARAM(SHOW_ELEMENT)\ LINK_PARAM(MODE)\ - LINK_PARAM(COLORED_ELEMENTS) + LINK_PARAM(LINK_EXECUTE)\ + LINK_PARAM(COLORED_ELEMENTS)\ enum PropIndex { #define LINK_PINDEX_DEFINE(_1,_2,_param) LINK_PINDEX(_param), @@ -446,6 +451,7 @@ public: LINK_PARAM_EXT(PLACEMENT)\ LINK_PARAM_EXT(SHOW_ELEMENT)\ LINK_PARAM_EXT_TYPE(COUNT,App::PropertyIntegerConstraint)\ + LINK_PARAM_EXT(LINK_EXECUTE)\ LINK_PARAM_EXT_ATYPE(COLORED_ELEMENTS,App::Prop_Hidden)\ LINK_PROPS_DEFINE(LINK_PARAMS_LINK)