Merge pull request #25195 from pieterhijma/doc-document

Doc: Improve the documentation of Document
This commit is contained in:
Chris Hennes
2026-01-29 13:29:43 +01:00
committed by GitHub
11 changed files with 2185 additions and 734 deletions

View File

@@ -461,16 +461,18 @@ public:
/** @name Link handling */
//@{
/** Check for link recursion depth
/**
* @brief Check for link recursion depth.
*
* The function uses an internal count of all objects in all documents as
* the limit of recursion depth. If the depth exceeds this limit, it means
* that there is likely a cyclic reference in the links.
*
* @param depth: current depth
* @param option: whether to throw exception, print an error message or quieten any output.
* In the latter case the caller must check the returned value.
*
* @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, MessageOption option = MessageOption::Error);

View File

@@ -1251,12 +1251,12 @@ constexpr auto fcAttrDepObjName {"Name"};
constexpr auto fcAttrDepAllowPartial {"AllowPartial"};
constexpr auto fcElementObjectDep {"Dep"};
void Document::writeObjects(const std::vector<DocumentObject*>& obj,
void Document::writeObjects(const std::vector<DocumentObject*>& objs,
Base::Writer& writer) const
{
// writing the features types
writer.incInd(); // indentation for 'Objects count'
writer.Stream() << writer.ind() << "<Objects Count=\"" << obj.size();
writer.Stream() << writer.ind() << "<Objects Count=\"" << objs.size();
if (isExporting(nullptr) == 0U) {
writer.Stream() << "\" " << fcAttrDependencies << "=\"1";
}
@@ -1265,7 +1265,7 @@ void Document::writeObjects(const std::vector<DocumentObject*>& obj,
writer.incInd(); // indentation for 'Object type'
if (isExporting(nullptr) == 0U) {
for (const auto o : obj) {
for (const auto o : objs) {
const auto& outList =
o->getOutList(DocumentObject::OutListNoHidden | DocumentObject::OutListNoXLinked);
writer.Stream() << writer.ind()
@@ -1294,7 +1294,7 @@ void Document::writeObjects(const std::vector<DocumentObject*>& obj,
}
std::vector<DocumentObject*>::const_iterator it;
for (it = obj.begin(); it != obj.end(); ++it) {
for (it = objs.begin(); it != objs.end(); ++it) {
writer.Stream() << writer.ind() << "<Object "
<< "type=\"" << (*it)->getTypeId().getName() << "\" "
<< "name=\"" << (*it)->getExportName() << "\" "
@@ -1324,10 +1324,10 @@ void Document::writeObjects(const std::vector<DocumentObject*>& obj,
writer.Stream() << writer.ind() << "</Objects>" << '\n';
// writing the features itself
writer.Stream() << writer.ind() << "<ObjectData Count=\"" << obj.size() << "\">" << '\n';
writer.Stream() << writer.ind() << "<ObjectData Count=\"" << objs.size() << "\">" << '\n';
writer.incInd(); // indentation for 'Object name'
for (it = obj.begin(); it != obj.end(); ++it) {
for (it = objs.begin(); it != objs.end(); ++it) {
writer.Stream() << writer.ind() << "<Object name=\"" << (*it)->getExportName() << "\"";
if ((*it)->hasExtensions()) {
writer.Stream() << " Extensions=\"True\"";
@@ -2036,11 +2036,11 @@ bool Document::afterRestore(const std::vector<DocumentObject*>& objArray, bool c
return false;
}
// some link type property cannot restore link information until other
// objects has been restored. For example, PropertyExpressionEngine and
// PropertySheet with expression containing label reference. So we add the
// Property::afterRestore() interface to let them sort it out. Note, this
// API is not called in object dedpenency order, because the order
// Some link type properties cannot restore link information until other
// objects have been restored. For example, PropertyExpressionEngine and
// PropertySheet with expressions containing a label reference. So we add
// the Property::afterRestore() interface to let them sort it out. Note,
// this API is not called in object dependency order, because the order
// information is not ready yet.
std::map<DocumentObject*, std::vector<Property*>> propMap;
for (auto obj : objArray) {
@@ -2228,7 +2228,7 @@ vector<DocumentObject*> Document::getTouched() const
return result;
}
void Document::setClosable(const bool c) // NOLINT
void Document::setClosable(bool c) // NOLINT
{
setStatus(Document::Closable, c);
}
@@ -2508,7 +2508,7 @@ std::vector<Document*> Document::getDependentDocuments(const bool sort)
}
std::vector<Document*> Document::getDependentDocuments(std::vector<Document*> docs,
const bool sort)
const bool sort)
{
DependencyList depList;
std::map<Document*, Vertex> docMap;
@@ -2572,11 +2572,6 @@ std::vector<Document*> Document::getDependentDocuments(std::vector<Document*> do
return ret;
}
void Document::_rebuildDependencyList(const std::vector<DocumentObject*>& objs)
{
(void)objs;
}
/**
* @brief Signal that object identifiers, typically a property or document object has been renamed.
*
@@ -3144,15 +3139,15 @@ Document::addObjects(const char* sType, const std::vector<std::string>& objectNa
return objects;
}
void Document::addObject(DocumentObject* pcObject, const char* pObjectName)
void Document::addObject(DocumentObject* obj, const char* name)
{
if (pcObject->getDocument()) {
if (obj->getDocument()) {
throw Base::RuntimeError("Document object is already added to a document");
}
pcObject->setDocument(this);
obj->setDocument(this);
_addObject(pcObject, pObjectName, AddObjectOption::SetNewStatus | AddObjectOption::ActivateObject);
_addObject(obj, name, AddObjectOption::SetNewStatus | AddObjectOption::ActivateObject);
}
void Document::_addObject(DocumentObject* pcObject, const char* pObjectName, AddObjectOptions options, const char* viewType)

File diff suppressed because it is too large Load Diff

View File

@@ -206,13 +206,6 @@ bool DocumentObject::recomputeFeature(bool recursive)
return isValid();
}
/**
* @brief Set this document object touched.
* Touching a document object does not mean to recompute it, it only means that
* other document objects that link it (i.e. its InList) will be recomputed.
* If it should be forced to recompute a document object then use
* \ref enforceRecompute() instead.
*/
void DocumentObject::touch(bool noRecompute)
{
if (!noRecompute) {
@@ -224,10 +217,6 @@ void DocumentObject::touch(bool noRecompute)
}
}
/**
* @brief Set this document object freezed.
* A freezed document object does not recompute ever.
*/
void DocumentObject::freeze()
{
StatusBits.set(ObjectStatus::Freeze);
@@ -250,10 +239,6 @@ void DocumentObject::freeze()
}
}
/**
* @brief Set this document object unfreezed.
* A freezed document object does not recompute ever.
*/
void DocumentObject::unfreeze(bool noRecompute)
{
StatusBits.reset(ObjectStatus::Freeze);
@@ -271,31 +256,16 @@ void DocumentObject::unfreeze(bool noRecompute)
touch(noRecompute);
}
/**
* @brief Check whether the document object is touched or not.
* @return true if document object is touched, false if not.
*/
bool DocumentObject::isTouched() const
{
return ExpressionEngine.isTouched() || StatusBits.test(ObjectStatus::Touch);
}
/**
* @brief Enforces this document object to be recomputed.
* This can be useful to recompute the feature without
* having to change one of its input properties.
*/
void DocumentObject::enforceRecompute()
{
touch(false);
}
/**
* @brief Check whether the document object must be recomputed or not.
* This means that the 'Enforce' flag is set or that \ref mustExecute()
* returns a value > 0.
* @return true if document object must be recomputed, false if not.
*/
bool DocumentObject::mustRecompute() const
{
if (StatusBits.test(ObjectStatus::Freeze)) {
@@ -646,7 +616,7 @@ bool DocumentObject::isInOutListRecursive(DocumentObject* linkTo) const
}
std::vector<std::list<App::DocumentObject*>>
DocumentObject::getPathsByOutList(App::DocumentObject* to) const
DocumentObject::getPathsByOutList(const App::DocumentObject* to) const
{
return _pDoc->getPathsByOutList(this, to);
}
@@ -1210,33 +1180,16 @@ void DocumentObject::Save(Base::Writer& writer) const
App::ExtensionContainer::Save(writer);
}
/**
* @brief Associate the expression \expr with the object identifier \a path in this document object.
* @param path Target object identifier for the result of the expression
* @param expr Expression tree
*/
void DocumentObject::setExpression(const ObjectIdentifier& path, std::shared_ptr<Expression> expr)
{
ExpressionEngine.setValue(path, std::move(expr));
}
/**
* @brief Clear the expression of the object identifier \a path in this document object.
* @param path Target object identifier
*/
void DocumentObject::clearExpression(const ObjectIdentifier& path)
{
setExpression(path, std::shared_ptr<Expression>());
}
/**
* @brief Get expression information associated with \a path.
* @param path Object identifier
* @return Expression info, containing expression and optional comment.
*/
const PropertyExpressionEngine::ExpressionInfo
DocumentObject::getExpression(const ObjectIdentifier& path) const
{
@@ -1250,13 +1203,6 @@ DocumentObject::getExpression(const ObjectIdentifier& path) const
}
}
/**
* @brief Invoke ExpressionEngine's renameObjectIdentifier, to possibly rewrite expressions using
* the \a paths map with current and new identifiers.
*
* @param paths
*/
void DocumentObject::renameObjectIdentifiers(
const std::map<ObjectIdentifier, ObjectIdentifier>& paths)
{

File diff suppressed because it is too large Load Diff

View File

@@ -97,10 +97,31 @@ public:
*/
virtual bool extensionGetSubObjects(std::vector<std::string>& ret, int reason) const;
/** Get the linked object
* @sa DocumentObject::getLinkedObject()
/**
* @brief Get the linked object of this extension.
*
* @return Return turn if handled, the linked object is returned in \c ret
* This method returns the linked object of this document object. If there
* is no linked object, it will return the container object of the
* extension.
*
* @param[out] ret The linked object is returned in this parameter.
*
* @param[in] recurse If true, it will recursively resolve the link until it
* reaches the final linked object.
*
* @param mat[in,out] If non-null, it is used as the current transformation
* matrix on input. On output it is used as the accumulated transformation
* up until the final linked object.
*
* @param[in] transform If false, then it will not accumulate the object's own
* placement into @p mat, which lets you override the object's placement.
*
* @param[in] depth This parameter indicates the level on which we are
* resolving the link.
*
* @return Returns true if the linked object is successfully retrieved and
* returned in @p ret. If the linked object is not found or is invalid, it
* returns false.
*/
virtual bool extensionGetLinkedObject(DocumentObject*& ret,
bool recursive,

View File

@@ -97,7 +97,6 @@ void Document::exportGraphviz(std::ostream& out) const
* This class creates the dependency graph for a document.
*
*/
class GraphCreator
{
public:
@@ -132,7 +131,6 @@ void Document::exportGraphviz(std::ostream& out) const
* @param docObj Document object to get an ID from
* @return A string
*/
std::string getId(const DocumentObject* docObj)
{
std::string id;
@@ -150,7 +148,6 @@ void Document::exportGraphviz(std::ostream& out) const
* @param path
* @return A string
*/
std::string getId(const ObjectIdentifier& path)
{
DocumentObject* docObj = path.getDocumentObject();
@@ -183,7 +180,6 @@ void Document::exportGraphviz(std::ostream& out) const
* @brief setGraphAttributes Set graph attributes on a subgraph for a DocumentObject node.
* @param obj DocumentObject
*/
void setGraphAttributes(const DocumentObject* obj)
{
assert(GraphList.find(obj) != GraphList.end());
@@ -201,7 +197,6 @@ void Document::exportGraphviz(std::ostream& out) const
* @param vertex Property node
* @param name Name of node
*/
void setPropertyVertexAttributes(Graph& g, Vertex vertex, const std::string& name)
{
get(vertex_attribute, g)[vertex]["label"] = name;
@@ -217,7 +212,6 @@ void Document::exportGraphviz(std::ostream& out) const
* @param obj DocumentObject to assess.
* @param CSSubgraphs Boolean if the GeoFeatureGroups are created as subgraphs
*/
void addExpressionSubgraphIfNeeded(DocumentObject* obj, bool CSsubgraphs)
{
@@ -283,7 +277,6 @@ void Document::exportGraphviz(std::ostream& out) const
* @param docObj The document object to add.
* @param name Name of node.
*/
void add(DocumentObject* docObj,
const std::string& name,
const std::string& label,

View File

@@ -341,6 +341,17 @@ public:
const char* flattenSubname(const char* subname) const;
void expandSubname(std::string& subname) const;
/**
* @brief Get the object the link points to.
*
* This method returns the linked object of this link. The @p depth
* parameter is used as an indication of how deep the recursion is as a
* safeguard against infinite recursion caused by cyclic dependencies.
*
* @param[in] depth The level on which we are resolving the link.
*
* @return Returns the linked object or @c nullptr if there is no linked value.
*/
DocumentObject* getLink(int depth = 0) const;
Base::Matrix4D getTransform(bool transform) const;
@@ -403,6 +414,33 @@ public:
const char* subname = nullptr,
const std::vector<std::string>& subs = std::vector<std::string>());
/**
* @brief Get the true linked object.
*
* This method returns the true linked object, which is the object that the
* link points to. The @p depth parameter is used as an indication of the
* current level of recursion is as a safeguard against infinite recursion
* caused by cyclic dependencies.
*
* @param recurse If true, it will recursively resolve the link until it reaches
* the final linked object, or until it reaches the maximum recursion depth.
*
* @param mat If non-null, it is used as the current transformation matrix on
* input. On output it is used as the accumulated transformation up until
* the final linked object.
*
* @param depth This parameter indicates the level on which we are
* resolving the link.
*
* @param noElement If true, it will not return the linked object if it is
* a link to an element.
*
* @return Returns the true linked object. If the linked object is not found
* or is invalid, it returns @c nullptr.
*
* @sa DocumentObject::getLinkedObject() which may return itself if it is not a link.
*
*/
DocumentObject* getTrueLinkedObject(bool recurse,
Base::Matrix4D* mat = nullptr,
int depth = 0,

View File

@@ -100,7 +100,6 @@ public:
/**
* @brief The ExpressionInfo struct encapsulates an expression.
*/
struct ExpressionInfo
{
std::shared_ptr<App::Expression> expression; /**< The actual expression tree */

View File

@@ -262,14 +262,20 @@ public:
return nullptr;
}
/** Update object label reference in this property
/**
* @brief Update the object label reference of this property.
*
* @param obj: the object owner of the changing label
* @param ref: subname reference to old label
* @param newLabel: the future new label
* This method does not update the property itself, but returns a copy of
* the property with the updated label reference if the property is
* affected. The copy will later be assigned to this property by calling its
* Paste() method.
*
* @return Returns a copy of the property if its link reference is affected.
* The copy will later be assgiend to this property by calling its Paste().
* @param[in] obj The object owner of the changing label.
* @param[in] ref The subname reference to old label.
* @param[in] newLabel The future new label.
*
* @return A copy of the property if its link reference is affected,
* otherwise `nullptr`.
*/
virtual Property*
CopyOnLabelChange(App::DocumentObject* obj, const std::string& ref, const char* newLabel) const
@@ -556,13 +562,17 @@ public:
*/
static void getLabelReferences(std::vector<std::string>& labels, const char* subname);
/** Helper function to collect changed property when an object re-label
/**
* @brief Update label references on label change of a document object.
*
* @param obj: the object that owns the label
* @param newLabel: the new label
* This helper function collects changed properties when an object re-label
* takes place. It returns a map from affected properties to copies of
* those affectedd propertiess with updated subname references.
*
* @return return a map from the affected property to a copy of it with
* updated subname references
* @param[in] obj The object that owns the label that is changed.
* @param[in] newLabel The new label of the object.
*
* @return The map from affected property to a copy of it.
*/
static std::vector<std::pair<Property*, std::unique_ptr<Property>>>
updateLabelReferences(App::DocumentObject* obj, const char* newLabel);

View File

@@ -19,36 +19,225 @@
/**
* @defgroup DocumentGroup Document
* @ingroup APP
* @brief The class that represents a FreeCAD document
* @brief The class that represents a FreeCAD document.
*
* This (besides the App::Application class) is the most important class in FreeCAD.
* It contains all the data of the opened, saved, or newly created FreeCAD Document.
* The App::Document manages the Undo and Redo mechanism and the linking of documents.
* This (besides the App::Application class) is the most important class in
* FreeCAD. It contains all the data of the opened, saved, or newly created
* FreeCAD %Document. The App::Document class manages the undo and redo
* mechanism and linking of documents.
*
* Note: the documents are not free objects. They are completely handled by the
* App::Application. Only the Application can Open or destroy a document.
* Note: Documents are not freestanding objects. They are completely handled by the
* App::Application class. Only the application can open or destroy a document.
*
* \section Exception Exception handling
* As the document is the main data structure of FreeCAD we have to take a close
* look at how Exceptions affect the integrity of the App::Document.
* @section SecDocumentProperties Documents as property containers
*
* \section UndoRedo Undo Redo an Transactions
* Undo Redo handling is one of the major mechanism of a document in terms of
* user friendliness and speed (no one will wait for Undo too long).
* The main role of a document is being a container of @ref DocumentObjectGroup
* "DocumentObjects". Both a @ref DocumentObjectGroup "DocumentObject" and a
* @ref App::Document "Document" are @ref PropertyFramework
* "PropertyContainers" and can hold properties.
*
* \section Dependency Graph and dependency handling
* The FreeCAD document handles the dependencies of its DocumentObjects with
* an adjacency list. This gives the opportunity to calculate the shortest
* recompute path. Also, it enables more complicated dependencies beyond trees.
* However, there is a crucial difference between the properties in a document
* and in a document object. The properties in a document cannot be the target
* of expressions and they do not take part in the dependency check mechanism.
* This means that if a document object references a property of a document and
* the document property is changed, the document object that references the
* document property will not be marked to be recomputed. As such, the
* properties in a document should be regarded as simple properties for
* file-related information.
*
* @see App::Application
* @see App::DocumentObject
* @section SecObjectManagement Object management
*
* As mentioned above, a document is a container of document objects. Document
* objects can only exist inside a document and the document provides
* functionality to create, remove, and access document objects. For example,
* it is possible to get objects of a specific type or with a specific
* extension.
*
* The document also manages the names and labels document objects, ensuring
* that names are unique and that labels are unique if configured to have
* unique labels.
*
* @section DependencyGraph Dependencies between document objects
*
* Objects can link to other objects in the same document or in other documents
* and as such dependencies between document objects come into existence. The
* document is responsible for recomputing document objects if some property of
* a document object changes. The dependencies between document objects
* determine in which order the objects must be recomputed.
*
* To compute the objects in the right order, the document computes a
* dependency graph based on the information contained in the OutLists and
* InLists of the document objects. For more information on OutLists and
* InLists, see @ref SecDocumentObjectRecompute "Document Objects". Given the
* dependency graph, the document computes the list of objects in topological
* order (not to be confused with topological naming) which means that the
* objects without dependencies come first and then the objects that depend on
* these objects, etc.
*
* The document will check for each object in the list whether the object is
* "touched", which means that it is marked to be recomputed. If so, the
* object is recomputed and the objects in the InList of that object are
* touched as well. Because of the topological order, these objects will come
* in a later iteration.
*
* This mechanism allows FreeCAD to recompute only the objects that need to be
* recomputed and in only one pass over the list of objects. However, it is
* important that the dependency graph does not contain cycles and is a
* "Directed Acyclic Graph" or DAG. As such, cyclic dependencies are not
* allowed in FreeCAD.
*
* @section SecUndoRedo Undo Redo and Transactions
*
* Another important responsbility of documents is to manage undo and redo
* actions. The information to support undo and redo is captured in
* transactions. It is important to understand that transactions are a feature
* of document objects and not of documents. This means that a change of a
* property inside a document (as opposed to inside a document object) is not
* captured by the transaction system (similar to the fact that document
* properties do not support expressions).
*
* The transaction system is quite complex and acts on various levels.
* Although the transactions themselves only capture changes regarding document
* objects, the transactions are managed in the document. Although the
* transactions are managed inside a document, it is possible to support
* transactions that affect multiple documents. This is managed by the GUI and
* if a change affects multiple documents, then all documents will store
* transactions that change its document and they will all have the same
* transaction ID.
*
* The mechanisms for undo and redo are part of the @ref APP "App" level, but
* the GUI defines the transactions (where to open a transaction, where to
* commit or abort) and it initiates the undo and redo actions.
*
* The transaction system makes use of classes with very similar names which
* can be confusing. Here is a short overview:
*
* - A @ref App::TransactionalObject "TransactionalObject" is a base class that
* provides the functionality to support transactions. It is inherited by
* @ref App::DocumentObject "DocumentObject".
* - A @ref App::TransactionObject "TransactionObject" is a class that
* represents a single action and can express adding a new object, removing
* an object or changing an object. Subclasses of the transaction object are
* @ref App::TransactionDocumentObject "TransactionDocumentObject" and
* @ref Gui::TransactionViewProvider "Gui::TransactionViewProvider".
* - A @ref App::Transaction "Transaction" captures a single undo or redo
* action and consists of multiple @ref App::TransactionObject
* "TransactionObjects" associated with their @ref App::TransactionalObject
* "TransactionalObject", often a @ref App::DocumentObject "DocumentObject".
*/
/**
* @defgroup DocumentObjectGroup Document Object
* @ingroup APP
* @brief %Base class of all objects handled in the Document.
* @brief %Base class of all objects handled in the @ref App::Document "Document".
*
* A DocumentObject is the base class of all objects that can be contained in a
* FreeCAD @ref App::Document "Document". Document objects can represent any
* object that can be created in a FreeCAD Document, such as features:
* Part::Feature, Mesh::Feature, Sketcher::SketchObject, but also other objects
* such as App::Link, App::DocumentObjectGroup, App::VarSet, or
* SpreadSheet::Sheet that do not necessary represent geometry.
*
* Document objects provide the functionality to handle properties (it extends
* @ref App::PropertyContainer "PropertyContainer"), transactions (it extends
* @ref App::TransactionalObject "TransactionalObject") and extensions (it
* extends @ref App::ExtensionContainer "ExtensionContainer"), and expressions.
* Expressions are managed by the property @ref
* App::DocumentObject::ExpressionEngine "ExpressionEngine", which is a special
* type of a link property that contains expressions that may link to other
* document objects.
*
* @section SecDocumentObjectRecompute Recomputing Document Objects
*
* Objects can be recomputed, which means that the properties of the object are
* recomputed. Since properties may depend on other document objects, for
* example because of the @ref App::DocumentObject::ExpressionEngine
* "ExpressionEngine" property, these document objects have to be computed
* first. Managing the order of recomputation is managed by the @ref
* App::Document "Document" which calls the @ref
* App::DocumentObject::recompute() "recompute()" on the document object. To
* signal that a document object needs to be recomputed, the document object
* can be touched (see @ref App::DocumentObject::touch() "touch()" and @ref
* App::DocumentObject::isTouched() "isTouched()").
*
* As part of recomputation, the document object can also be executed or
* "invoked", (see @ref App::DocumentObject::execute() "execute()") which
* results in recomputing the output properties of the object.
*
* Although recomputation is mostly orchestrated by the @ref App::Document
* "Document", document objects play an important role in capturing the
* dependencies between document objects in terms of OutLists and InLists.
*
* Dependencies between document objects come into existence because of links
* and if a document object `A` links to another document object `B`, then `B`
* is in the OutList of `A` and `A` is in the InList of `B`. The InList for an
* object `Obj` represents the document objects that are dependent on it. The
* outlist of an object `Obj` represent the dependencies of it. So, to
* recompute `Obj`, first the objects in the outlist need to be recomputed.
* Vice versa, if a property of an object is changed, the InList indicates
* which other objects depend on it and need to be recomputed.
*
* So, as mentioned above, links define dependencies between document objects
* These dependencies are recorded by means of setting the value of a @ref
* App::PropertyLink "PropertyLink" (and similar properties) in a document
* object `Obj` to a document object `Value`. Within @ref
* App::PropertyLink::setValue() "PropertyLink::setValue()" (and similar
* methods for other link properties), the (internal) methods _addBackLink() or
* _removeBackLink() will be called on `Value` with as argument the container
* of the property link `Obj`, indicating that `Obj` depends on `Value`. With
* this methodology, each document object will build up its own InList that can
* be used during recomputation to query which other document objects need to
* be computed.
*
* The OutList and InList can be accessed by the methods @ref
* App::DocumentObject::getOutList() "getOutList()" and @ref
* App::DocumentObject::getInList() "getInList()".
*
* @section SecDocumentObjectProperties Properties
*
* The document object introduces three properties:
*
* - @ref App::DocumentObject::Label "Label" which is a string that identifies
* the object in the document and is used to display the object in the GUI.
* - @ref App::DocumentObject::Label2 "Label2" which is a string that contains
* additional information about the object and is used for the description of
* the object.
* - @ref App::DocumentObject::ExpressionEngine "ExpressionEngine" which
* contains a mapping from @ref App::ObjectIdentifier "ObjectIdentifier" to
* @ref App::Expression "Expression". The object identifier defines the
* properties that store the result of the expression.
* - @ref App::DocumentObject::Visibility "Visibility" which is a boolean
* that indicates whether the object is visible in App namespace.
*
* @section SecDocumentObjectSignals Signals
*
* A document object has three signals that can be connected to:
*
* - @ref App::DocumentObject::signalBeforeChange "signalBeforeChange" which is
* emitted before a property of the document object is changed. This signal
* can be used to prepare for changes to the document object.
* - @ref App::DocumentObject::signalChanged "signalChanged" which is emitted
* after a property of the document object has changed.
* - @ref App::DocumentObject::signalEarlyChanged "signalEarlyChanged" which
* is also emitted after a property has changed but emitted right before
* "signalChanged".
*
* Note that at the @ref App::Document "Document" level, there are also similar
* signals.
*
* @section SecDocumentObjectSeparation Separation App/Gui
*
* In FreeCAD there is a strict separation between the App and Gui parts. A
* @ref App::DocumentObject "DocumentObject" typically has a Gui::ViewProvider
* class associated with it. The method @ref
* App::DocumentObject::getViewProviderName() "getViewProviderName()" defines
* what the type of the class of the view provider is, allowing the Gui to
* retrieve the type and construct a view provider.
*
* Another intresting aspect of the separation is that the property @ref
* App::DocumentObject::Visibility "Visibility" is defined both in
* App::DocumentObject and in @ref Gui::ViewProviderDocumentObject::Visibility
* "Gui::ViewProviderDocumentObject".
*/
/**