App: add New APIs for future Link function

DocumentObject:

* getSubObject(): the most important API for Link to work with
  hierarchies. The function is a inspired from and replaces the
  getPySubObjects(). It returns a child object following a dot separated
  subname reference, and can optionally return accumulated
  transformation, and/or a python object of the refered
  sub-object/element. The default implementation here is to look for
  link type property, and search for the referenced object. This patch also
  include other specialized implementation of this API, such as
  (GeoFeature)GroupExtension (through extensionGetSubObject()),
  PartDesign::Body, and so on. A link type object is expected to
  call the linked object's getSubObject() for resolving.

* getSubObjectList(): helper function to return a list of object
  referenced in the given subname.

* getSubObjects(): return a list of subname references of all children
  objects. The purpose of this function is similar to
  ViewProvider::claimChildren().  Container type object is expected to
  implement this function.  The reason it returns subname references
  instead of just object is to allow the container to skip hierarchies.
  For example, the Assembly3 container uses this to skip the constraint
  and element group.

* getLinkedObject(), obtain the linked object, and optionally with the
  accumulated transformation. It is expected to return a linked object
  or the object itself if it is not a link. In case there are multiple
  levels of linking involved, this function allows the caller to retrieve
  the linked object recursively.

* hasChildElement(), set/isElementVisible(), controls the children
  visibility for a group type object. Because the child object may be
  claimed by other objects, it is essential to have independent control
  of children visibilities. These APIs are designed to abstract how
  group manages the child visibility. For performance reason, these
  function are meant to control only the immediate child object.

* resolve(), helper function to parse subname reference and resolve the
  final object, and optionally the immediate parent of the final object,
  the final object reference name (for calling `set/isElementVisible()`),
  and the subname reference if there is one.

* touch(), add optional argument 'noRecompute' for better backward
  compatibility with the NoRecompute flag. By default, touch() causes
  recompute unless noRecompute is true

* signalChanged/signalBeforeChange, two new signal for tracking changes
  of a specific object.

* getViewProviderNameOverride(), return a string of the view provider
  type of this object. This allows Python class to override the view
  provider of an object. This feature will be used by ViewProviderLink
  which is designed to work with any object that has LinkBaseExtension.

* canLinkProperties(), will be used by Gui::PropertyView to display
  linked object properties together with the object's own properties.

* redirectSubname(), will be used by Gui::Tree to allow an object to
  redirect selection to some other object when (pre)selected in the tree
  view.

* Visibility, new property serve as the same purpose as view provider
  property of the same name. It is added here so that App namespace
  code can check for visibility without Gui module. This is useful,
  for example, when constructing a compound shape of a container that
  respects the children visibility.

* (has)hasHiddenMarker(), return or check for a special sub-element
  name used as marker for overriding sub-object visibility. Will be
  used by Gui::ViewProvider, it is put here for the same reason as
  adding Visibility property.

* getID(), return object internal identifier. Each object is now
  assigned an integer identifier that is unique within its containing
  document.

Document:

* ShowHidden, new property to tell tree view whether to show hidden
  object items.

* signalTouchedObject, new signal triggered when manually touch an
  object when calling its touch() function

* getObjectByID(), get object by its identifier

* addObject() is modified to allow overriding view provider

* has/getLinksTo(), helper function to obtain links to a given object.

Application:

* checkLinkDepth(), helper function to check recursive depth for link
  traversal. The depth is checked against the total object count of
  all opened documents. The count (_objCount) is internally updated
  whenever object is added or removed.

* has/getLinksTo(), same as Document::has/getLinksTo() but return links
  from all opened documents.

GroupExtension/OriginGroupExtension/DatumFeature/DatumCS/Part::Feature:
implement sepcialized getSubObject/getSubObjects().
This commit is contained in:
Zheng, Lei
2019-06-24 16:30:08 +08:00
committed by wmayer
parent 3fcbf71fb5
commit ff1d1cd341
31 changed files with 1762 additions and 172 deletions

View File

@@ -148,6 +148,7 @@ using namespace boost::program_options;
# include <new>
#endif
FC_LOG_LEVEL_INIT("App",true,true);
//using Base::GetConsole;
using namespace Base;
@@ -249,7 +250,7 @@ init_freecad_module(void)
#endif
Application::Application(std::map<std::string,std::string> &mConfig)
: _mConfig(mConfig), _pActiveDoc(0)
: _mConfig(mConfig), _pActiveDoc(0), _objCount(-1)
{
//_hApp = new ApplicationOCC;
mpcPramManager["System parameter"] = _pcSysParamMngr;
@@ -481,6 +482,8 @@ bool Application::closeDocument(const char* name)
std::unique_ptr<Document> delDoc (pos->second);
DocMap.erase( pos );
_objCount = -1;
// Trigger observers after removing the document from the internal map.
signalDeletedDocument();
@@ -704,6 +707,51 @@ std::string Application::getHelpDir()
#endif
}
int Application::checkLinkDepth(int depth, bool no_throw) {
if(_objCount<0) {
_objCount = 0;
for(auto &v : DocMap)
_objCount += v.second->countObjects();
}
if(depth > _objCount+2) {
const char *msg = "Link recursion limit reached. "
"Please check for cyclic reference.";
if(no_throw) {
FC_ERR(msg);
return 0;
}else
throw Base::RuntimeError(msg);
}
return _objCount+2;
}
std::set<DocumentObject *> Application::getLinksTo(
const DocumentObject *obj, int options, int maxCount) const
{
std::set<DocumentObject *> links;
if(!obj) {
for(auto &v : DocMap) {
v.second->getLinksTo(links,obj,options,maxCount);
if(maxCount && (int)links.size()>=maxCount)
break;
}
} else {
std::set<Document*> docs;
for(auto o : obj->getInList()) {
if(o && o->getNameInDocument() && docs.insert(o->getDocument()).second) {
o->getDocument()->getLinksTo(links,obj,options,maxCount);
if(maxCount && (int)links.size()>=maxCount)
break;
}
}
}
return links;
}
bool Application::hasLinksTo(const DocumentObject *obj) const {
return !getLinksTo(obj,0,1).empty();
}
ParameterManager & Application::GetSystemParameter(void)
{
return *_pcSysParamMngr;
@@ -1009,11 +1057,13 @@ void Application::slotChangedDocument(const App::Document& doc, const Property&
void Application::slotNewObject(const App::DocumentObject&O)
{
this->signalNewObject(O);
_objCount = -1;
}
void Application::slotDeletedObject(const App::DocumentObject&O)
{
this->signalDeletedObject(O);
_objCount = -1;
}
void Application::slotBeforeChangeObject(const DocumentObject& O, const Property& Prop)

View File

@@ -28,6 +28,7 @@
#include <boost/signals2.hpp>
#include <vector>
#include <deque>
#include <Base/PyObjectBase.h>
#include <Base/Parameter.h>
@@ -48,6 +49,16 @@ class DocumentObject;
class ApplicationObserver;
class Property;
enum GetLinkOption {
/// Get all links (both directly and in directly) linked to the given object
GetLinkRecursive = 1,
/// Get link array element instead of the array
GetLinkArrayElement = 2,
/// Get linked object instead of the link, no effect if GetLinkRecursive
GetLinkedObject = 4,
/// Get only external links, no effect if GetLinkRecursive
GetLinkExternal = 8,
};
/** The Application
@@ -124,6 +135,8 @@ public:
boost::signals2::signal<void (const Document&)> signalUndoDocument;
/// signal on redo in document
boost::signals2::signal<void (const Document&)> signalRedoDocument;
/// signal on show hidden items
boost::signals2::signal<void (const Document&)> signalShowHidden;
//@}
@@ -274,6 +287,34 @@ public:
static std::string getHelpDir();
//@}
/** @name Link handling */
//@{
/** Check for link recursion depth
*
* @param depth: current depth
* @param no_throw: whether to throw exception
*
* @return Return the maximum remaining depth.
*
* The function uses an internal count of all objects in all documents as
* the limit of recursion depth.
*/
int checkLinkDepth(int depth, bool no_throw=true);
/** Return the links to a given object
*
* @param obj: the linked object. If NULL, then all links are returned.
* @param option: @sa App::GetLinkOptions
* @param maxCount: limit the number of links returned, 0 means no limit
*/
std::set<DocumentObject*> getLinksTo(
const DocumentObject *, int options, int maxCount=0) const;
/// Check if there is any link to the given object
bool hasLinksTo(const DocumentObject *obj) const;
//@}
friend class App::Document;
protected:
@@ -315,7 +356,6 @@ private:
static ParameterManager *_pcUserParamMngr;
//@}
//---------------------------------------------------------------------
// python exports goes here +++++++++++++++++++++++++++++++++++++++++++
//---------------------------------------------------------------------
@@ -353,7 +393,10 @@ private:
static PyObject *sSetLogLevel (PyObject *self,PyObject *args);
static PyObject *sGetLogLevel (PyObject *self,PyObject *args);
static PyMethodDef Methods[];
static PyObject *sCheckLinkDepth (PyObject *self,PyObject *args);
static PyObject *sGetLinksTo (PyObject *self,PyObject *args);
static PyMethodDef Methods[];
friend class ApplicationObserver;
@@ -401,6 +444,9 @@ private:
std::map<std::string,std::string> &_mConfig;
App::Document* _pActiveDoc;
// for estimate max link depth
int _objCount;
static Base::ConsoleObserverStd *_pConsoleObserverStd;
static Base::ConsoleObserverFile *_pConsoleObserverFile;
};

View File

@@ -35,6 +35,7 @@
#include "Document.h"
#include "DocumentPy.h"
#include "DocumentObserverPython.h"
#include "DocumentObjectPy.h"
// FreeCAD Base header
#include <Base/Interpreter.h>
@@ -144,7 +145,12 @@ PyMethodDef Application::Methods[] = {
"'level' can either be string 'Log', 'Msg', 'Wrn', 'Error', or an integer value"},
{"getLogLevel", (PyCFunction) Application::sGetLogLevel, METH_VARARGS,
"getLogLevel(tag) -- Get the log level of a string tag"},
{"checkLinkDepth", (PyCFunction) Application::sCheckLinkDepth, METH_VARARGS,
"checkLinkDepth(depth) -- check link recursion depth"},
{"getLinksTo", (PyCFunction) Application::sGetLinksTo, METH_VARARGS,
"getLinksTo(obj,options=0,maxCount=0) -- return the objects linked to 'obj'\n\n"
"options: 1: recursive, 2: check link array. Options can combine.\n"
"maxCount: to limit the number of links returned\n"},
{NULL, NULL, 0, NULL} /* Sentinel */
};
@@ -763,4 +769,40 @@ PyObject *Application::sGetLogLevel(PyObject * /*self*/, PyObject *args)
} PY_CATCH;
}
PyObject *Application::sCheckLinkDepth(PyObject * /*self*/, PyObject *args)
{
short depth = 0;
if (!PyArg_ParseTuple(args, "h", &depth))
return NULL;
PY_TRY {
return Py::new_reference_to(Py::Int(GetApplication().checkLinkDepth(depth,false)));
}PY_CATCH;
}
PyObject *Application::sGetLinksTo(PyObject * /*self*/, PyObject *args)
{
PyObject *pyobj = Py_None;
int options = 0;
short count = 0;
if (!PyArg_ParseTuple(args, "|Oih",&pyobj,&options, &count))
return NULL;
PY_TRY {
DocumentObject *obj = 0;
if(pyobj!=Py_None) {
if(!PyObject_TypeCheck(pyobj,&DocumentObjectPy::Type)) {
PyErr_SetString(PyExc_TypeError, "Expect the first argument of type document object");
return 0;
}
obj = static_cast<DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
}
auto links = GetApplication().getLinksTo(obj,options,count);
Py::Tuple ret(links.size());
int i=0;
for(auto o : links)
ret.setItem(i++,Py::Object(o->getPyObject(),true));
return Py::new_reference_to(ret);
}PY_CATCH;
}

View File

