/** * @defgroup APP App * @ingroup CORE * @brief The part of FreeCAD that works without GUI (console or server mode) * * @details It contains the App namespace and defines core concepts such as * @ref DocumentGroup "Document", @ref DocumentObjectGroup "Document Object", * @ref PropertyFramework "Property Framework", @ref ExpressionFramework * "Expression Framework", and the @ref ExtensionFramework "Extension * Framework". * * The largest difference between the functionality in @ref BASE "Base" * compared to %App is that %App introduces the notion of properties, both used * in @ref App::Document "Document" and @ref App::DocumentObject * "DocumentObject". In addition, %App has a representation of the running * @ref App::Application "Application". */ /** * @defgroup DocumentGroup Document * @ingroup APP * @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. * * Note: the documents are not free objects. They are completely handled by the * App::Application. 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 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). * * \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. * * @see App::Application * @see App::DocumentObject */ /** * @defgroup DocumentObjectGroup Document Object * @ingroup APP * @brief %Base class of all objects handled in the Document. */ /** * @defgroup PropertyFramework Property framework * @ingroup APP * @brief System to access object properties. * * @section propframe_intro Introduction * * The property framework introduces an intricate system to access properties * of objects. It provides the ability to: * 1. define properties in a class and access them by name, * 2. add properties to a class at runtime and access them by name, and * 3. access properties of a class by name without knowing the class type. * * The first two points are similar to what the dynamic reflection framework of * C# or Java offer. The third point allows FreeCAD to have App::Property as a * generic interface to access properties. This is similar to the way that * Python allows to access properties of a class by name. * * This ability is introduced by the @ref App::PropertyContainer * "PropertyContainer" class and can be used by all derived classes. In * particular, there are two classes that inherit from @ref * App::PropertyContainer "PropertyContainer" which are @ref App::Document * "Document" and @ref App::DocumentObject "DocumentObject". These two classes * serve different purposes but are both able to hold properties. @ref * App::PropertyContainer "PropertyContainer" contains the shared logic to do * so. * * @section propframe_static_dynamic_props Static/Dynamic Properties * * In C++, it is possible to define properties as part of the class. These can * be considered "static" properties but this term is typically not used within * FreeCAD. Properties created in a class cannot be removed from a @ref * App::PropertyContainer "PropertyContainer". * * However, it is also possible to add properties to a @ref * App::PropertyContainer "PropertyContainer" at runtime. These properties are * called "dynamic properties" and these properties can be freely added and * removed by users. In Python, all properties are dynamic properties. * * @section propframe_mechanisms Mechanisms * * The macros `PROPERTY_HEADER` and `PROPERTY_SOURCE` (and variants) are used * to define a private static PropertyData member in the class. This data * structure contains a multi index that maps 1) the property name to the * property specifications @ref App::PropertyData::PropertySpec "PropertySpec", * allowing access of properties by name, and 2) maps the offset of a property * with respect to its container, allowing access to property specifications. * * The static function @ref App::PropertyContainer::getPropertyDataPtr() * "PropertyContainer::getPropertyDataPtr()" is used to access the class-wide * static @ref App::PropertyData "PropertyData" structure shared by all * instances of the class. The virtual function @ref * App::PropertyContainer::getPropertyData() * "PropertyContainer::getPropertyData()" allows access to the class-level * static PropertyData based on the dynamic type of the object, even when * downcasted. This allows for example a @ref App::PropertyInteger * "PropertyInteger*" downcasted to @ref App::Property "Property*" to access * all its properties. * * Since the @ref App::PropertyData "PropertyData" structure is static in the * class and maintains static information of properties, such as the group and * documentation, we need to be able to access a specific instance given a * property name. This is achieved by looking up the @ref * App::PropertyData::PropertySpec "PropertySpec" in the index based on the * property name. The property specification stores an offset to the property * address given an @ref App::PropertyData::OffsetBase "OffsetBase" which wraps * the address of a @ref App::PropertyContainer "PropertyContainer". See @ref * App::PropertyData::findProperty() "PropertyData::findProperty()" and @ref * App::PropertyData::OffsetBase::getOffsetTo() "OffsetBase::getOffsetTo()". * The offset of the property relative to the offset base gives us the address * of the property in the instance. Note that different values for properties * across property containers are stored in the @ref App::Property "Property" instances. * * The reverse is also needed: Given a property in a property container, it is * possible to compute the offset relative to the base of the property * container. The index in @ref App::PropertyData "PropertyData" allows us to * acquire the property spec and provides us with the static data associated * with the property. * * Dynamic properties are stored in their own @ref App::DynamicProperty * "DynamicProperty" container. It can be added with the function @ref * App::PropertyContainer::addDynamicProperty() * "PropertyContainer::addDynamicProperty()" and removed with @ref * App::PropertyContainer::removeDynamicProperty() * "PropertyContainer::removeDynamicProperty()". The rest of the interface of * dynamic properties is virtually the same. In general, in FreeCAD, it is * difficult for users to know whether a property is dynamic or static and they * typically do not need to be concerned with this distinction. * * @section Locking Dynamic Properties * * Since in Python all properties are dynamic properties, it is difficult to * understand whether properties are part of a Python class and are necessary * for the functioning of the class, or whether a user has added these * properties. Therefore, it is possible to indicate that a property is * "locked": * * @code * prop->setStatus(Property::LockDynamic, true); * @endcode * * In Python, this can be indicated in the `addProperty()` function: * * @code * addProperty(type: string, name: string, group="", doc="", * attr=0, read_only=False, hidden=False, * locked=False, enum_vals=[]) * @endcode * * The Property Framework makes it possible in the first place to make an * automatic mapping to python (e.g. in App::FeaturePy) and abstract editing * properties in Gui::PropertyEditor. * * @section Examples * * Here some little examples how to use the property framework: * * @code * // Acquire a property by name and return the value as a Python object. * Property *prop = feature->getPropertyByName(nameProperty); * if (prop) { * return prop->getPyObject(); * } * @endcode * * or: * * @code * // Restore properties from a reader. * void PropertyContainer::Restore(Base::Reader &reader) * { * reader.readElement("Properties"); * int Cnt = reader.getAttributeAsInteger("Count"); * * for(int i=0 ;iRestore(reader); * } * * reader.readEndElement("Property"); * } * reader.readEndElement("Properties"); * } * @endcode */ /** * @defgroup ExpressionFramework Expressions framework * @ingroup APP * @brief The expression system allows users to write expressions and formulas that produce values */ /** * @defgroup ExtensionFramework Extension framework * @ingroup APP * @brief The extension system provides a way to extend the functionality of Document Objects. * * The concept of extensions provides a way to extend the functionality of * objects in FreeCAD despite Python's limitations with multiple inheritance * (see below for an explanation). Extensions are FreeCAD objects that act * like regular objects in the sense that they have properties and class * methods to define their functionality. However, extensions are not exposed * as individual usable entities but are used to extend other objects. An * extended object obtains all the properties and methods of the extension. * * As such, it is like C++ multiple inheritance, which is indeed used to * achieve this on C++ side, but it provides a few important additional * functionalities as well: * * - Property persistence is handled: save and restore work out of the box. * - The objects Python API gets extended too with the extension Python API. * - Extensions can be added from C++ and Python, even from both together. * * The interoperability with Python is highly important since in FreeCAD, all * functionality should be easily accessible from both Python and C++. To * ensure this -- as already noted -- extensions can be added to an object from * Python. * * However, this means that it is not clear from the C++ object type whether an * extension was added or not: If added from C++, it becomes clear in the type * due to the use of multiple inheritance. If added from Python, it is a * runtime extension and not visible from the type. Hence, querying existing * extensions of an object and accessing its methods works not by type casting * but by the interface provided in ExtensionContainer. The default workflow * is to query whether an extension exists and then to get the extension * object. This interface always works the same, no matter if added from * Python or C++. * * @code * if (object->hasExtension(GroupExtension::getClassTypeId())) { * App::GroupExtension* group = object->getExtensionByType(); * group->hasObject(...); * } * @endcode * * To add an extension to an object, it must comply to a single restriction: it * must be derived from ExtensionContainer. This is important to allow adding * extensions from Python and also to access the universal extension API. As * DocumentObject itself derives from ExtensionContainer, this should be the * case automatically in most circumstances. * * Note that two small boilerplate changes are needed in addition to the * multiple inheritance when adding extensions from C++. * * 1. It must be ensured that the property and type registration is aware of * the extensions by using special macros. * 2. The extensions need to be initialised in the constructor. * * Here is a working example: * @code{.cpp} * class AppExport Part : public App::DocumentObject * , public App::FirstExtension * , public App::SecondExtension * { * PROPERTY_HEADER_WITH_EXTENSIONS(App::Part); * }; * * PROPERTY_SOURCE_WITH_EXTENSIONS(App::Part, App::DocumentObject) * * Part::Part(void) * { * FirstExtension::initExtension(this); * SecondExtension::initExtension(this); * } * @endcode * * From Python, adding an extension is easier: It must be simply registered to * a document object at object initialisation like done with properties. Note * that the special Python extension objects need to be added, not the C++ * objects. Normally the only difference in name is the additional "Python" at * the end of the extension name. * * @code{.py} * class Test(): * __init(self)__: * registerExtension("App::FirstExtensionPython", self) * registerExtension("App::SecondExtensionPython", self) * @endcode * * Extensions can provide methods that should be overridden by the extended * object for customisation of the extension behaviour. In C++ this is as * simple as overriding the provided virtual functions. In Python a class * method must be provided which has the same name as the method to override. * This method must not necessarily be in the object that is extended, it must * be in the object which is provided to the "registerExtension" call as second * argument. This second argument is used as a proxy and queried if the method * to override exists in this proxy before calling it. * * @section SecExtensionFrameworkCreateExtension Create an Extension * * An Extension is like every other FreeCAD object and is based on properties. * All information storage and persistence should be achieved by use of these * properties. Additionally, any number of methods can be added to provide * functionality related to the properties. There are three small differences * to normal objects: * * 1. They must be derived from this Extension class, * 2. Properties must be handled with special extension macros. * 3. Extensions must be initialised. * * The following code illustrates the basic setup of an extension: * * @code * class MyExtension : public Extension * { * EXTENSION_PROPERTY_HEADER(MyExtension); * PropertyInt MyProp; * virtual bool overridableMethod(DocumentObject* obj) {}; * }; * * EXTENSION_PROPERTY_SOURCE(App::MyExtension, App::Extension) * MyExtension::MyExtension() * { * EXTENSION_ADD_PROPERTY(MyProp, (0)) * initExtensionType(MyExtension::getExtensionClassTypeId()); * } * * using MyExtensionPython = ExtensionPythonT; * @endcode * * The special Python extension type created above is important, as only those Python extensions * can be added to an object from Python. It does not work to add the C++ version directly there. * * Note that every method of the extension becomes part of the extended object when added from C++. * This means one should carefully design the API and make only necessary methods public or protected. * Every internal method should be private. * * The automatic availability of methods in the class does not hold for the * Python interface, only for C++ classes. This is common in the rest of * FreeCAD as well: there is no automatic creation of Python API from C++ * classes. * * Hence, the extension creator must also create a custom Python object of its * extension, which is the same for the normal FreeCAD Python object workflow. * There is nothing special at all for Python extension objects: the normal * `.pyi` and `PyImp.cpp` approach is used but note that it is important that * the object's father is the correct extension base class. Additionally, make * sure the extension returns the correct Python object in its * getExtensionPyObject() call. * * Every method that is created in the extension's Python counterpart will be * later added to the extended object. This happens automatically for both the * C++ and Python extension if getExtensionPyObject() returns the correct * Python object. This does not require extra work. * * A special case that needs to be handled for extensions is the possibility of * overridden methods. Often, it is desired to customise extension behaviour * by allowing the user to override methods provided by the extension. On the * C++ side, this is trivial: such methods are simply marked as "virtual" and * can then be overridden in any derived class. This is more involved for the * Python interface and here special care needs to be taken. * * As already shown above, one needs to create a special `ExtensionPythonT<>` * object for extension from Python. This is done exactly for the purpose of * allowing to have overridable methods. The ExtensionPythonT wrapper adds a * proxy property that holds a PyObject which itself will contain the * implementations for the overridden methods. This design is equal to the * ObjectPythonT<> design of normal document objects. As this wrapper inherits * the C++ extension class it, can also override the virtual functions the user * designed to be overridden. What it should do at a call of the virtual * method is to check if this method is implemented in the proxy object and if * so, call it, and if not, call the normal C++ version. It is the extension * creator's responsibility to implement this check and call behaviour for * every overridable method. This is done by creating a custom wrapper just * like ExtensionPythonT<> and overriding all virtual methods. * * @code * template class MyExtensionPythonT : public ExtensionT { * public: * * MyExtensionPythonT() {} * virtual ~MyExtensionPythonT() {} * * virtual bool overridableMethod(DocumentObject* obj) override { * Py::Object pyobj = Py::asObject(obj->getPyObject()); * EXTENSION_PROXY_ONEARG(allowObject, pyobj); * * if(result.isNone()) * ExtensionT::allowObject(obj); * * if(result.isBoolean()) * return result.isTrue(); * * return false; * }; * }; * @endcode * * @note As seen in the code there are multiple helper macros to ease the * repetitive work of querying and calling methods of the proxy object. See the * macro documentation for how to use them. * * To ensure that your wrapper is used when a extension is created from Python * the extension type must be exposed as follows: * * @code * using MyExtensionPython = ExtensionPythonT>; * @endcode * * This boilerplate is absolutely necessary to allow overridable methods in * Python and it is the extension creator's responsibility to ensure full * implementation. * * @section SecExtensionLimitiationsPython Limitations of Python * * Without this extension system, it would be challenging to extend * functionality in FreeCAD. Although C++ supports multiple inheritance, it is * not possible to use it in FreeCAD because it should be possible to expose * all objects to Python. * * However, using multiple parent classes in Python is currently not possible * with the default object approach. Moreover, it is basically impossible to * handle multiple inheritance in the C-API for Python extensions. */ /** * @namespace App * @ingroup APP * @brief The namespace for the part of FreeCAD that works without GUI. * @details This namespace includes %Application services of FreeCAD that such as: * - The Application class * - The Document class * - The DocumentObject classes * - The Expression classes * - The Property classes * * For a more high-level discussion see the topic @ref APP "App". */