Skip to content

Commit

Permalink
App: add proxy execute support to Link
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
realthunder authored and wwmayer committed Feb 14, 2020
1 parent e77ab5e commit e97336f
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 21 deletions.
75 changes: 55 additions & 20 deletions src/App/Link.cpp
Expand Up @@ -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<PropertyPythonObject>(
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();
}

Expand Down Expand Up @@ -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<PropertyPythonObject>(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;
Expand All @@ -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());
}

Expand Down
8 changes: 7 additions & 1 deletion src/App/Link.h
Expand Up @@ -126,6 +126,10 @@ class AppExport LinkBaseExtension : public App::DocumentObjectExtension
#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__)
Expand Down Expand Up @@ -155,7 +159,8 @@ class AppExport LinkBaseExtension : public App::DocumentObjectExtension
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),
Expand Down Expand Up @@ -446,6 +451,7 @@ class AppExport Link : public App::DocumentObject, public App::LinkExtension
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)
Expand Down

0 comments on commit e97336f

Please sign in to comment.