@@ -68,6 +68,8 @@ recompute path. Also, it enables more complicated dependencies beyond trees.
# include <random>
#endif
#include <boost/algorithm/string.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/subgraph.hpp>
#include <boost/graph/graphviz.hpp>
@@ -83,6 +85,7 @@ recompute path. Also, it enables more complicated dependencies beyond trees.
#include <boost/regex.hpp>
#include <unordered_set>
#include <unordered_map>
#include <random>
#include <QCoreApplication>
#include <QCryptographicHash>
@@ -154,7 +157,9 @@ struct DocumentP
{
// Array to preserve the creation order of created objects
std::vector<DocumentObject*> objectArray;
std::map<std::string,DocumentObject*> objectMap;
std::unordered_map<std::string,DocumentObject*> objectMap;
std::unordered_map<long,DocumentObject*> objectIdMap;
long lastObjectId;
DocumentObject* activeObject;
Transaction *activeUndoTransaction;
int iTransactionMode;
@@ -171,6 +176,13 @@ struct DocumentP
#endif //USE_OLD_DAG
DocumentP() {
static std::random_device _RD;
static std::mt19937 _RGEN(_RD());
static std::uniform_int_distribution<> _RDIST(0,5000);
// Set some random offset to reduce likelyhood of ID collison when
// copying shape from other document. It is probably better to randomize
// on each object ID.
lastObjectId = _RDIST(_RGEN);
activeObject = 0;
activeUndoTransaction = 0;
iTransactionMode = 0;
@@ -218,7 +230,7 @@ void Document::writeDependencyGraphViz(std::ostream &out)
out << "\tordering=out;" << endl;
out << "\tnode [shape = box];" << endl;
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
out << "\t" << It->first << ";" <<endl;
std::vector<DocumentObject*> OutList = It->second->getOutList();
for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
@@ -573,11 +585,11 @@ void Document::exportGraphviz(std::ostream& out) const
}
// Internal document objects
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It)
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It)
addExpressionSubgraphIfNeeded(It->second, CSSubgraphs);
// Add external document objects
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
std::vector<DocumentObject*> OutList = It->second->getOutList();
for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2) {
if (*It2) {
@@ -598,11 +610,11 @@ void Document::exportGraphviz(std::ostream& out) const
bool CSSubgraphs = depGrp->GetBool("GeoFeatureSubgraphs", true);
// Add internal document objects
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It)
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It)
add(It->second, It->second->getNameInDocument(), It->second->Label.getValue(), CSSubgraphs);
// Add external document objects
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
std::vector<DocumentObject*> OutList = It->second->getOutList();
for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2) {
if (*It2) {
@@ -664,7 +676,7 @@ void Document::exportGraphviz(std::ostream& out) const
bool omitGeoFeatureGroups = depGrp->GetBool("GeoFeatureSubgraphs", true);
// Add edges between document objects
for (std::map<std::string, DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
if(omitGeoFeatureGroups) {
//coordinate systems are represented by subgraphs
@@ -1132,8 +1144,9 @@ void Document::onChanged(const Property* prop)
// the Name property is a label for display purposes
if (prop == &Label) {
App::GetApplication().signalRelabelDocument(*this);
}
else if (prop == &Uid) {
} else if(prop == &ShowHidden) {
App::GetApplication().signalShowHidden(*this);
} else if (prop == &Uid) {
std::string new_dir = getTransientDirectoryName(this->Uid.getValueStr(),this->FileName.getStrValue());
std::string old_dir = this->TransientDir.getStrValue();
Base::FileInfo TransDirNew(new_dir);
@@ -1279,6 +1292,8 @@ Document::Document(void)
ADD_PROPERTY_TYPE(License,(license.c_str()),0,Prop_None,"License string of the Item");
ADD_PROPERTY_TYPE(LicenseURL,(licenseUrl.c_str()),0,Prop_None,"URL to the license text/contract");
ADD_PROPERTY_TYPE(ShowHidden,(false), 0,PropertyType(Prop_None),
"Whether to show hidden object items in the tree view");
// this creates and sets 'TransientDir' in onChanged()
ADD_PROPERTY_TYPE(TransientDir,(""),0,PropertyType(Prop_Transient|Prop_ReadOnly),
@@ -1302,14 +1317,12 @@ Document::~Document()
catch (const boost::exception&) {
}
std::map<std::string,DocumentObject*>::iterator it;
#ifdef FC_LOGUPDATECHAIN
Console().Log("-Delete Features of %s \n",getName());
#endif
d->objectArray.clear();
for (it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
for (auto it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
it->second->setStatus(ObjectStatus::Destroy, true);
delete(it->second);
}
@@ -1413,7 +1426,6 @@ void Document::Restore(Base::XMLReader &reader)
reader.readElement("Feature");
string type = reader.getAttribute("type");
string name = reader.getAttribute("name");
try {
addObject(type.c_str(), name.c_str(), /*isNew=*/ false);
}
@@ -1477,6 +1489,7 @@ void Document::exportObjects(const std::vector<App::DocumentObject*>& obj,
writer.writeFiles();
}
void Document::writeObjects(const std::vector<App::DocumentObject*>& obj,
Base::Writer &writer) const
{
@@ -1490,12 +1503,18 @@ void Document::writeObjects(const std::vector<App::DocumentObject*>& obj,
writer.Stream() << writer.ind() << "<Object "
<< "type=\"" << (*it)->getTypeId().getName() << "\" "
<< "name=\"" << (*it)->getNameInDocument() << "\" ";
<< "id=\"" << (*it)->getID() << "\" "
<< "ViewType=\"" << (*it)->getViewProviderNameStored() << "\" ";
// See DocumentObjectPy::getState
if ((*it)->testStatus(ObjectStatus::Touch))
writer.Stream() << "Touched=\"1\" ";
if ((*it)->testStatus(ObjectStatus::Error))
if ((*it)->testStatus(ObjectStatus::Error)) {
writer.Stream() << "Invalid=\"1\" ";
auto desc = getErrorDescription(*it);
if(desc)
writer.Stream() << "Error=\"" << Property::encodeAttribute(desc) << "\" ";
}
writer.Stream() << "/>" << endl;
}
@@ -1528,21 +1547,33 @@ Document::readObjects(Base::XMLReader& reader)
setStatus(Document::KeepTrailingDigits, !reader.doNameMapping());
std::vector<App::DocumentObject*> objs;
// read the object types
reader.readElement("Objects");
int Cnt = reader.getAttributeAsInteger("Count");
long lastId = 0;
for (int i=0 ;i<Cnt ;i++) {
reader.readElement("Object");
std::string type = reader.getAttribute("type");
std::string name = reader.getAttribute("name");
const char *viewType = reader.hasAttribute("ViewType")?reader.getAttribute("ViewType"):0;
if(!testStatus(Status::Importing) && reader.hasAttribute("id")) {
// if not importing, then temporary reset lastObjectId and make the
// following addObject() generate the correct id for this object.
d->lastObjectId = reader.getAttributeAsInteger("id")-1;
}
try {
// Use name from XML as is and do NOT remove trailing digits because
// otherwise we may cause a dependency to itself
// Example: Object 'Cut001' references object 'Cut' and removing the
// digits we make an object 'Cut' referencing itself.
App::DocumentObject* obj = addObject(type.c_str(), name.c_str(), /*isNew=*/ false);
App::DocumentObject* obj = addObject(type.c_str(), obj_name, /*isNew=*/ false, viewType, partial);
if (obj) {
if(lastId < obj->_Id)
lastId = obj->_Id;
objs.push_back(obj);
// use this name for the later access because an object with
// the given name may already exist
@@ -1559,6 +1590,8 @@ Document::readObjects(Base::XMLReader& reader)
Base::Console().Error("Cannot create object '%s': (%s)\n", name.c_str(), e.what());
}
}
if(!testStatus(Status::Importing))
d->lastObjectId = lastId;
reader.readEndElement("Objects");
setStatus(Document::KeepTrailingDigits, keepDigits);
@@ -1627,6 +1660,7 @@ Document::importObjects(Base::XMLReader& reader)
std::vector<App::DocumentObject*> objs = readObjects(reader);
reader.readEndElement("Document");
signalImportObjects(objs, reader);
// reset all touched
@@ -1846,7 +1880,8 @@ void Document::restore (void)
}
d->objectArray.clear();
d->objectMap.clear();
d->activeObject = 0;
d->objectIdMap.clear();
d->lastObjectId = 0;
Base::FileInfo fi(FileName.getValue());
Base::ifstream file(fi, std::ios::in | std::ios::binary);
@@ -1966,12 +2001,75 @@ int Document::countObjects(void) const
return static_cast<int>(d->objectArray.size());
}
void Document::getLinksTo(std::set<DocumentObject*> &links,
const DocumentObject *obj, int options, int maxCount,
const std::vector<DocumentObject*> &objs) const
{
std::map<const App::DocumentObject*,App::DocumentObject*> linkMap;
for(auto o : objs.size()?objs:d->objectArray) {
if(o == obj) continue;
auto linked = o;
if(options & GetLinkArrayElement)
linked = o->getLinkedObject(false);
else {
auto ext = o->getExtensionByType<LinkBaseExtension>(true);
if(ext)
linked = ext->getTrueLinkedObject(false,0,0,true);
else
linked = o->getLinkedObject(false);
}
if(linked && linked!=o) {
if(options & GetLinkRecursive)
linkMap[linked] = o;
else if(linked == obj || !obj) {
if((options & GetLinkExternal)
&& linked->getDocument()==o->getDocument())
continue;
else if(options & GetLinkedObject)
links.insert(linked);
else
links.insert(o);
if(maxCount && maxCount<=(int)links.size())
return;
}
}
}
if(!(options & GetLinkRecursive))
return;
std::vector<const DocumentObject*> current(1,obj);
for(int depth=0;current.size();++depth) {
if(!GetApplication().checkLinkDepth(depth,true))
break;
std::vector<const DocumentObject*> next;
for(auto o : current) {
auto iter = linkMap.find(o);
if(iter!=linkMap.end() && links.insert(iter->second).second) {
if(maxCount && maxCount<=(int)links.size())
return;
next.push_back(iter->second);
}
}
current.swap(next);
}
return;
}
bool Document::hasLinksTo(const DocumentObject *obj) const {
std::set<DocumentObject *> links;
getLinksTo(links,obj,0,1);
return !links.empty();
}
std::vector<App::DocumentObject*> Document::getInList(const DocumentObject* me) const
{
// result list
std::vector<App::DocumentObject*> result;
// go through all objects
for (std::map<std::string,DocumentObject*>::const_iterator It = d->objectMap.begin(); It != d->objectMap.end();++It) {
for (auto It = d->objectMap.begin(); It != d->objectMap.end();++It) {
// get the outList and search if me is in that list
std::vector<DocumentObject*> OutList = It->second->getOutList();
for (std::vector<DocumentObject*>::const_iterator It2=OutList.begin();It2!=OutList.end();++It2)
@@ -2606,7 +2704,8 @@ void Document::recomputeFeature(DocumentObject* Feat)
}
}
DocumentObject * Document::addObject(const char* sType, const char* pObjectName, bool isNew)
DocumentObject * Document::addObject(const char* sType, const char* pObjectName,
bool isNew, const char *viewType, bool isPartial)
{
Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true));
@@ -2641,6 +2740,9 @@ DocumentObject * Document::addObject(const char* sType, const char* pObjectName,
// insert in the name map
d->objectMap[ObjectName] = pcObject;
// generate object id and add to id map;
pcObject->_Id = ++d->lastObjectId;
d->objectIdMap[pcObject->_Id] = pcObject;
// cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
// insert in the vector
@@ -2660,6 +2762,12 @@ DocumentObject * Document::addObject(const char* sType, const char* pObjectName,
// mark the object as new (i.e. set status bit 2) and send the signal
pcObject->setStatus(ObjectStatus::New, true);
if(!viewType)
viewType = pcObject->getViewProviderNameOverride();
if(viewType)
pcObject->_pcViewProviderName = viewType;
signalNewObject(*pcObject);
// do no transactions if we do a rollback!
@@ -2730,6 +2838,9 @@ std::vector<DocumentObject *> Document::addObjects(const char* sType, const std:
// insert in the name map
d->objectMap[ObjectName] = pcObject;
// generate object id and add to id map;
pcObject->_Id = ++d->lastObjectId;
d->objectIdMap[pcObject->_Id] = pcObject;
// cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
// insert in the vector
@@ -2744,6 +2855,10 @@ std::vector<DocumentObject *> Document::addObjects(const char* sType, const std:
// mark the object as new (i.e. set status bit 2) and send the signal
pcObject->setStatus(ObjectStatus::New, true);
const char *viewType = pcObject->getViewProviderNameOverride();
pcObject->_pcViewProviderName = viewType?viewType:"";
signalNewObject(*pcObject);
// do no transactions if we do a rollback!
@@ -2786,6 +2901,9 @@ void Document::addObject(DocumentObject* pcObject, const char* pObjectName)
// insert in the name map
d->objectMap[ObjectName] = pcObject;
// generate object id and add to id map;
if(!pcObject->_Id) pcObject->_Id = ++d->lastObjectId;
d->objectIdMap[pcObject->_Id] = pcObject;
// cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
// insert in the vector
@@ -2795,6 +2913,10 @@ void Document::addObject(DocumentObject* pcObject, const char* pObjectName)
// mark the object as new (i.e. set status bit 2) and send the signal
pcObject->setStatus(ObjectStatus::New, true);
const char *viewType = pcObject->getViewProviderNameOverride();
pcObject->_pcViewProviderName = viewType?viewType:"";
signalNewObject(*pcObject);
// do no transactions if we do a rollback!
@@ -2809,6 +2931,9 @@ void Document::_addObject(DocumentObject* pcObject, const char* pObjectName)
{
std::string ObjectName = getUniqueObjectName(pObjectName);
d->objectMap[ObjectName] = pcObject;
// generate object id and add to id map;
if(!pcObject->_Id) pcObject->_Id = ++d->lastObjectId;
d->objectIdMap[pcObject->_Id] = pcObject;
d->objectArray.push_back(pcObject);
// cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())
pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);
@@ -2820,6 +2945,9 @@ void Document::_addObject(DocumentObject* pcObject, const char* pObjectName)
d->activeUndoTransaction->addObjectDel(pcObject);
}
const char *viewType = pcObject->getViewProviderNameOverride();
pcObject->_pcViewProviderName = viewType?viewType:"";
// send the signal
signalNewObject(*pcObject);
@@ -2835,7 +2963,7 @@ void Document::_addObject(DocumentObject* pcObject, const char* pObjectName)
/// Remove an object out of the document
void Document::removeObject(const char* sName)
{
std::map<std::string,DocumentObject*>::iterator pos = d->objectMap.find(sName);
auto pos = d->objectMap.find(sName);
// name not found?
if (pos == d->objectMap.end())
@@ -2909,6 +3037,7 @@ void Document::removeObject(const char* sName)
}
pos->second->setStatus(ObjectStatus::Remove, false); // Unset the bit to be on the safe side
d->objectIdMap.erase(pos->second->_Id);
d->objectMap.erase(pos);
}
@@ -2918,7 +3047,21 @@ void Document::_removeObject(DocumentObject* pcObject)
// TODO Refactoring: share code with Document::removeObject() (2015-09-01, Fat-Zer)
_checkTransaction(pcObject);
std::map<std::string,DocumentObject*>::iterator pos = d->objectMap.find(pcObject->getNameInDocument());
auto pos = d->objectMap.find(pcObject->getNameInDocument());
if(!d->rollback && d->activeUndoTransaction && pos->second->hasChildElement()) {
// Preserve link group children global visibility. See comments in
// removeObject() for more details.
for(auto &sub : pos->second->getSubObjects()) {
if(sub.empty())
continue;
if(sub[sub.size()-1]!='.')
sub += '.';
auto sobj = pos->second->getSubObject(sub.c_str());
if(sobj && sobj->getDocument()==this && !sobj->Visibility.getValue())
d->activeUndoTransaction->addObjectChange(sobj,&sobj->Visibility);
}
}
if (d->activeObject == pcObject)
d->activeObject = 0;
@@ -2951,6 +3094,7 @@ void Document::_removeObject(DocumentObject* pcObject)
// remove from map
pcObject->setStatus(ObjectStatus::Remove, false); // Unset the bit to be on the safe side
d->objectIdMap.erase(pcObject->_Id);
d->objectMap.erase(pos);
for (std::vector<DocumentObject*>::iterator it = d->objectArray.begin(); it != d->objectArray.end(); ++it) {
@@ -3138,9 +3282,7 @@ DocumentObject * Document::getActiveObject(void) const
DocumentObject * Document::getObject(const char *Name) const
{
std::map<std::string,DocumentObject*>::const_iterator pos;
pos = d->objectMap.find(Name);
auto pos = d->objectMap.find(Name);
if (pos != d->objectMap.end())
return pos->second;
@@ -3148,10 +3290,19 @@ DocumentObject * Document::getObject(const char *Name) const
return 0;
}
DocumentObject * Document::getObjectByID(long id) const
{
auto it = d->objectIdMap.find(id);
if(it!=d->objectIdMap.end())
return it->second;
return 0;
}
// Note: This method is only used in Tree.cpp slotChangeObject(), see explanation there
bool Document::isIn(const DocumentObject *pFeat) const
{
for (std::map<std::string,DocumentObject*>::const_iterator o = d->objectMap.begin(); o != d->objectMap.end(); ++o) {
for (auto o = d->objectMap.begin(); o != d->objectMap.end(); ++o) {
if (o->second == pFeat)
return true;
}
@@ -3161,9 +3312,7 @@ bool Document::isIn(const DocumentObject *pFeat) const
const char * Document::getObjectName(DocumentObject *pFeat) const
{
std::map<std::string,DocumentObject*>::const_iterator pos;
for (pos = d->objectMap.begin();pos != d->objectMap.end();++pos) {
for (auto pos = d->objectMap.begin();pos != d->objectMap.end();++pos) {
if (pos->second == pFeat)
return pos->first.c_str();
}
@@ -3178,8 +3327,7 @@ std::string Document::getUniqueObjectName(const char *Name) const
std::string CleanName = Base::Tools::getIdentifier(Name);
// name in use?
std::map<std::string,DocumentObject*>::const_iterator pos;
pos = d->objectMap.find(CleanName);
auto pos = d->objectMap.find(CleanName);
if (pos == d->objectMap.end()) {
// if not, name is OK
@@ -3222,6 +3370,7 @@ std::vector<DocumentObject*> Document::getObjects() const
return d->objectArray;
}
std::vector<DocumentObject*> Document::getObjectsOfType(const Base::Type& typeId) const
{
std::vector<DocumentObject*> Objects;
@@ -3260,7 +3409,7 @@ std::vector<DocumentObject*> Document::findObjects(const Base::Type& typeId, con
int Document::countObjectsOfType(const Base::Type& typeId) const
{
int ct=0;
for (std::map<std::string,DocumentObject*>::const_iterator it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
for (auto it = d->objectMap.begin(); it != d->objectMap.end(); ++it) {
if (it->second->getTypeId().isDerivedFrom(typeId))
ct++;
}

View File

@@ -69,7 +69,7 @@ public:
Closable = 2,
Restoring = 3,
Recomputing = 4,
PartialRestore = 5
PartialRestore = 5,
};
/** @name Properties */
@@ -109,6 +109,8 @@ public:
PropertyLink Tip;
/// Tip object of the document (if any)
PropertyString TipName;
/// Whether to show hidden items in TreeView
PropertyBool ShowHidden;
//@}
/** @name Signals of the document */
@@ -126,6 +128,8 @@ public:
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalBeforeChangeObject;
/// signal on changed Object
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalChangedObject;
/// signal on manually called DocumentObject::touch()
boost::signals2::signal<void (const App::DocumentObject&)> signalTouchedObject;
/// signal on relabeled Object
boost::signals2::signal<void (const App::DocumentObject&)> signalRelabelObject;
/// signal on activated Object
@@ -201,8 +205,11 @@ public:
* @param sType the type of created object
* @param pObjectName if nonNULL use that name otherwise generate a new unique name based on the \a sType
* @param isNew if false don't call the \c DocumentObject::setupObject() callback (default is true)
* @param viewType override object's view provider name
* @param isPartial indicate if this object is meant to be partially loaded
*/
DocumentObject *addObject(const char* sType, const char* pObjectName=0, bool isNew=true);
DocumentObject *addObject(const char* sType, const char* pObjectName=0,
bool isNew=true, const char *viewType=0, bool isPartial=false);
/** Add an array of features of the given types and names.
* Unicode names are set through the Label property.
* @param sType The type of created object
@@ -239,6 +246,8 @@ public:
DocumentObject *getActiveObject(void) const;
/// Returns a Object of this document
DocumentObject *getObject(const char *Name) const;
/// Returns a Object of this document by its id
DocumentObject *getObjectByID(long id) const;
/// Returns true if the DocumentObject is contained in this document
bool isIn(const DocumentObject *pFeat) const;
/// Returns a Name of an Object or 0
@@ -248,7 +257,7 @@ public:
/// Returns a name of the form prefix_number. d specifies the number of digits.
std::string getStandardObjectName(const char *Name, int d) const;
/// Returns a list of all Objects
std::vector<DocumentObject*> getObjects() const;
const std::vector<DocumentObject*> &getObjects() const;
std::vector<DocumentObject*> getObjectsOfType(const Base::Type& typeId) const;
/// Returns all object with given extensions. If derived=true also all objects with extensions derived from the given one
std::vector<DocumentObject*> getObjectsWithExtension(const Base::Type& typeId, bool derived = true) const;
@@ -359,6 +368,22 @@ public:
(const App::DocumentObject* from, const App::DocumentObject* to) const;
//@}
/** Return the links to a given object
*
* @param links: holds the links found
* @param obj: the linked object. If NULL, then all links are returned.
* @param option: @sa App::GetLinkOptions
* @param maxCount: limit the number of links returned, 0 means no limit
* @param objs: optional objects to search for, if empty, then all objects
* of this document are searched.
*/
void getLinksTo(std::set<DocumentObject*> &links,
const DocumentObject *obj, int options, int maxCount=0,
const std::vector<DocumentObject*> &objs = {}) const;
/// Check if there is any link to the given object
bool hasLinksTo(const DocumentObject *obj) const;
/// Function called to signal that an object identifier has been renamed
void renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App::ObjectIdentifier> & paths, const std::function<bool(const App::DocumentObject*)> &selector = [](const App::DocumentObject *) { return true; });

View File

@@ -29,17 +29,22 @@
#include <Base/Writer.h>
#include <Base/Tools.h>
#include <Base/Console.h>
#include <Base/Exception.h>
#include "Application.h"
#include "Document.h"
#include "DocumentObject.h"
#include "DocumentObjectGroup.h"
#include "PropertyLinks.h"
#include "PropertyGeo.h"
#include "PropertyExpressionEngine.h"
#include "DocumentObjectExtension.h"
#include "GeoFeatureGroupExtension.h"
#include <App/DocumentObjectPy.h>
#include <boost/bind.hpp>
FC_LOG_LEVEL_INIT("App",true,true)
using namespace App;
/** \defgroup DocObject Document Object
@@ -56,11 +61,22 @@ DocumentObjectExecReturn *DocumentObject::StdReturn = 0;
//===========================================================================
DocumentObject::DocumentObject(void)
: ExpressionEngine(),_pDoc(0),pcNameInDocument(0)
: ExpressionEngine(),_pDoc(0),pcNameInDocument(0),_Id(0)
{
// define Label of type 'Output' to avoid being marked as touched after relabeling
ADD_PROPERTY_TYPE(Label,("Unnamed"),"Base",Prop_Output,"User name of the object (UTF8)");
ADD_PROPERTY_TYPE(Label2,(""),"Base",Prop_Hidden,"User description of the object (UTF8)");
Label2.setStatus(App::Property::Output,true);
ADD_PROPERTY_TYPE(ExpressionEngine,(),"Base",Prop_Hidden,"Property expressions");
ADD_PROPERTY(Visibility, (true));
// default set Visibility status to hidden and output (no touch) for
// compatibitily reason. We use setStatus instead of PropertyType to
// allow user to change its status later
Visibility.setStatus(Property::Output,true);
Visibility.setStatus(Property::Hidden,true);
Visibility.setStatus(Property::NoModify,true);
}
DocumentObject::~DocumentObject(void)
@@ -122,9 +138,13 @@ bool DocumentObject::recomputeFeature()
* If it should be forced to recompute a document object then use
* \ref enforceRecompute() instead.
*/
void DocumentObject::touch(void)
void DocumentObject::touch(bool noRecompute)
{
if(!noRecompute)
StatusBits.set(ObjectStatus::Enforce);
StatusBits.set(ObjectStatus::Touch);
if (_pDoc)
_pDoc->signalTouchedObject(*this);
}
/**
@@ -143,8 +163,7 @@ bool DocumentObject::isTouched() const
*/
void DocumentObject::enforceRecompute(void)
{
StatusBits.set(ObjectStatus::Enforce);
StatusBits.set(ObjectStatus::Touch);
touch(false);
}
/**
@@ -188,7 +207,16 @@ const char* DocumentObject::getStatusString(void) const
return "Valid";
}
const char *DocumentObject::getNameInDocument(void) const
std::string DocumentObject::getFullName() const {
if(!getDocument() || !pcNameInDocument)
return "?";
std::string name(getDocument()->getName());
name += '#';
name += *pcNameInDocument;
return name;
}
const char *DocumentObject::getNameInDocument() const
{
// Note: It can happen that we query the internal name of an object even if it is not
// part of a document (anymore). This is the case e.g. if we have a reference in Python
@@ -322,7 +350,8 @@ void _getInListRecursive(std::set<DocumentObject*>& objSet,
std::vector<App::DocumentObject*> DocumentObject::getInListRecursive(void) const
{
// number of objects in document is a good estimate in result size
int maxDepth = getDocument()->countObjects() + 2;
// int maxDepth = getDocument()->countObjects() +2;
int maxDepth = GetApplication().checkLinkDepth(0);
std::set<App::DocumentObject*> result;
// using a rcursie helper to collect all InLists
@@ -353,7 +382,7 @@ void _getOutListRecursive(std::set<DocumentObject*>& objSet,
std::vector<App::DocumentObject*> DocumentObject::getOutListRecursive(void) const
{
// number of objects in document is a good estimate in result size
int maxDepth = getDocument()->countObjects() + 2;
int maxDepth = GetApplication().checkLinkDepth(0);
std::set<App::DocumentObject*> result;
// using a recursive helper to collect all OutLists
@@ -532,13 +561,20 @@ void DocumentObject::onBeforeChange(const Property* prop)
if (_pDoc)
onBeforeChangeProperty(_pDoc, prop);
signalBeforeChange(*this,*prop);
}
/// get called by the container when a Property was changed
void DocumentObject::onChanged(const Property* prop)
{
if (_pDoc)
_pDoc->onChangedProperty(this,prop);
if(GetApplication().isClosingAll())
return;
// Delay signaling view provider until the document object has handled the
// change
// if (_pDoc)
// _pDoc->onChangedProperty(this,prop);
if (prop == &Label && _pDoc && oldLabel != Label.getStrValue())
_pDoc->signalRelabelObject(*this);
@@ -553,6 +589,12 @@ void DocumentObject::onChanged(const Property* prop)
//call the parent for appropriate handling
TransactionalObject::onChanged(prop);
// Now signal the view provider
if (_pDoc)
_pDoc->onChangedProperty(this,prop);
signalChanged(*this,*prop);
}
PyObject *DocumentObject::getPyObject(void)
@@ -564,10 +606,126 @@ PyObject *DocumentObject::getPyObject(void)
return Py::new_reference_to(PythonObject);
}
std::vector<PyObject *> DocumentObject::getPySubObjects(const std::vector<std::string>&) const
DocumentObject *DocumentObject::getSubObject(const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
{
// default implementation returns nothing
return std::vector<PyObject *>();
DocumentObject *ret = 0;
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
for(auto ext : exts) {
if(ext->extensionGetSubObject(ret,subname,pyObj,mat,transform, depth))
return ret;
}
std::string name;
const char *dot=0;
if(!subname || !(dot=strchr(subname,'.'))) {
ret = const_cast<DocumentObject*>(this);
}else if(subname[0]=='$') {
name = std::string(subname+1,dot);
for(auto obj : getOutList()) {
if(name == obj->Label.getValue()) {
ret = obj;
break;
}
}
}else{
name = std::string(subname,dot);
const auto &outList = getOutList();
if(outList.size()!=_outListMap.size()) {
_outListMap.clear();
for(auto obj : outList)
_outListMap[obj->getNameInDocument()] = obj;
}
auto it = _outListMap.find(name.c_str());
if(it != _outListMap.end())
ret = it->second;
}
// TODO: By right, normal object's placement does not transform its sub
// objects (think of the claimed children of a Fusion). But I do think we
// should change that.
if(transform && mat) {
auto pla = dynamic_cast<PropertyPlacement*>(getPropertyByName("Placement"));
if(pla)
*mat *= pla->getValue().toMatrix();
}
if(ret && dot)
return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
return ret;
}
std::vector<DocumentObject*> DocumentObject::getSubObjectList(const char *subname) const {
std::vector<DocumentObject*> res;
res.push_back(const_cast<DocumentObject*>(this));
if(!subname || !subname[0])
return res;
std::string sub(subname);
for(auto pos=sub.find('.');pos!=std::string::npos;pos=sub.find('.',pos+1)) {
char c = sub[pos+1];
sub[pos+1] = 0;
auto sobj = getSubObject(sub.c_str());
if(!sobj || !sobj->getNameInDocument())
break;
res.push_back(sobj);
sub[pos+1] = c;
}
return res;
}
std::vector<std::string> DocumentObject::getSubObjects(int reason) const {
std::vector<std::string> ret;
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
for(auto ext : exts) {
if(ext->extensionGetSubObjects(ret,reason))
return ret;
}
return ret;
}
std::vector<std::pair<App::DocumentObject *,std::string> > DocumentObject::getParents(int depth) const {
std::vector<std::pair<App::DocumentObject *,std::string> > ret;
if(!getNameInDocument() || !GetApplication().checkLinkDepth(depth))
return ret;
std::string name(getNameInDocument());
name += ".";
for(auto parent : getInList()) {
if(!parent || !parent->getNameInDocument())
continue;
if(!parent->hasChildElement() &&
!parent->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId()))
continue;
if(!parent->getSubObject(name.c_str()))
continue;
auto links = GetApplication().getLinksTo(parent,App::GetLinkRecursive);
links.insert(parent);
for(auto parent : links) {
auto parents = parent->getParents(depth+1);
if(parents.empty())
parents.emplace_back(parent,std::string());
for(auto &v : parents)
ret.emplace_back(v.first,v.second+name);
}
}
return ret;
}
DocumentObject *DocumentObject::getLinkedObject(
bool recursive, Base::Matrix4D *mat, bool transform, int depth) const
{
DocumentObject *ret = 0;
auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
for(auto ext : exts) {
if(ext->extensionGetLinkedObject(ret,recursive,mat,transform,depth))
return ret;
}
if(transform && mat) {
auto pla = dynamic_cast<PropertyPlacement*>(getPropertyByName("Placement"));
if(pla)
*mat *= pla->getValue().toMatrix();
}
return const_cast<DocumentObject*>(this);
}
void DocumentObject::Save (Base::Writer &writer) const
@@ -664,6 +822,8 @@ void DocumentObject::onDocumentRestored()
auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
for(auto ext : vector)
ext->onExtendedDocumentRestored();
if(Visibility.testStatus(Property::Output))
Visibility.setStatus(Property::NoModify,true);
}
void DocumentObject::onSettingDocument()
@@ -715,3 +875,136 @@ void App::DocumentObject::_addBackLink(DocumentObject* newObj)
(void)newObj;
#endif //USE_OLD_DAG
}
int DocumentObject::setElementVisible(const char *element, bool visible) {
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
int ret = ext->extensionSetElementVisible(element,visible);
if(ret>=0) return ret;
}
return -1;
}
int DocumentObject::isElementVisible(const char *element) const {
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
int ret = ext->extensionIsElementVisible(element);
if(ret>=0) return ret;
}
return -1;
}
bool DocumentObject::hasChildElement() const {
for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
if(ext->extensionHasChildElement())
return true;
}
return false;
}
DocumentObject *DocumentObject::resolve(const char *subname,
App::DocumentObject **parent, std::string *childName, const char **subElement,
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
{
auto self = const_cast<DocumentObject*>(this);
if(parent) *parent = 0;
if(subElement) *subElement = 0;
auto obj = getSubObject(subname,pyObj,pmat,transform,depth);
if(!obj || !subname || *subname==0)
return self;
if(!parent && !subElement)
return obj;
// NOTE, the convension of '.' separated SubName demands a mandatory ending
// '.' for each object name in SubName, even if there is no subelement
// following it. So finding the last dot will give us the end of the last
// object name.
const char *dot=0;
if(Data::ComplexGeoData::isMappedElement(subname) ||
!(dot=strrchr(subname,'.')) ||
dot == subname)
{
if(subElement)
*subElement = dot?dot+1:subname;
return obj; // this means no parent object reference in SubName
}
if(parent)
*parent = self;
bool elementMapChecked = false;
const char *lastDot = dot;
for(--dot;;--dot) {
// check for the second last dot, which is the end of the last parent object
if(*dot == '.' || dot == subname) {
// We can't get parent object by its name, because the object may be
// externally linked (i.e. in a different document). So go through
// getSubObject again.
if(!elementMapChecked) {
elementMapChecked = true;
const char *sub = dot==subname?dot:dot+1;
if(Data::ComplexGeoData::isMappedElement(sub)) {
lastDot = dot;
if(dot==subname)
break;
else
continue;
}
}
if(dot==subname)
break;
auto sobj = getSubObject(std::string(subname,dot-subname+1).c_str());
if(sobj!=obj) {
if(parent) {
// Link/LinkGroup has special visiblility handling of plain
// group, so keep ascending
if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
*parent = sobj;
break;
}
for(auto ddot=dot-1;ddot!=subname;--ddot) {
if(*ddot != '.') continue;
auto sobj = getSubObject(std::string(subname,ddot-subname+1).c_str());
if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
*parent = sobj;
break;
}
}
}
break;
}
}
}
if(childName && lastDot!=dot) {
if(*dot == '.')
++dot;
const char *nextDot = strchr(dot,'.');
assert(nextDot);
*childName = std::string(dot,nextDot-dot);
}
if(subElement)
*subElement = *lastDot=='.'?lastDot+1:lastDot;
return obj;
}
const std::string &DocumentObject::hiddenMarker() {
static std::string marker("!hide");
return marker;
}
const char *DocumentObject::hasHiddenMarker(const char *subname) {
if(!subname) return 0;
const char *marker = strrchr(subname,'.');
if(!marker)
marker = subname;
else
++marker;
return hiddenMarker()==marker?marker:0;
}
bool DocumentObject::redirectSubName(std::ostringstream &, DocumentObject *, DocumentObject *) const {
return false;
}

View File

@@ -30,6 +30,7 @@
#include <App/PropertyExpressionEngine.h>
#include <Base/TimeInfo.h>
#include <Base/Matrix.h>
#include <CXX/Objects.hxx>
#include <bitset>
@@ -46,7 +47,7 @@ enum ObjectStatus {
Touch = 0,
Error = 1,
New = 2,
Recompute = 3,
Recompute = 3, // set when the object is currently being recomputed
Restore = 4,
Remove = 5,
PythonCall = 6,
@@ -86,18 +87,35 @@ class AppExport DocumentObject: public App::TransactionalObject
public:
PropertyString Label;
PropertyString Label2;
PropertyExpressionEngine ExpressionEngine;
/// Allow control visibility status in App name space
PropertyBool Visibility;
/// signal before changing a property of this object
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalBeforeChange;
/// signal on changed property of this object
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalChanged;
/// returns the type name of the ViewProvider
virtual const char* getViewProviderName(void) const {
return "";
}
/// This function is introduced to allow Python feature override its view provider
virtual const char *getViewProviderNameOverride() const {
return getViewProviderName();
}
/// Constructor
DocumentObject(void);
virtual ~DocumentObject();
/// returns the name which is set in the document for this object (not the name property!)
const char *getNameInDocument(void) const;
/// Return the object ID that is unique within its owner document
long getID() const {return _Id;}
/// Return the object full name of the form DocName#ObjName
std::string getFullName() const;
virtual bool isAttachedToDocument() const;
virtual const char* detachFromDocument();
/// gets the document in which this Object is handled
@@ -107,7 +125,7 @@ public:
*/
//@{
/// set this document object touched (cause recomputation on dependent features)
void touch(void);
void touch(bool noRecompute=false);
/// test if this document object is touched
bool isTouched(void) const;
/// Enforce this document object to be recomputed
@@ -137,6 +155,31 @@ public:
void setStatus(ObjectStatus pos, bool on) {StatusBits.set((size_t)pos, on);}
//@}
/** Child element handling
*/
//@{
/** Set sub-element visibility
*
* For performance reason, \c element must not contain any further
* sub-elements, i.e. there should be no '.' inside \c element.
*
* @return -1 if element visiblity is not supported, 0 if element is not
* found, 1 if success
*/
virtual int setElementVisible(const char *element, bool visible);
/** Get sub-element visibility
*
* @return -1 if element visiblity is not supported or element not found, 0
* if element is invisible, or else 1
*/
virtual int isElementVisible(const char *element) const;
/// return true to activate tree view group object handling and element visibility
virtual bool hasChildElement() const;
//@}
/** DAG handling
This part of the interface deals with viewing the document as
a DAG (directed acyclic graph).
@@ -214,8 +257,89 @@ public:
*/
virtual void onLostLinkToObject(DocumentObject*);
virtual PyObject *getPyObject(void);
/// its used to get the python sub objects by name (e.g. by the selection)
virtual std::vector<PyObject *> getPySubObjects(const std::vector<std::string>&) const;
/** Get the sub element/object by name
*
* @param subname: a string which is dot separated name to refer to a sub
* element or object. An empty string can be used to refer to the object
* itself
*
* @param pyObj: if non zero, returns the python object corresponding to
* this sub object. The actual type of this python object is implementation
* dependent. For example, The current implementation of Part::Feature will
* return the TopoShapePy, event if there is no sub-element reference, in
* which case it returns the whole shape.
*
* @param mat: If non zero, it is used as the current transformation matrix
* on input. And output as the accumulated transformation up until and
* include the transformation applied by the final object reference in \c
* subname. For Part::Feature, the transformation is applied to the
* TopoShape inside \c pyObj before returning.
*
* @param transform: if false, then it will not apply the object's own
* transformation to \c mat, which lets you override the object's placement
* (and possibly scale).
*
* @param depth: depth limitation as hint for cyclic link detection
*
* @return The last document object refered in subname. If subname is empty,
* then it shall return itself. If subname is invalid, then it shall return
* zero.
*/
virtual DocumentObject *getSubObject(const char *subname, PyObject **pyObj=0,
Base::Matrix4D *mat=0, bool transform=true, int depth=0) const;
/// Return a list of objects referenced by a given subname including this object
std::vector<DocumentObject*> getSubObjectList(const char *subname) const;
/// reason of calling getSubObjects()
enum GSReason {
/// default, mostly for exporting shape objects
GS_DEFAULT,
/// for element selection
GS_SELECT,
};
/** Return name reference of all sub-objects
*
* @param reason: indicate reason of obtaining the sub objects
*
* The default implementation returns all object references in
* PropertyLink, and PropertyLinkList, if any
*
* @return Return a vector of subname references for all sub-objects. In
* most cases, the name returned will be the object name plus an ending
* '.', which can be passed directly to getSubObject() to retrieve the
* name. The reason to return the name reference instead of the sub object
* itself is because there may be no real sub object, or the sub object
* need special transformation. For example, sub objects of an array type
* of object.
*/
virtual std::vector<std::string> getSubObjects(int reason=0) const;
///Obtain top parents and subnames of this object using its InList
std::vector<std::pair<App::DocumentObject*,std::string> > getParents(int depth=0) const;
/** Return the linked object with optional transformation
*
* @param recurse: If false, return the immediate linked object, or else
* recursively call this function to return the final linked object.
*
* @param mat: If non zero, it is used as the current transformation matrix
* on input. And output as the accumulated transformation till the final
* linked object.
*
* @param transform: if false, then it will not accumulate the object's own
* placement into \c mat, which lets you override the object's placement.
*
* @return Return the linked object. This function must return itself if the
* it is not a link or the link is invalid.
*/
virtual DocumentObject *getLinkedObject(bool recurse=true,
Base::Matrix4D *mat=0, bool transform=false, int depth=0) const;
/* Return true to cause PropertyView to show linked object's property */
virtual bool canLinkProperties() const {return true;}
friend class Document;
friend class Transaction;
@@ -237,6 +361,51 @@ public:
const std::string & getOldLabel() const { return oldLabel; }
const char *getViewProviderNameStored() const {
return _pcViewProviderName.c_str();
}
/** Resolve the last document object referenced in the subname
*
* @param subname: dot separated subname
* @param parent: return the direct parent of the object
* @param childName: return child name to be passed to is/setElementVisible()
* @param subElement: return non-object sub-element name if found. The
* pointer is guaranteed to be within the buffer pointed to by 'subname'
*
* @sa getSubObject()
* @return Returns the last referenced document object in the subname. If no
* such object in subname, return pObject.
*/
App::DocumentObject *resolve(const char *subname, App::DocumentObject **parent=0,
std::string *childName=0, const char **subElement=0,
PyObject **pyObj=0, Base::Matrix4D *mat=0, bool transform=true, int depth=0) const;
/** Allow object to redirect a subname path
*
* @param ss: input as the current subname path from \a topParent leading
* just before this object, i.e. ends at the parent of this object. This
* function should append its own name to this path, or redirect the
* subname to other place.
* @param topParent: top parent of this subname path
* @param child: the immediate child object in the path
*
* This function is called by tree view to generate a subname path when an
* item is selected in the tree. Document object can use this function to
* redirect the selection to some other objects.
*/
virtual bool redirectSubName(std::ostringstream &ss,
DocumentObject *topParent, DocumentObject *child) const;
/** Sepecial marker to mark the object has hidden
*
* It is used by Gui::ViewProvider::getElementColors(), but exposed here
* for convenience
*/
static const std::string &hiddenMarker();
/// Check if the subname reference ends with hidden marker.
static const char *hasHiddenMarker(const char *subname);
protected:
/// recompute only this object
virtual App::DocumentObjectExecReturn *recompute(void);
@@ -301,6 +470,13 @@ protected: // attributes
// pointer to the document name string (for performance)
const std::string *pcNameInDocument;
private:
// accessed by App::Document to record and restore the correct view provider type
std::string _pcViewProviderName;
// unique identifier (ammong a document) of this object.
long _Id;
private:
// Back pointer to all the fathers in a DAG of the document
// this is used by the document (via friend) to have a effective DAG handling

View File

@@ -91,3 +91,20 @@ DocumentObject* DocumentObjectExtension::getExtendedObject() {
assert(getExtendedContainer()->isDerivedFrom(DocumentObject::getClassTypeId()));
return static_cast<DocumentObject*>(getExtendedContainer());
}
bool DocumentObjectExtension::extensionGetSubObject(DocumentObject *&,
const char *, PyObject **, Base::Matrix4D *, bool, int) const
{
return false;
}
bool DocumentObjectExtension::extensionGetSubObjects(std::vector<std::string>&, int) const
{
return false;
}
bool DocumentObjectExtension::extensionGetLinkedObject(
DocumentObject *&, bool, Base::Matrix4D *, bool, int) const
{
return false;
}

View File

@@ -61,12 +61,39 @@ public:
virtual void onExtendedSetupObject();
/// get called when object is going to be removed from the document
virtual void onExtendedUnsetupObject();
virtual PyObject* getExtensionPyObject(void) override;
/// returns the type name of the ViewProviderExtension which is automatically attached
/// to the viewprovider object when it is initiated
virtual const char* getViewProviderExtensionName(void) const {return "";}
/** Get the sub object by name
* @sa DocumentObject::getSubObject()
*
* @return Return turn if handled, the sub object is returned in \c ret
*/
virtual bool extensionGetSubObject( DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const;
/** Get name references of all sub objects
* @sa DocumentObject::getSubObjects()
*
* @return Return turn if handled, the sub object is returned in \c ret
*/
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const;
/** Get the linked object
* @sa DocumentObject::getLinkedObject()
*
* @return Return turn if handled, the linked object is returned in \c ret
*/
virtual bool extensionGetLinkedObject(DocumentObject *&ret, bool recursive,
Base::Matrix4D *mat, bool transform, int depth) const;
virtual int extensionSetElementVisible(const char *, bool) {return -1;}
virtual int extensionIsElementVisible(const char *) {return -1;}
virtual bool extensionHasChildElement() const {return false;}
};
} //App

View File

@@ -60,6 +60,90 @@
<UserDocu>Recomputes this object</UserDocu>
</Documentation>
</Methode>
<Methode Name="getSubObject" Keyword="true">
<Documentation>
<UserDocu>
getSubObject(subname, retType=0, matrix=None, transform=True, depth=0)
* subname(string|list|tuple): dot separated string or sequence of strings
referencing subobject.
* retType: return type, 0=PyObject, 1=DocObject, 2=DocAndPyObject, 3=Placement
PyObject: return a python binding object for the (sub)object referenced in
each 'subname' The actual type of 'PyObject' is impelementation dependent.
For Part::Feature compatible objects, this will be of type TopoShapePy and
pre-transformed by accumulated transformation matrix along the object path.
DocObject: return the document object referenced in subname, if 'matrix' is
None. Or, return a tuple (object, matrix) for each 'subname' and 'matrix' is
the accumulated transformation matrix for the sub object.
DocAndPyObject: return a tuple (object, matrix, pyobj) for each subname
Placement: return a trasformed placement of the sub-object
* matrix: the initial transformation to be applied to the sub object.
* transform: whether to transform the sub object using this object's placement
* depth: current recursive depth
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getSubObjectList">
<Documentation>
<UserDocu>
getSubObjectList(subname)
Return a list of objects referenced by a given subname including this object
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getSubObjects">
<Documentation>
<UserDocu>getSubObjects(reason=0): Return subname reference of all sub-objects</UserDocu>
</Documentation>
</Methode>
<Methode Name="getLinkedObject" Keyword="true">
<Documentation>
<UserDocu>
getLinkedObject(recursive=True, matrix=None, transform=True, depth=0)
Returns the linked object if there is one, or else return itself
* recusive: whether to recursively resolve the links
* transform: whether to transform the sub object using this object's placement
* matrix: If not none, this sepcifies the initial transformation to be applied
to the sub object. And cause the method to return a tuple (object, matrix)
containing the accumulated transformation matrix
* depth: current recursive depth
</UserDocu>
</Documentation>
</Methode>
<Methode Name="setElementVisible">
<Documentation>
<UserDocu>
setElementVisible(element,visible): Set the visibility of a child element
Return -1 if element visibility is not supported, 0 if element not found, 1 if success
</UserDocu>
</Documentation>
</Methode>
<Methode Name="isElementVisible">
<Documentation>
<UserDocu>
isElementVisible(element): Check if a child element is visible
Return -1 if element visibility is not supported or element not found, 0 if invisible, or else 1
</UserDocu>
</Documentation>
</Methode>
<Methode Name="hasChildElement">
<Documentation>
<UserDocu>Return true to indicate the object having child elements</UserDocu>
</Documentation>
</Methode>
<Methode Name="getParentGroup">
<Documentation>
<UserDocu>Returns the group the object is in or None if it is not part of a group.
@@ -79,6 +163,19 @@
<UserDocu>Get all paths from this object to another object following the OutList.</UserDocu>
</Documentation>
</Methode>
<Methode Name="resolve" Const="true">
<Documentation>
<UserDocu>
resolve(subname) -- resolve the sub object
Returns a tuple (subobj,parent,elementName,subElement), where 'subobj' is the
last object referenced in 'subname', and 'parent' is the direct parent of
'subobj', and 'elementName' is the name of the subobj, which can be used
to call parent.isElementVisible/setElementVisible(). 'subElement' is the
non-object sub-element name if any.
</UserDocu>
</Documentation>
</Methode>
<Attribute Name="OutList" ReadOnly="true">
<Documentation>
<UserDocu>A list of all objects this object links to.</UserDocu>
@@ -103,6 +200,12 @@
</Documentation>
<Parameter Name="InListRecursive" Type="List"/>
</Attribute>
<Attribute Name="FullName" ReadOnly="true">
<Documentation>
<UserDocu>Return the document name and internal name of this object</UserDocu>
</Documentation>
<Parameter Name="FullName" Type="String"/>
</Attribute>
<Attribute Name="Name" ReadOnly="true">
<Documentation>
<UserDocu>Return the internal name of this object</UserDocu>
@@ -129,5 +232,17 @@ or None if the GUI is not up</UserDocu>
<Parameter Name="ViewObject" Type="Object"/>
</Attribute>
<CustomAttributes />
<Attribute Name="ID" ReadOnly="true">
<Documentation>
<UserDocu>The unique identifier (among its document) of this object</UserDocu>
</Documentation>
<Parameter Name="ID" Type="Int"/>
</Attribute>
<Attribute Name="Parents" ReadOnly="true">
<Documentation>
<UserDocu>A List of tuple(parent,subname) holding all parents to this object</UserDocu>
</Documentation>
<Parameter Name="Parents" Type="List"/>
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -22,6 +22,8 @@
#include "PreCompiled.h"
#include <Base/GeometryPyCXX.h>
#include <Base/MatrixPy.h>
#include "DocumentObject.h"
#include "Document.h"
#include "Expression.h"
@@ -53,6 +55,11 @@ Py::String DocumentObjectPy::getName(void) const
return Py::String(std::string(internal));
}
Py::String DocumentObjectPy::getFullName(void) const
{
return Py::String(getDocumentObjectPtr()->getFullName());
}
Py::Object DocumentObjectPy::getDocument(void) const
{
DocumentObject* object = this->getDocumentObjectPtr();
@@ -132,8 +139,21 @@ PyObject* DocumentObjectPy::supportedProperties(PyObject *args)
PyObject* DocumentObjectPy::touch(PyObject * args)
{
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
char *propName = 0;
if (!PyArg_ParseTuple(args, "|s",&propName)) // convert args: Python->C
return NULL; // NULL triggers exception
if(propName) {
if(!propName[0]) {
getDocumentObjectPtr()->touch(true);
Py_Return;
}
auto prop = getDocumentObjectPtr()->getPropertyByName(propName);
if(!prop)
throw Py::RuntimeError("Property not found");
prop->touch();
Py_Return;
}
getDocumentObjectPtr()->touch();
Py_Return;
}
@@ -204,17 +224,19 @@ Py::Object DocumentObjectPy::getViewObject(void) const
// in v0.14+, the GUI module can be loaded in console mode (but doesn't have all its document methods)
return Py::None();
}
if(!getDocumentObjectPtr()->getDocument()) {
throw Py::RuntimeError("Object has no document");
}
const char* internalName = getDocumentObjectPtr()->getNameInDocument();
if (!internalName) {
throw Py::RuntimeError("Object has been removed from document");
}
Py::Callable method(module.getAttr("getDocument"));
Py::Tuple arg(1);
arg.setItem(0, Py::String(getDocumentObjectPtr()->getDocument()->getName()));
Py::Object doc = method.apply(arg);
method = doc.getAttr("getObject");
const char* internalName = getDocumentObjectPtr()->getNameInDocument();
if (!internalName) {
throw Py::RuntimeError("Object has been removed from document");
}
arg.setItem(0, Py::String(internalName));
Py::Object obj = method.apply(arg);
return obj;
@@ -346,6 +368,247 @@ PyObject* DocumentObjectPy::recompute(PyObject *args)
}
}
PyObject* DocumentObjectPy::getSubObject(PyObject *args, PyObject *keywds)
{
PyObject *obj;
short retType = 0;
PyObject *pyMat = Py_None;
PyObject *doTransform = Py_True;
short depth = 0;
static char *kwlist[] = {"subname","retType","matrix","transform","depth", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|hOOh", kwlist,
&obj,&retType,&pyMat,&doTransform,&depth))
return 0;
if(retType<0 || retType>6) {
PyErr_SetString(PyExc_TypeError, "invalid retType, can only be integer 0~6");
return 0;
}
std::vector<std::string> subs;
bool single=true;
if (PyUnicode_Check(obj)) {
#if PY_MAJOR_VERSION >= 3
subs.push_back(PyUnicode_AsUTF8(obj));
#else
PyObject* unicode = PyUnicode_AsUTF8String(obj);
subs.push_back(PyString_AsString(unicode));
Py_DECREF(unicode);
}
else if (PyString_Check(obj)) {
subs.push_back(PyString_AsString(obj));
#endif
} else if (PySequence_Check(obj)) {
single=false;
Py::Sequence shapeSeq(obj);
for (Py::Sequence::iterator it = shapeSeq.begin(); it != shapeSeq.end(); ++it) {
PyObject* item = (*it).ptr();
if (PyUnicode_Check(item)) {
#if PY_MAJOR_VERSION >= 3
subs.push_back(PyUnicode_AsUTF8(item));
#else
PyObject* unicode = PyUnicode_AsUTF8String(item);
subs.push_back(PyString_AsString(unicode));
Py_DECREF(unicode);
}
else if (PyString_Check(item)) {
subs.push_back(PyString_AsString(item));
#endif
}else{
PyErr_SetString(PyExc_TypeError, "non-string object in sequence");
return 0;
}
}
}else{
PyErr_SetString(PyExc_TypeError, "subname must be either a string or sequence of string");
return 0;
}
bool transform = PyObject_IsTrue(doTransform);
struct SubInfo {
App::DocumentObject *sobj;
Py::Object obj;
Py::Object pyObj;
Base::Matrix4D mat;
SubInfo(const Base::Matrix4D &mat):mat(mat){}
};
Base::Matrix4D mat;
if(pyMat!=Py_None) {
if(!PyObject_TypeCheck(pyMat,&Base::MatrixPy::Type)) {
PyErr_SetString(PyExc_TypeError, "expect argument 'matrix' to be of type Base.Matrix");
return 0;
}
mat = *static_cast<Base::MatrixPy*>(pyMat)->getMatrixPtr();
}
PY_TRY {
std::vector<SubInfo> ret;
for(const auto &sub : subs) {
ret.emplace_back(mat);
auto &info = ret.back();
PyObject *pyObj = 0;
info.sobj = getDocumentObjectPtr()->getSubObject(
sub.c_str(),retType!=0&&retType!=2?0:&pyObj,&info.mat,transform,depth);
if(pyObj)
info.pyObj = Py::Object(pyObj,true);
if(info.sobj)
info.obj = Py::Object(info.sobj->getPyObject(),true);
}
if(ret.empty())
Py_Return;
if(single) {
if(retType==0)
return Py::new_reference_to(ret[0].pyObj);
else if(retType==1 && pyMat==Py_None)
return Py::new_reference_to(ret[0].obj);
else if(!ret[0].sobj)
Py_Return;
else if(retType==3)
return Py::new_reference_to(Py::Placement(Base::Placement(ret[0].mat)));
else if(retType==4)
return Py::new_reference_to(Py::Matrix(ret[0].mat));
else if(retType==5 || retType==6) {
ret[0].sobj->getLinkedObject(true,&ret[0].mat,false);
if(retType==5)
return Py::new_reference_to(Py::Placement(Base::Placement(ret[0].mat)));
else
return Py::new_reference_to(Py::Matrix(ret[0].mat));
}
Py::Tuple rret(retType==1?2:3);
rret.setItem(0,ret[0].obj);
rret.setItem(1,Py::Object(new Base::MatrixPy(ret[0].mat)));
if(retType!=1)
rret.setItem(2,ret[0].pyObj);
return Py::new_reference_to(rret);
}
Py::Tuple tuple(ret.size());
for(size_t i=0;i<ret.size();++i) {
if(retType==0)
tuple.setItem(i,ret[i].pyObj);
else if(retType==1 && pyMat==Py_None)
tuple.setItem(i,ret[i].obj);
else if(!ret[i].sobj)
tuple.setItem(i, Py::Object());
else if(retType==3)
tuple.setItem(i,Py::Placement(Base::Placement(ret[0].mat)));
else if(retType==4)
tuple.setItem(i,Py::Matrix(ret[0].mat));
else if(retType==5 || retType==6) {
ret[i].sobj->getLinkedObject(true,&ret[i].mat,false);
if(retType==5)
tuple.setItem(i,Py::Placement(Base::Placement(ret[i].mat)));
else
tuple.setItem(i,Py::Matrix(ret[i].mat));
} else {
Py::Tuple rret(retType==1?2:3);
rret.setItem(0,ret[i].obj);
rret.setItem(1,Py::Object(new Base::MatrixPy(ret[i].mat)));
if(retType!=1)
rret.setItem(2,ret[i].pyObj);
tuple.setItem(i,rret);
}
}
return Py::new_reference_to(tuple);
}PY_CATCH
}
PyObject* DocumentObjectPy::getSubObjectList(PyObject *args) {
const char *subname;
if (!PyArg_ParseTuple(args, "s", &subname))
return NULL;
Py::List res;
PY_TRY {
for(auto o : getDocumentObjectPtr()->getSubObjectList(subname))
res.append(Py::asObject(o->getPyObject()));
return Py::new_reference_to(res);
}PY_CATCH
}
PyObject* DocumentObjectPy::getSubObjects(PyObject *args) {
int reason = 0;
if (!PyArg_ParseTuple(args, "|i", &reason))
return NULL;
PY_TRY {
auto names = getDocumentObjectPtr()->getSubObjects(reason);
Py::Tuple pyObjs(names.size());
for(size_t i=0;i<names.size();++i)
pyObjs.setItem(i,Py::String(names[i]));
return Py::new_reference_to(pyObjs);
}PY_CATCH;
}
PyObject* DocumentObjectPy::getLinkedObject(PyObject *args, PyObject *keywds)
{
PyObject *recursive = Py_True;
PyObject *pyMat = Py_None;
PyObject *transform = Py_True;
short depth = 0;
static char *kwlist[] = {"recursive","matrix","transform","depth", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|OOOh", kwlist,
&recursive,&pyMat,&transform,&depth))
return NULL;
Base::Matrix4D _mat;
Base::Matrix4D *mat = 0;
if(pyMat!=Py_None) {
if(!PyObject_TypeCheck(pyMat,&Base::MatrixPy::Type)) {
PyErr_SetString(PyExc_TypeError, "expect argument 'matrix' to be of type Base.Matrix");
return 0;
}
_mat = *static_cast<Base::MatrixPy*>(pyMat)->getMatrixPtr();
mat = &_mat;
}
PY_TRY {
auto linked = getDocumentObjectPtr()->getLinkedObject(
PyObject_IsTrue(recursive), mat, PyObject_IsTrue(transform),depth);
if(!linked)
linked = getDocumentObjectPtr();
auto pyObj = Py::Object(linked->getPyObject(),true);
if(mat) {
Py::Tuple ret(2);
ret.setItem(0,pyObj);
ret.setItem(1,Py::Object(new Base::MatrixPy(*mat)));
return Py::new_reference_to(ret);
}
return Py::new_reference_to(pyObj);
} PY_CATCH;
}
PyObject* DocumentObjectPy::isElementVisible(PyObject *args)
{
char *element = 0;
if (!PyArg_ParseTuple(args, "s", &element))
return NULL;
PY_TRY {
return Py_BuildValue("h", getDocumentObjectPtr()->isElementVisible(element));
} PY_CATCH;
}
PyObject* DocumentObjectPy::setElementVisible(PyObject *args)
{
char *element = 0;
PyObject *visible = Py_True;
if (!PyArg_ParseTuple(args, "s|O", &element,&visible))
return NULL;
PY_TRY {
return Py_BuildValue("h", getDocumentObjectPtr()->setElementVisible(element,PyObject_IsTrue(visible)));
} PY_CATCH;
}
PyObject* DocumentObjectPy::hasChildElement(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
return NULL;
PY_TRY {
return Py_BuildValue("O", getDocumentObjectPtr()->hasChildElement()?Py_True:Py_False);
} PY_CATCH;
}
PyObject* DocumentObjectPy::getParentGroup(PyObject *args)
{
if (!PyArg_ParseTuple(args, ""))
@@ -467,3 +730,41 @@ int DocumentObjectPy::setCustomAttributes(const char* attr, PyObject *obj)
return 0;
}
Py::Int DocumentObjectPy::getID() const {
return Py::Int(getDocumentObjectPtr()->getID());
}
Py::Boolean DocumentObjectPy::getRemoving() const {
return Py::Boolean(getDocumentObjectPtr()->testStatus(ObjectStatus::Remove));
}
PyObject *DocumentObjectPy::resolve(PyObject *args)
{
const char *subname;
if (!PyArg_ParseTuple(args, "s",&subname))
return NULL; // NULL triggers exception
PY_TRY {
std::string elementName;
const char *subElement = 0;
App::DocumentObject *parent = 0;
auto obj = getDocumentObjectPtr()->resolve(subname,&parent,&elementName,&subElement);
Py::Tuple ret(4);
ret.setItem(0,obj?Py::Object(obj->getPyObject(),true):Py::None());
ret.setItem(1,parent?Py::Object(parent->getPyObject(),true):Py::None());
ret.setItem(2,Py::String(elementName.c_str()));
ret.setItem(3,Py::String(subElement?subElement:""));
return Py::new_reference_to(ret);
} PY_CATCH;
Py_Return;
}
Py::List DocumentObjectPy::getParents() const {
Py::List ret;
for(auto &v : getDocumentObjectPtr()->getParents())
ret.append(Py::TupleN(Py::Object(v.first->getPyObject(),true),Py::String(v.second)));
return ret;
}

View File

@@ -63,9 +63,20 @@
<UserDocu>Commit an Undo/Redo transaction</UserDocu>
</Documentation>
</Methode>
<Methode Name="addObject">
<Methode Name="addObject" Keyword="true">
<Documentation>
<UserDocu>Add an object with given type and name to the document</UserDocu>
<UserDocu>addObject(type, name=None, objProxy=None, viewProxy=None, attach=False, viewType=None)
Add an object to document
type (String): the type of the document object to create.
name (String): the optional name of the new object.
objProxy (Object): the Python binding object to attach to the new document object.
viewProxy (Object): the Python binding object to attach the view provider of this object.
attach (Boolean): if True, then bind the document object first before adding to the document
to allow Python code to override view provider type. Once bound, and before adding to
the document, it will try to call Python binding object's attach(obj) method.
viewType (String): override the view provider type directly, only effective when attach is False.</UserDocu>
</Documentation>
</Methode>
<Methode Name="removeObject">
@@ -123,6 +134,16 @@ Return a list of objects that match the specified type and name.
Both parameters are optional.</UserDocu>
</Documentation>
</Methode>
<Methode Name="getLinksTo">
<Documentation>
<UserDocu>
getLinksTo(obj, options=0, maxCount=0): return objects linked to 'obj'
options: 1: recursive, 2: check link array. Options can combine.
maxCount: to limit the number of links returned
</UserDocu>
</Documentation>
</Methode>
<Methode Name="supportedTypes">
<Documentation>
<UserDocu>A list of supported types of objects</UserDocu>

View File

@@ -32,6 +32,7 @@
#include "DocumentObject.h"
#include "DocumentObjectPy.h"
#include "MergeDocuments.h"
#include "PropertyLinks.h"
// inclusion of the generated files (generated By DocumentPy.xml)
#include "DocumentPy.h"
@@ -55,20 +56,12 @@ PyObject* DocumentPy::save(PyObject * args)
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
return NULL; // NULL triggers exception
try {
PY_TRY {
if (!getDocumentPtr()->save()) {
PyErr_SetString(PyExc_ValueError, "Object attribute 'FileName' is not set");
return NULL;
}
}
catch (const Base::FileException& e) {
PyErr_SetString(PyExc_IOError, e.what());
return 0;
}
catch (const Base::Exception& e) {
PyErr_SetString(PyExc_RuntimeError, e.what());
return 0;
}
} PY_CATCH;
const char* filename = getDocumentPtr()->FileName.getValue();
Base::FileInfo fi(filename);
@@ -89,28 +82,10 @@ PyObject* DocumentPy::saveAs(PyObject * args)
std::string utf8Name = fn;
PyMem_Free(fn);
try {
if (!getDocumentPtr()->saveAs(utf8Name.c_str())) {
PyErr_SetString(PyExc_ValueError, "Object attribute 'FileName' is not set");
return NULL;
}
}
catch (const Base::FileException& e) {
PyErr_SetString(PyExc_IOError, e.what());
return 0;
}
catch (const Base::Exception& e) {
PyErr_SetString(PyExc_RuntimeError, e.what());
return 0;
}
Base::FileInfo fi(utf8Name);
if (!fi.isReadable()) {
PyErr_Format(PyExc_IOError, "No such file or directory: '%s'", utf8Name.c_str());
return NULL;
}
Py_Return;
PY_TRY {
getDocumentPtr()->saveAs(utf8Name.c_str());
Py_Return;
}PY_CATCH
}
PyObject* DocumentPy::saveCopy(PyObject * args)
@@ -118,18 +93,11 @@ PyObject* DocumentPy::saveCopy(PyObject * args)
char* fn;
if (!PyArg_ParseTuple(args, "s", &fn)) // convert args: Python->C
return NULL; // NULL triggers exception
if (!getDocumentPtr()->saveCopy(fn)) {
PyErr_Format(PyExc_ValueError, "Object attribute 'FileName' is not set");
return NULL;
}
Base::FileInfo fi(fn);
if (!fi.isReadable()) {
PyErr_Format(PyExc_IOError, "No such file or directory: '%s'", fn);
return NULL;
}
Py_Return;
PY_TRY {
getDocumentPtr()->saveCopy(fn);
Py_Return;
}PY_CATCH
}
PyObject* DocumentPy::load(PyObject * args)
@@ -219,17 +187,33 @@ PyObject* DocumentPy::exportGraphviz(PyObject * args)
}
}
PyObject* DocumentPy::addObject(PyObject *args)
PyObject* DocumentPy::addObject(PyObject *args, PyObject *kwd)
{
char *sType,*sName=0;
char *sType,*sName=0,*sViewType=0;
PyObject* obj=0;
PyObject* view=0;
if (!PyArg_ParseTuple(args, "s|sOO", &sType,&sName,&obj,&view)) // convert args: Python->C
return NULL; // NULL triggers exception
PyObject *attach=Py_False;
static char *kwlist[] = {"type","name","objProxy","viewProxy","attach","viewType",NULL};
if (!PyArg_ParseTupleAndKeywords(args,kwd,"s|sOOOs",
kwlist, &sType,&sName,&obj,&view,&attach,&sViewType))
return NULL;
DocumentObject *pcFtr;
DocumentObject *pcFtr = 0;
pcFtr = getDocumentPtr()->addObject(sType,sName);
if(!obj || !PyObject_IsTrue(attach)) {
pcFtr = getDocumentPtr()->addObject(sType,sName,true,sViewType);
}else{
Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true));
if(base) {
if (!base->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) {
delete base;
std::stringstream str;
str << "'" << sType << "' is not a document object type";
throw Base::TypeError(str.str());
}
pcFtr = static_cast<DocumentObject*>(base);
}
}
if (pcFtr) {
// Allows to hide the handling with Proxy in client python code
if (obj) {
@@ -243,6 +227,21 @@ PyObject* DocumentPy::addObject(PyObject *args)
}
pyftr.setAttr("Proxy", pyobj);
if(PyObject_IsTrue(attach)) {
getDocumentPtr()->addObject(pcFtr,sName);
try {
Py::Callable method(pyobj.getAttr("attach"));
if(!method.isNone()) {
Py::TupleN arg(pyftr);
method.apply(arg);
}
}catch (Py::Exception&) {
Base::PyException e;
e.ReportException();
}
}
// if a document class is set we also need a view provider defined which must be
// something different to None
Py::Object pyvp;
@@ -416,11 +415,14 @@ PyObject* DocumentPy::recompute(PyObject * args)
PyObject* DocumentPy::getObject(PyObject *args)
{
char *sName;
if (!PyArg_ParseTuple(args, "s",&sName)) // convert args: Python->C
return NULL; // NULL triggers exception
long id = -1;
char *sName = 0;
if (!PyArg_ParseTuple(args, "s",&sName)) { // convert args: Python->C
if(!PyArg_ParseTuple(args, "l", &id))
return NULL; // NULL triggers exception
}
DocumentObject *pcFtr = getDocumentPtr()->getObject(sName);
DocumentObject *pcFtr = sName?getDocumentPtr()->getObject(sName):getDocumentPtr()->getObjectByID(id);
if (pcFtr)
return pcFtr->getPyObject();
else
@@ -695,3 +697,31 @@ int DocumentPy::setCustomAttributes(const char* attr, PyObject *)
return 0;
}
PyObject* DocumentPy::getLinksTo(PyObject *args)
{
PyObject *pyobj = Py_None;
int options = 0;
short count = 0;
if (!PyArg_ParseTuple(args, "|Oih", &pyobj,&options, &count))
return NULL;
PY_TRY {
DocumentObject *obj = 0;
if(pyobj!=Py_None) {
if(!PyObject_TypeCheck(pyobj,&DocumentObjectPy::Type)) {
PyErr_SetString(PyExc_TypeError, "Expect the first argument of type document object");
return 0;
}
obj = static_cast<DocumentObjectPy*>(pyobj)->getDocumentObjectPtr();
}
std::set<DocumentObject *> links;
getDocumentPtr()->getLinksTo(links,obj,options,count);
Py::Tuple ret(links.size());
int i=0;
for(auto o : links)
ret.setItem(i++,Py::Object(o->getPyObject(),true));
return Py::new_reference_to(ret);
}PY_CATCH
}

View File

@@ -301,17 +301,24 @@ private:
};
// Property define
#define EXTENSION_ADD_PROPERTY(_prop_, _defaultval_) \
#define _EXTENSION_ADD_PROPERTY(_name, _prop_, _defaultval_) \
do { \
this->_prop_.setValue _defaultval_;\
propertyData.addProperty(static_cast<App::Extension*>(this), #_prop_, &this->_prop_); \
propertyData.addProperty(static_cast<App::Extension*>(this), _name, &this->_prop_); \
} while (0)
#define EXTENSION_ADD_PROPERTY(_prop_, _defaultval_) \
_EXTENSION_ADD_PROPERTY(#_prop_, _prop_, _defaultval_)
#define _EXTENSION_ADD_PROPERTY_TYPE(_name, _prop_, _defaultval_, _group_,_type_,_Docu_) \
do { \
this->_prop_.setValue _defaultval_;\
propertyData.addProperty(static_cast<App::Extension*>(this), _name, &this->_prop_, (_group_),(_type_),(_Docu_)); \
} while (0)
#define EXTENSION_ADD_PROPERTY_TYPE(_prop_, _defaultval_, _group_,_type_,_Docu_) \
do { \
this->_prop_.setValue _defaultval_;\
propertyData.addProperty(static_cast<App::Extension*>(this), #_prop_, &this->_prop_, (_group_),(_type_),(_Docu_)); \
} while (0)
_EXTENSION_ADD_PROPERTY_TYPE(#_prop_, _prop_, _defaultval_, _group_,_type_,_Docu_)
/**

View File

@@ -94,8 +94,8 @@ bool ExtensionContainer::hasExtension(const std::string& name) const {
}
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived) const {
Extension* ExtensionContainer::getExtension(Base::Type t, bool derived, bool no_except) const {
auto result = _extensions.find(t);
if((result == _extensions.end()) && derived) {
//we need to check for derived types
@@ -103,7 +103,7 @@ Extension* ExtensionContainer::getExtension(Base::Type t, bool derived) const {
if(entry.first.isDerivedFrom(t))
return entry.second;
}
if(no_except) return 0;
//if we arrive here we don't have anything matching
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
}
@@ -111,6 +111,7 @@ Extension* ExtensionContainer::getExtension(Base::Type t, bool derived) const {
return result->second;
}
else {
if(no_except) return 0;
//if we arrive here we don't have anything matching
throw Base::TypeError("ExtensionContainer::getExtension: No extension of given type available");
}

View File

@@ -127,24 +127,24 @@ public:
bool hasExtension(Base::Type, bool derived=true) const; //returns first of type (or derived from if set to true) and throws otherwise
bool hasExtension(const std::string& name) const; //this version does not check derived classes
bool hasExtensions() const;
App::Extension* getExtension(Base::Type, bool derived = true) const;
App::Extension* getExtension(Base::Type, bool derived = true, bool no_except=false) const;
App::Extension* getExtension(const std::string& name) const; //this version does not check derived classes
//returns first of type (or derived from) and throws otherwise
template<typename ExtensionT>
ExtensionT* getExtensionByType() const {
return dynamic_cast<ExtensionT*>(getExtension(ExtensionT::getExtensionClassTypeId()));
ExtensionT* getExtensionByType(bool no_except=false, bool derived=true) const {
return static_cast<ExtensionT*>(getExtension(ExtensionT::getExtensionClassTypeId(),derived,no_except));
};
//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::getExtensionClassTypeId());
std::vector<ExtensionT*> typevec;
for(auto ext : vec)
typevec.push_back(dynamic_cast<ExtensionT*>(ext));
for(auto entry : _extensions) {
if(entry.first.isDerivedFrom(ExtensionT::getExtensionClassTypeId()))
typevec.push_back(static_cast<ExtensionT*>(entry.second));
}
return typevec;
};

View File

@@ -34,6 +34,7 @@
#include "Origin.h"
#include "OriginGroupExtension.h"
#include <Base/Console.h>
#include <Base/Tools.h>
//#include "GeoFeatureGroupPy.h"
//#include "FeaturePythonPyImp.h"
@@ -100,11 +101,9 @@ DocumentObject* GeoFeatureGroupExtension::getGroupOfObject(const DocumentObject*
//There is a chance that a derived geofeaturegroup links with a local link and hence is not
//the parent group even though it links to the object. We use hasObject as one and only truth
//if it has the object within the group
if(inObj->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) &&
inObj->getExtensionByType<GeoFeatureGroupExtension>()->hasObject(obj)) {
auto group = inObj->getExtensionByType<GeoFeatureGroupExtension>(true);
if(group && group->hasObject(obj))
return inObj;
}
}
return nullptr;
@@ -124,10 +123,9 @@ Base::Placement GeoFeatureGroupExtension::recursiveGroupPlacement(GeoFeatureGrou
auto inList = group->getExtendedObject()->getInList();
for(auto* link : inList) {
if(link->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())){
if (link->getExtensionByType<GeoFeatureGroupExtension>()->hasObject(group->getExtendedObject()))
return recursiveGroupPlacement(link->getExtensionByType<GeoFeatureGroupExtension>()) * group->placement().getValue();
}
auto parent = link->getExtensionByType<GeoFeatureGroupExtension>(true);
if(parent && parent->hasObject(group->getExtendedObject()))
return recursiveGroupPlacement(parent) * group->placement().getValue();
}
return group->placement().getValue();
@@ -205,9 +203,10 @@ void GeoFeatureGroupExtension::extensionOnChanged(const Property* p) {
//would return anyone of it and hence it is possible that we miss an error. We need a custom check
auto list = obj->getInList();
for (auto in : list) {
if(in->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId()) && //is GeoFeatureGroup?
in != getExtendedObject() && //is a different one?
in->getExtensionByType<App::GeoFeatureGroupExtension>()->hasObject(obj)) { //is not a non-grouping link?
if(in == getExtendedObject())
continue;
auto parent = in->getExtensionByType<GeoFeatureGroupExtension>(true);
if(parent && parent->hasObject(obj)) {
error = true;
corrected.erase(std::remove(corrected.begin(), corrected.end(), obj), corrected.end());
}
@@ -285,8 +284,6 @@ std::vector< DocumentObject* > GeoFeatureGroupExtension::getScopedObjectsFromLin
return result;
}
void GeoFeatureGroupExtension::getCSOutList(const App::DocumentObject* obj,
std::vector< DocumentObject* >& vec) {
@@ -373,6 +370,59 @@ void GeoFeatureGroupExtension::recursiveCSRelevantLinks(const DocumentObject* ob
}
}
bool GeoFeatureGroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
{
ret = 0;
const char *dot;
if(!subname || *subname==0) {
auto obj = dynamic_cast<const DocumentObject*>(getExtendedContainer());
ret = const_cast<DocumentObject*>(obj);
if(mat && transform)
*mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
}else if((dot=strchr(subname,'.'))) {
if(subname[0]!='$')
ret = Group.find(std::string(subname,dot));
else{
std::string name = std::string(subname+1,dot);
for(auto child : Group.getValues()) {
if(name == child->Label.getStrValue()){
ret = child;
break;
}
}
}
if(ret) {
if(dot) ++dot;
if(dot && *dot && !ret->hasExtension(App::GeoFeatureGroupExtension::getExtensionClassTypeId())) {
// Consider this
// Body
// | -- Pad
// | -- Sketch
//
// Suppose we want to getSubObject(Body,"Pad.Sketch.")
// Because of the special property of geo feature group, both
// Pad and Sketch are children of Body, so we can't call
// getSubObject(Pad,"Sketch.") as this will transform Sketch
// using Pad's placement.
//
const char *next = strchr(dot,'.');
if(next) {
App::DocumentObject *nret=0;
extensionGetSubObject(nret,dot,pyObj,mat,transform,depth+1);
if(nret) {
ret = nret;
return true;
}
}
}
if(mat && transform)
*mat *= const_cast<GeoFeatureGroupExtension*>(this)->placement().getValue().toMatrix();
ret = ret->getSubObject(dot?dot:"",pyObj,mat,true,depth+1);
}
}
return true;
}
bool GeoFeatureGroupExtension::areLinksValid(const DocumentObject* obj) {
@@ -448,6 +498,14 @@ void GeoFeatureGroupExtension::getInvalidLinkObjects(const DocumentObject* obj,
}
}
bool GeoFeatureGroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
for(auto obj : Group.getValues()) {
if(obj && obj->getNameInDocument() && !obj->testStatus(ObjectStatus::GeoExcluded))
ret.push_back(std::string(obj->getNameInDocument())+'.');
}
return true;
}
// Python feature ---------------------------------------------------------

View File

@@ -50,6 +50,7 @@ namespace App
*/
class AppExport GeoFeatureGroupExtension : public App::GroupExtension
{
typedef App::GroupExtension inherited;
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::GeoFeatureGroupExtension);
public:
@@ -95,6 +96,11 @@ public:
return obj->hasExtension(GroupExtension::getExtensionClassTypeId()) &&
!obj->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId());
}
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname, PyObject **pyObj,
Base::Matrix4D *mat, bool transform, int depth) const override;
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const override;
virtual std::vector< DocumentObject* > addObjects(std::vector< DocumentObject* > obj) override;
virtual std::vector< DocumentObject* > removeObjects(std::vector< DocumentObject* > obj) override;

View File

@@ -33,6 +33,7 @@
#include "FeaturePythonPyImp.h"
#include "GeoFeatureGroupExtension.h"
#include <Base/Console.h>
#include <Base/Tools.h>
using namespace App;
@@ -251,15 +252,12 @@ bool GroupExtension::recursiveHasObject(const DocumentObject* obj, const GroupEx
return false;
}
bool GroupExtension::isChildOf(const GroupExtension* group, bool recursive) const
{
return group->hasObject(getExtendedObject(), recursive);
}
std::vector<DocumentObject*> GroupExtension::getObjects() const
const std::vector<DocumentObject*> &GroupExtension::getObjects() const
{
return Group.getValues();
}
@@ -348,6 +346,41 @@ void GroupExtension::extensionOnChanged(const Property* p) {
App::Extension::extensionOnChanged(p);
}
bool GroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool /*transform*/, int depth) const
{
const char *dot;
if(!subname || *subname==0) {
auto obj = Base::freecad_dynamic_cast<const DocumentObject>(getExtendedContainer());
ret = const_cast<DocumentObject*>(obj);
return true;
}
dot=strchr(subname,'.');
if(!dot)
return false;
if(subname[0]!='$')
ret = Group.find(std::string(subname,dot));
else{
std::string name = std::string(subname+1,dot);
for(auto child : Group.getValues()) {
if(name == child->Label.getStrValue()){
ret = child;
break;
}
}
}
if(!ret)
return false;
return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
}
bool GroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
for(auto obj : Group.getValues()) {
if(obj && obj->getNameInDocument())
ret.push_back(std::string(obj->getNameInDocument())+'.');
}
return true;
}
namespace App {
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GroupExtensionPython, App::GroupExtension)

View File

@@ -38,6 +38,7 @@ class GroupExtensionPy;
class AppExport GroupExtension : public DocumentObjectExtension
{
EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::GroupExtension);
typedef DocumentObjectExtension inherited;
public:
/// Constructor
@@ -91,7 +92,7 @@ public:
bool isChildOf(const GroupExtension* group, bool recursive = true) const;
/** Returns a list of all objects this group does have.
*/
std::vector<DocumentObject*> getObjects() const;
const std::vector<DocumentObject*> &getObjects() const;
/** Returns a list of all objects of \a typeId this group does have.
*/
std::vector<DocumentObject*> getObjectsOfType(const Base::Type& typeId) const;
@@ -109,7 +110,12 @@ public:
virtual PyObject* getExtensionPyObject(void) override;
virtual void extensionOnChanged(const Property* p) override;
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const override;
virtual bool extensionGetSubObjects(std::vector<std::string> &ret, int reason) const override;
/// Properties
PropertyLinkList Group;

View File

@@ -69,7 +69,7 @@ App::OriginFeature *Origin::getOriginFeature( const char *role) const {
} else {
std::stringstream err;
err << "Origin \"" << getNameInDocument () << "\" doesn't contain feature with role \""
err << "Origin \"" << getFullName () << "\" doesn't contain feature with role \""
<< role << '"';
throw Base::RuntimeError ( err.str().c_str () );
}
@@ -81,7 +81,7 @@ App::Line *Origin::getAxis( const char *role ) const {
return static_cast<App::Line *> (feat);
} else {
std::stringstream err;
err << "Origin \"" << getNameInDocument () << "\" contains bad Axis object for role \""
err << "Origin \"" << getFullName () << "\" contains bad Axis object for role \""
<< role << '"';
throw Base::RuntimeError ( err.str().c_str () );
}
@@ -93,7 +93,7 @@ App::Plane *Origin::getPlane( const char *role ) const {
return static_cast<App::Plane *> (feat);
} else {
std::stringstream err;
err << "Origin \"" << getNameInDocument () << "\" comtains bad Plane object for role \""
err << "Origin \"" << getFullName () << "\" comtains bad Plane object for role \""
<< role << '"';
throw Base::RuntimeError ( err.str().c_str () );
}

View File

@@ -53,19 +53,42 @@ App::Origin *OriginGroupExtension::getOrigin () const {
if ( !originObj ) {
std::stringstream err;
err << "Can't find Origin for \"" << getExtendedObject()->getNameInDocument () << "\"";
err << "Can't find Origin for \"" << getExtendedObject()->getFullName () << "\"";
throw Base::RuntimeError ( err.str().c_str () );
} else if (! originObj->isDerivedFrom ( App::Origin::getClassTypeId() ) ) {
std::stringstream err;
err << "Bad object \"" << originObj->getNameInDocument () << "\"(" << originObj->getTypeId().getName()
<< ") linked to the Origin of \"" << getExtendedObject()->getNameInDocument () << "\"";
err << "Bad object \"" << originObj->getFullName () << "\"(" << originObj->getTypeId().getName()
<< ") linked to the Origin of \"" << getExtendedObject()->getFullName () << "\"";
throw Base::RuntimeError ( err.str().c_str () );
} else {
return static_cast<App::Origin *> ( originObj );
}
}
bool OriginGroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
{
App::DocumentObject *originObj = Origin.getValue ();
const char *dot;
if(originObj && originObj->getNameInDocument() &&
subname && (dot=strchr(subname,'.')))
{
bool found;
if(subname[0] == '$')
found = std::string(subname+1,dot)==originObj->Label.getValue();
else
found = std::string(subname,dot)==originObj->getNameInDocument();
if(found) {
if(mat && transform)
*mat *= const_cast<OriginGroupExtension*>(this)->placement().getValue().toMatrix();
ret = originObj->getSubObject(dot+1,pyObj,mat,true,depth+1);
return true;
}
}
return GeoFeatureGroupExtension::extensionGetSubObject(ret,subname,pyObj,mat,transform,depth);
}
App::DocumentObject *OriginGroupExtension::getGroupOfObject (const DocumentObject* obj) {
if(!obj)

View File

@@ -67,6 +67,9 @@ public:
virtual std::vector<DocumentObject*> addObjects(std::vector<DocumentObject*> obj) override;
virtual bool hasObject(const DocumentObject* obj, bool recursive = false) const override;
virtual bool extensionGetSubObject(DocumentObject *&ret, const char *subname, PyObject **pyObj,
Base::Matrix4D *mat, bool transform, int depth) const override;
protected:
/// Checks integrity of the Origin
virtual App::DocumentObjectExecReturn *extensionExecute () override;

View File

@@ -26,6 +26,8 @@
#ifndef _PreComp_
#endif
#include "OCCError.h"
#include "PartPyCXX.h"
#include "DatumFeature.h"
@@ -58,6 +60,31 @@ TopoDS_Shape Datum::getShape() const
return sh.getShape();
}
App::DocumentObject *Datum::getSubObject(const char *subname,
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
{
// For the sake of simplicity, we don't bother to check for subname, just
// return the shape as it is, because a datum object only holds shape with
// one single geometry element.
(void)subname;
(void)depth;
if(pmat && transform)
*pmat *= Placement.getValue().toMatrix();
if(!pyObj)
return const_cast<Datum*>(this);
Base::PyGILStateLocker lock;
PY_TRY {
TopoShape ts(getShape().Located(TopLoc_Location()));
if(pmat && !ts.isNull())
ts.transformShape(*pmat,false,true);
*pyObj = Py::new_reference_to(shape2pyshape(ts.getShape()));
return const_cast<Datum*>(this);
} PY_CATCH_OCC
}
Base::Vector3d Datum::getBasePoint () const {
return Placement.getValue().getPosition();
}

View File

@@ -49,11 +49,13 @@ public:
virtual const char* getViewProviderName(void) const = 0;
/// Return a shape including Placement representing the datum feature
TopoDS_Shape getShape() const;
virtual TopoDS_Shape getShape() const;
/// Returns a point of the feature it counts as it's base
virtual Base::Vector3d getBasePoint () const;
virtual App::DocumentObject *getSubObject(const char *subname, PyObject **pyObj,
Base::Matrix4D *mat, bool transform, int depth) const override;
protected:
void onDocumentRestored();
void handleChangedPropertyName(Base::XMLReader &reader, const char* TypeName, const char* PropName);

View File

@@ -42,7 +42,8 @@
# include <Bnd_Box.hxx>
# include <BRepBndLib.hxx>
# include <BRepExtrema_DistShapeShape.hxx>
# include <BRepAdaptor_Curve.hxx>
# include <TopoDS.hxx>
# include <GProp_GProps.hxx>
# include <BRepGProp.hxx>
# include <gce_MakeLin.hxx>
@@ -59,13 +60,18 @@
#include <Base/Stream.h>
#include <Base/Placement.h>
#include <Base/Rotation.h>
#include <App/Application.h>
#include <App/FeaturePythonPyImp.h>
#include <App/Document.h>
#include "PartPyCXX.h"
#include "PartFeature.h"
#include "PartFeaturePy.h"
#include "TopoShapePy.h"
using namespace Part;
FC_LOG_LEVEL_INIT("Part",true,true);
PROPERTY_SOURCE(Part::Feature, App::GeoFeature)
@@ -112,20 +118,74 @@ PyObject *Feature::getPyObject(void)
return Py::new_reference_to(PythonObject);
}
std::vector<PyObject *> Feature::getPySubObjects(const std::vector<std::string>& NameVec) const
App::DocumentObject *Feature::getSubObject(const char *subname,
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
{
try {
std::vector<PyObject *> temp;
for (std::vector<std::string>::const_iterator it=NameVec.begin();it!=NameVec.end();++it) {
PyObject *obj = Shape.getShape().getPySubShape((*it).c_str());
if (obj)
temp.push_back(obj);
}
return temp;
// having '.' inside subname means it is referencing some children object,
// instead of any sub-element from ourself
if(subname && !Data::ComplexGeoData::isMappedElement(subname) && strchr(subname,'.'))
return App::DocumentObject::getSubObject(subname,pyObj,pmat,transform,depth);
if(pmat && transform)
*pmat *= Placement.getValue().toMatrix();
if(!pyObj) {
#if 0
if(subname==0 || *subname==0 || Shape.getShape().hasSubShape(subname))
return const_cast<Feature*>(this);
return nullptr;
#else
// TopoShape::hasSubShape is kind of slow, let's cut outself some slack here.
return const_cast<Feature*>(this);
#endif
}
catch (Standard_Failure&) {
//throw Py::ValueError(e.GetMessageString());
return std::vector<PyObject *>();
try {
TopoShape ts(Shape.getShape());
bool doTransform = pmat && *pmat!=ts.getTransform();
if(doTransform) {
ts.setShape(ts.getShape().Located(TopLoc_Location()),false);
ts.initCache(1);
}
if(subname && *subname && !ts.isNull())
ts = ts.getSubTopoShape(subname,true);
if(doTransform && !ts.isNull()) {
static int sCopy = -1;
if(sCopy<0) {
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Part/General");
sCopy = hGrp->GetBool("CopySubShape",false)?1:0;
}
bool copy = sCopy?true:false;
if(!copy) {
// Work around OCC bug on transforming circular edge with an
// offseted surface. The bug probably affect other shape type,
// too.
TopExp_Explorer exp(ts.getShape(),TopAbs_EDGE);
if(exp.More()) {
auto edge = TopoDS::Edge(exp.Current());
exp.Next();
if(!exp.More()) {
BRepAdaptor_Curve curve(edge);
copy = curve.GetType() == GeomAbs_Circle;
}
}
}
ts.transformShape(*pmat,copy,true);
}
*pyObj = Py::new_reference_to(shape2pyshape(ts));
return const_cast<Feature*>(this);
}catch(Standard_Failure &e) {
std::ostringstream str;
Standard_CString msg = e.GetMessageString();
str << typeid(e).name() << " ";
if (msg) {str << msg;}
else {str << "No OCCT Exception Message";}
str << ": " << getFullName();
if (subname)
str << '.' << subname;
FC_ERR(str.str());
return 0;
}
}

View File

@@ -31,6 +31,8 @@
#include <App/PropertyGeo.h>
// includes for findAllFacesCutBy()
#include <TopoDS_Face.hxx>
#include <BRep_Builder.hxx>
#include <TopoDS_Compound.hxx>
class gp_Dir;
class BRepBuilderAPI_MakeShape;
@@ -63,10 +65,12 @@ public:
virtual const App::PropertyComplexGeoData* getPropertyOfGeometry() const;
virtual PyObject* getPyObject(void);
virtual std::vector<PyObject *> getPySubObjects(const std::vector<std::string>&) const;
TopLoc_Location getLocation() const;
virtual DocumentObject *getSubObject(const char *subname, PyObject **pyObj,
Base::Matrix4D *mat, bool transform, int depth) const override;
protected:
/// recompute only this object
virtual App::DocumentObjectExecReturn *recompute(void);

View File

@@ -497,3 +497,29 @@ PyObject *Body::getPyObject(void)
}
return Py::new_reference_to(PythonObject);
}
std::vector<std::string> Body::getSubObjects(int reason) const {
if(reason==GS_SELECT && !showTip)
return Part::BodyBase::getSubObjects(reason);
return {};
}
App::DocumentObject *Body::getSubObject(const char *subname,
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
{
if(!pyObj || showTip ||
(subname && !Data::ComplexGeoData::isMappedElement(subname) && strchr(subname,'.')))
return Part::BodyBase::getSubObject(subname,pyObj,pmat,transform,depth);
// We return the shape only if there are feature visibile inside
for(auto obj : Group.getValues()) {
if(obj->Visibility.getValue() &&
obj->isDerivedFrom(PartDesign::Feature::getClassTypeId()))
{
return Part::BodyBase::getSubObject(subname,pyObj,pmat,transform,depth);
}
}
if(pmat && transform)
*pmat *= Placement.getValue().toMatrix();
return const_cast<Body*>(this);
}

View File

@@ -120,6 +120,13 @@ public:
PyObject *getPyObject(void) override;
virtual std::vector<std::string> getSubObjects(int reason=0) const override;
virtual App::DocumentObject *getSubObject(const char *subname,
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const override;
void setShowTip(bool enable) {
showTip = enable;
}
protected:
virtual void onSettingDocument() override;
@@ -146,6 +153,7 @@ protected:
private:
boost::signals2::scoped_connection connection;
bool showTip = false;
};
} //namespace PartDesign

View File

@@ -28,6 +28,8 @@
# include <gp_Pln.hxx>
#endif
#include <Mod/Part/App/PartPyCXX.h>
#include <Mod/Part/App/OCCError.h>
#include "DatumCS.h"
using namespace PartDesign;
@@ -77,3 +79,32 @@ Base::Vector3d CoordinateSystem::getZAxis()
rot.multVec(Base::Vector3d(0,0,1), normal);
return normal;
}
App::DocumentObject *CoordinateSystem::getSubObject(const char *subname,
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int) const
{
if(pmat && transform)
*pmat *= Placement.getValue().toMatrix();
if(!pyObj)
return const_cast<CoordinateSystem*>(this);
gp_Dir dir(0,0,1);
if(subname) {
if(strcmp(subname,"X")==0)
dir = gp_Dir(1,0,0);
else if(strcmp(subname,"Y")==0)
dir = gp_Dir(0,1,0);
}
Base::PyGILStateLocker lock;
PY_TRY {
BRepBuilderAPI_MakeFace builder(gp_Pln(gp_Pnt(0,0,0), dir));
Part::TopoShape ts(builder.Shape());
if(pmat)
ts.transformShape(*pmat,false,true);
*pyObj = Py::new_reference_to(Part::shape2pyshape(ts));
return const_cast<CoordinateSystem*>(this);
} PY_CATCH_OCC
}

View File

@@ -44,6 +44,9 @@ public:
Base::Vector3d getXAxis();
Base::Vector3d getYAxis();
Base::Vector3d getZAxis();
virtual App::DocumentObject *getSubObject(const char *subname,
PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const override;
};
} //namespace PartDesign