FreeCAD Mod Dev Guide 20170101
FreeCAD Mod Dev Guide 20170101
FreeCAD Mod Dev Guide 20170101
code
1
Module developer’s guide to FreeCAD source code
by Qingfeng Xia http://www.iesensor.com
• 2015-09-18 version 0.1 for FreeCAD version 0.16-dev
• 2016-09-18 version 0.2 for FreeCAD version 0.17-dev
This ebook is licensed the same as FreeCAD document license CC-BY 3.0
http://creativecommons.org/licenses/by/3.0/
Original/lead developers:
• Jürgen Riegel
• Werner Mayer
• yorik van havre
Add all contributors see http://www.freecadweb.org/wiki/index.php?title=
Contributors
Make sure you are familiar with FreeCAD workbench GUI and API as a user:
• Foundamental document on official wiki for FreeCAD
• FreeCAD python API document
• single file PDF user manual for quick start
1
Organisation of this book
Acknowledgement to my family
2
Contents
1
2 CONTENTS
First of all, thanks to the original developers (Jürgen Riegel, Werner Mayer, Yorik van Havre), for sharing this great
artwork freely. FreeCAD is released under LGPL license, free for commercial usage with dynamic linkage.
wikipedia of FreeCAD
FreeCAD is basically a collage of different powerful libraries, the most important being openCascade, for managing
and constructing geometry, Coin3D to display that geometry, Qt to put all this in a nice Graphical User Interface,
and Python to give full scripting/macro functions.
The birth of FreeCAD: version 0.0.1 October 29, 2002 Initial Upload
7
8 CHAPTER 1. FREECAD OVERVIEW AND ARCHITECTURE
• python scripting in console mode and python-based macro recording in GUI mode
• all FreeCAD class is derived from this BaseClass, connected with BaseClassPy
• c++11 is not extensively used before 0.17
• c++ template is not heavily used, but FeatureT<> make DocumentObject ViewProvider extensible in Python
• FreeCAD not tied to Qt system until GUI, Boost::signal is used in command line mode: FreeCADCmd
• std::string(UTF8) is used internally, using QString getString(){QString.fromUtf8(s.c_str())}
• C++ for most of time consuming task (threading model), to avoid bottleneck of Global Interpretor Lock
Mixing C++ and Python in module development will be discussed in Chapter 5.
https://github.com/yorikvanhavre/Draft-dxf-importer
Current FreeCAD policy is to include only LGPL software and no GPL by default. Mentioned DXF import-export libraries
were downloaded by default. On DXF import-export operation in the past but Debian didn’t like that and FreeCAD changed
in a way user has to manually enable (Opt-In) the download.
Open Draft workbench and after that select Edit -> Preferences. Under Import-Export -> DXF / DWG tab, enable Automatic
update. After that FreeCAD will download mentioned libraries on first DXF import-export operation and it should work. If it
does not work restart FreeCAD and try again.
OpenCASCADE, as a CAD kernel, did not render 3D object to screen (when FreeCAD was born in 2002) until recently
release. Currently, there are several 3D lib based on OpenGL, see a list that works with QT https://wiki.qt.io/Using_3D_
engines_with_Qt. 3D gaming engines can also been used to render 3D objects, such as OGRE(Object-Oriented Graphics
Rendering Engine), Unreal, Unity.
Selection of Open Inventor to render FreeCAD is based on software license and performance consideration. Open Inventor,
originally IRIS Inventor, is a C++ object oriented retained mode 3D graphics API designed by SGI to provide a higher layer
of programming for OpenGL. Its main goals are better programmer convenience and efficiency. Open Inventor is free and
open-source software, subject to the requirements of the GNU Lesser General Public License (LGPL), version 2.1, in Aug 2000.
Coin3D implements the same API but not source code with Open Inventor, via clean room implementation comapatible
Stable release Open Inventor v2.1. Kongsberg ended development of Coin3D in 2011 and released the code under the BSD
3-clause license. It is possible to draw object in OpenInventor Scene by Python, via Coin3D’s python wrapper pivy , see
http://www.freecadweb.org/wiki/index.php?title=Pivy
VTK, is another open source and cross-platform visualisating library, which ParaView is based on. Interoperation is possible,
see Method for converting output from the VTK pipeline into Inventor nodes. From 0.17 and beyond, VTK pipeline is added
to Fem module.
[1] http://www.opencascade.com/content/overview
[2] http://forum.freecadweb.org/viewtopic.php?f=10&t=12821&p=102683#p102683 by “ickby”
reply from one key developer:
First of all FreeCAD works without OpenCASCADE. That is an important feature, not everything
needs geometric modeling, for example the Robot Workbench. OCC is only incorporated by the Part
Workbench.
Of course one could have based freecad completely on occ and reuse OCAF and the visalisation, however,
there are quite some points against it:
1. The OCAF overlap is minimal: only some stuff from App could have been reused, the whole Gui
handling would have been needed anyway. And to integrate all of the currently available functionality
basicly the same amount of work would have been needed. According to Jriegel initially freecad
based its document structure on ocaf, but it was found to be lacking and then replaced with a
custom implementation. And this makes adoptions and enhancements way easier, see for example
the recent expression integration.
2. The OpenCASCADE visualisation was lacking at best over all the years. They put in much work
in the last time which significantly improved it, but this was too late for FreeCAD. And the most
important issue: OpenCASCADE visualisation is highly tailored towards visualisation of their types.
A generell interface for arbitrary stuff is not available and hence it is not suited for freecad, where
many workbenches draw all kinds of things via the nice openInventor API
It is important to track the roadmap of FreecADm as it is still under heavy development. http://www.freecadweb.org/wiki/
index.php?title=Development_roadmap
The Main external components are upgrade gradually, like OpenInventor, pyCXX.
• C++11 is adopted since 0.17. C++17 latest standard library could replace boost::FileSystem in the future
• Migration from Qt4 to Qt5 is straight-forward (Qt4All.h Swithch from Qt4->Qt5) in C++, but depending on availability
of LGPL version of Qt5 python wrapping: PySide2
• Python3 support is under implementatoin
• OpenCASCADE(OCC) and VTK is migrating to 7.0 in late 2016
Transitioning from OpenGL to Vulkan will not happen in the future, while OpenGL should be available for a long time (10
years).
1.5.2 C++11
C++ is NOT a easy language but targeting at high performance, you need to manage memory manually and there are a lot of
pitfalls, see http://horstmann.com/cpp/pitfalls.html. Even experienced C++ programmer will find himself/herself has not
fully master C++. Exception safety will be beyond layman’s brain.
Common pitfalls for c++ are exampled in appendix.
Nevertheless, C++11 is almost a branch new language, or next generation c++. C++11 add some extra keywords like
“explicit, overload/final, noexcept” to avoid some unintended mistakes, also introduce new features and extra STL functions
like lambda and std::function, constexpr,enum class, smart pointer, auto type derivation, std::thread, atomic, regex etc.
According to Qt community news (July, 2016), LGPL python wrapping for Qt 5.x is promising in the near future
10 CHAPTER 1. FREECAD OVERVIEW AND ARCHITECTURE
The Pyside 2 project aims to provide a complete port of PySide to Qt 5.x. The development started on GitHub in
May 2015. The project managed to port Pyside to Qt 5.3, 5.4 & 5.5. During April 2016 The Qt Company decided
to properly support the port (see details ).
Chapter 2
IF (CMAKE_COMPILER_IS_GNUCC)
FIND_PATH(SMESH_INCLUDE_DIR SMESH_Mesh.hxx
# These are default search paths, why specify them?
# /usr/include
# /usr/local/include
PATH_SUFFIXES smesh
)
FIND_LIBRARY(SMESH_LIBRARY SMESH
# /usr/lib
# /usr/local/lib
)
11
12 CHAPTER 2. ORGANISATION OF FREECAD SOURCE CODE
ELSE (CMAKE_COMPILER_IS_GNUCC)
# Not yet implemented
ENDIF (CMAKE_COMPILER_IS_GNUCC)
SET(SMESH_FOUND FALSE)
IF(SMESH_LIBRARY)
SET(SMESH_FOUND TRUE)
GET_FILENAME_COMPONENT(SMESH_LIBRARY_DIR ${SMESH_LIBRARY} PATH)
set(SMESH_LIBRARIES
${SMESH_LIBRARY_DIR}/libDriver.so
${SMESH_LIBRARY_DIR}/libDriverDAT.so
${SMESH_LIBRARY_DIR}/libDriverSTL.so
${SMESH_LIBRARY_DIR}/libDriverUNV.so
${SMESH_LIBRARY_DIR}/libSMDS.so
${SMESH_LIBRARY_DIR}/libSMESH.so
${SMESH_LIBRARY_DIR}/libSMESHDS.so
${SMESH_LIBRARY_DIR}/libStdMeshers.so
)
ENDIF(SMESH_LIBRARY)
• Inspection Testing
• Test Workbench for self testing
• Sandbox Testing
Meta workbench
• Web web view of FreeCAD
• Start start page of FreeCAD
• Complete show all toolbar from loadable modules
Module not visible to workbench users
• Import
• JtReader
The geometry that appears in the 3D views of FreeCAD are rendered by the Coin3D library. Coin3D is an implementation of
the OpenInventor standard, which exempt you from OpenGL coding.
FreeCAD itself features several tools to see or modify openInventor code. For example, the following Python code will show
the openInventor representation of a selected object:
obj = FreeCAD.ActiveDocument.ActiveObject
viewprovider = obj.ViewObject
print viewprovider.toString()
2.5. LEARNING OPENINVENTOR/COIN3D 15
SoPath, SoNode, SoEngine are three main categories of Object in Coin3D. Classes are organised into modules, see http:
//developer90.openinventor.com/APIS/RefManCpp/main.html
Description from this online documentation is extracted for key classes. See the brief description for classes: http://coin3d.
bitbucket.org/Coin/annotated.html;
** Basic objects **
• SbXXX: Basic types like SbVec3f, SbMatrix, SbColor, SbString, SbTime; Containers like SbDict, SbList; geometrical
representation of basic shape like SbSphere; SbTypeInfo
• SoBase: ancester for most Coin3D ojbects, similar with QObject, FreeCAD’s Base::BaseClass
Top-level superclass for a number of class-hierarchies. SoBase provides the basic interfaces and methods for doing
reference counting, type identification and import/export. All classes in Coin3D which uses these mechanisms are
descendent from this class
ref() unref() getName()
virtual SoType getTypeId (void) const =0
notify (SoNotList *nl) //observer pattern, notify Auditor
addAuditor (void *const auditor, const SoNotRec::Type type)
Qobject is the base object for all derived Qt objects, offering event, containner, property, type support.
Example of inheritance chains:
Coin3D: SoBase->SoFieldContainer->SoNode->SoGroup->SoShape FreeCAD: BaseClass->App::PropertyContainer->App::Docu
• SoType: Inventor provides runtime type-checking through the SoType class. node->getTypeId().getName(); like
Base::TypeClass in FreeCAD
Basis for the run-time type system in Coin3D. Many of the classes in the Coin3D library must have their type
information registered before any instances are created (including, but not limited to: engines, nodes, fields, actions,
nodekits and manipulators). The use of SoType to store this information provides lots of various functionality for
working with class hierarchies, comparing class types, instantiating objects from classnames, etc etc
• SoField: Top-level abstract base class for fields serializable, similar with App::Property in FreeCAD
Fields is the mechanism used throughout Coin for encapsulating basic data types to detect changes made to
them, and to provide conversion, import and export facilities. SoSFXXX : Single Field with Base type wrapped
(App::Property); SoMFXXX : Multiple Field (array of field). E.g. SoSFBool class is a container for an SbBool
value.
• SoFieldContainer: serializaton(App::PropertyContainer in FreeCAD) function is built into SoNode
• SoBaseList Container for pointers to SoBase derived objects.
The additional capability of the SoBaseList class over its parent class, SbPList, is to automatically handle
referencing and dereferencing of items as they are added or removed from the lists
** Scene organisation **
• SoDB: This class collects various methods for initializing, setting and accessing common global data from the Coin
library
Similar with App::Document in FreeCAD import and export into file. Directed Acyclic Graph is used for better
performance, SoNodes are organised into database, serialization into text file *.iv .
“The foundation concept in Open Inventor is the”scene database" which defines the objects to be used in an
application. When using Open Inventor, a programmer creates, edits, and composes these objects into hierarchical
3D scene graphs (i.e., database). " Quoted from Open Inventor reference.
• SoNode: similar with App::DocumentObject in FreeCAD, has flags like ignore, override
Base class for nodes used in scene graphs. Coin is a retained mode 3D visualization library (built on top of the
immediate mode OpenGL library). “Retained mode” means that instead of passing commands to draw graphics
primitives directly to the renderer, you build up data structures which are rendered by the library on demand
• SoGroup: similar with App::DocumentObjectGroup in FreeCAD
16 CHAPTER 2. ORGANISATION OF FREECAD SOURCE CODE
An SoSwitch node is exactly like an SoGroup except that it visits only one of its children. SoShape is derived
from SoGroup Shared Instancing: share the SoShape, but seperate SoTransform, ref counting
• SoSeperator: State-preserving group node (derived from SoGroup), conprising SoColor, SoMaterial, SoTexture, SoShape,
etc.
Subgraphs parented by SoSeparator nodes will not affect the previous state, as they push and pop the traversal
state before and after traversal of its children. Order (topdown, left to right) in SoDB (scene graph) is important
to determine rendering, see exmaple in http://developer.openinventor.com/content/34-creating-groups. Scale node
is only added to first Hydrogen SoGroup, but this scale applied to the second Hydrogen SoGroup. To isolate
the effects of nodes in a group, use an SoSeparator node, which is a subclass of SoGroup . Before traversing
its children, an SoSeparator saves the current traversal state. When it has finished traversing its children, the
SoSeparator restores the previous traversal state. Nodes within an SoSeparator thus do not affect anything above
or to the right in the graph.
• SoPath: Container class for traversal path for nodes in scene database, see also SoFullPath, SoNodeKitPath. It is
derived from SoBase, not SoFieldContainer, it is different from App::PropertyLink in FreeCAD.
“SoPath objects contain a list of SoNode pointers and a list of child indices. Indices are necessary to disambiguate
situations where a node uses the same node as a child multiple times. Similarly, UUID and getUniqueName() in
FreeCAD make the unique reference to Document Objects.”
• SoBaseKit: base class for all NodeKit (not a SoGroup) which create groups of scene graph nodee. Parts are added as
hidden children, accessable only by the methods of SoBaseKit and its derived classes.
• SoSeparatorKit: A nodekit that is used for creating nodekit hierarchies. SoSeparatorKit contains a transform part, a
childList part, and a few others like pickStyle , appearance in its catalog.
** Scene rendering **
• SoAnnotation: (Derived from SoSeparator) node draws all its child geometry on top of other geometry.
This group-type node uses delayed rendering in combination with Z-buffer disabling to let its children transparently
render their geometry on top of the other geometry in the scene.
• SoShape: SoCube/SoCone/SoCynlinder/SoSphere/SoText/SoImageSoNurbsCurve/SoNurbsSurface/SoImage:
(App::GeoFeature in FreeCAD??)
For rendering basic shpapes.Insert a shape into the scenegraph and render with the current material, texture and
drawstyle settings (if any, otherwise the default settings are used)
• SoDetail: Superclass for all classes (SoCubeDetail. . . ) storing detailed information about particular shapes.
Detail information about shapes is used in relation to picking actions in Coin. They typically contain the relevant
information about what particular part of the shape a pick ray intersected with
** misc objects **
• SoEngine: SoEngine (derived from SoFieldContainer, as a sibling of SoNode) is the base class for Coin/Inventor engines.
Engines enables the application programmers to make complex connections between fields, for example, animation.
• SoVRMLXXX: VRML file import and export
• SoAudioDevice: 3D sound
• SoSensor: for scene manipulation
• SoCamera: belongs only to scene
• SoLight: belongs only to scene
• SoEnvironment: gloable settings
• ScXml: Namespace for static ScXML-related functions
• SoElement: base class for classes used internally for storing information in Open Inventor’s traversal state list.
• SoSelection: Manages a list of selected nodes, Derived from SoSeparator.
Inserting an SoSelection node in your scene graph enables you to let the user “pick” with the left mousebutton to
select/deselect objects below the SoSelection node
18 CHAPTER 2. ORGANISATION OF FREECAD SOURCE CODE
Applying actions is the basic mechanism in Coin for executing various operations on scene graphs or paths within
scene graphs, including search operations, rendering, interaction through picking, etc
By inserting SoCallback nodes in a scene graph, the application programmer can set up functions to be executed at
certain points in the traversal - SoCallbackAction: Invokes callbacks at specific nodes.This action has mechanisms
for tracking traversal position and traversal state.
In combination with the ability to pass geometry primitives to callback actions set by the user, this does for
instance make it rather straightforward to extract the geometry of a scene graph
• SoCallbackList The SoCallbackList is a container for callback function pointers, providing a method for triggering the
callback functions
see http://developer.openinventor.com/content/chapter-10-handling-events-and-selection
Quater: the most updated bind with Qt Quarter is superior over SoQt providing OpenGL widget viewer. Release 1.0.0
is the first major release. Quarter 1.0 is only usable with Coin-3.x and Qt-4.x.
Quarter is a light-weight glue library that provides seamless integration between Systems in Motions’s Coin high-level 3D
visualization library and Trolltech’s Qt 2D user interface library, to replace SoQt. The functionality in Quarter revolves
around QuarterWidget, a subclass of QGLWidget. This widget provides functionality for rendering of Coin scenegraphs and
translation of QEvents into SoEvents. Using this widget is as easy as using any other QWidget.
For developers targeting multi-platform - ‘Quarter’ provides a seamless integration with the Qt framework. https://en.
wikipedia.org/wiki/Coin3D
http://doc.coin3d.org/Quarter/
pivy is Python wrapper of Coin3D C++ lib, via SWIG A new SoPyScript Node is added to include Python script directly
Chapter 3
In this chapter, the namespace of Base, App and Main modules are introduced, these 3 modules make a complete program
without GUI.
Their functions can be accessed in python by “import FreeCAD”, see [FreeCAD module]http://www.freecadweb.org/api/
FreeCAD.html
This chapter focused on the propety framework and DocumentObject in App namespace, as they are most interesting to
module developer. The classes in Base namespace are not frequently used, but understanding of the type system could be
useful. Finaly, the FreeCAD startup process is tracked in Main source code folder.
19
20 CHAPTER 3. BASE, APP AND MAIN MODULE
• Stream.h define adapter classes for Qt class QByteArray; class QIODevice;class QBuffer;
• InputSource.h
class BaseExport StdInputStream : public XERCES_CPP_NAMESPACE_QUALIFIER BinInputStream
• FileInfo.h File name unification class
This class handles everything related to file names the file names which are internal generally UTF-8 encoded on
all platforms.
• FileTemplate.h used for testing purpose
• gzStream.h gzip compressed file Stream
• Console.h output message to terminal which starts FreeCADCmd
ConsoleObserver and ConsoleSingleton with python code [Console.cpp], This is not Python Console, but dealing
with stdio, logging to terminal which starts FreeCADCmd. class BaseExport ConsoleObserverStd: public
ConsoleObserver to write Console messages and logs the system con.
• Parameter.h ParameterGrp: key-value, XML persistence as app config
class BaseExport ParameterGrp : public Base::Handled,public Base::Subject <const char*>
class BaseExport ParameterManager : public ParameterGrp
• Debugger.h Debugger class
Debugging related classes in source files [Debugger.h, Debugger.cpp, StackWalker.h, StackWalker.cpp, MemDebug.h]
serialization support, example of class with cpp, py and XML code
• Persistence.h serailization of objects
base class for DocumentObject, Property, etc
• Persistence.cpp C++ implementation of Persistence class
• PersistencePyImp.cpp automatically generated C++ code for exporting Persistence class to python
• PersistencePy.xml XML to generate PersistencePyImp.cpp by python script
**Geometry related calculation classes with *Py.cpp**
• Axis.h Class: Axis
• BoundBox.h bounding boxes of the 3D part, define max{x,y,z} and min{x,y,z}
• Rotation.h define class and method for rotation an objecti n 3D space
• Placement.h class to place/relocate an object in 3D space
see offical api doc: http://www.freecadweb.org/api/Placement.html
• Vector.hTemplate class represents a point, direction in 3D space typedef Vector3<float> Vector3f; typedef
Vector3<double> Vector3d;
• Matrix.hTemplate class: Matrix4D for coordination translation and rotation
#include <Base/Sequencer.h>
void runOperation();
void myTest()
{
try{
runOperation();
} catch(...) {
// the programmer forgot to halt the sequencer here
// If SequencerLauncher leaves its scope the object gets destructed automatically and
// stops the running sequencer.
}
22 CHAPTER 3. BASE, APP AND MAIN MODULE
}
void runOperation()
{
// create an instance on the stack (not on any terms on the heap)
SequencerLauncher seq("my text", 10);
for (int i=0; i<10; i++)
{
// do something (e.g. here can be thrown an exception)
...
seq.next ();
}
}
The string encoding for FreeCAD is different form Qt’s wide char, using the helper functions in src/Base/Tools.h
fromStdString(const std::string & s) and toStdString(const QString& s)
struct BaseExport Tools
{
static std::string getUniqueName(const std::string&, const std::vector<std::string>&,int d=0);
static std::string addNumber(const std::string&, unsigned int, int d=0);
static std::string getIdentifier(const std::string&);
static std::wstring widen(const std::string& str);
static std::string narrow(const std::wstring& str);
static std::string escapedUnicodeFromUtf8(const char *s);
/**
* @brief toStdString Convert a QString into a UTF-8 encoded std::string.
* @param s String to convert.
* @return A std::string encoded as UTF-8.
*/
static inline std::string toStdString(const QString& s) { QByteArray tmp = s.toUtf8(); return std::string(t
/**
* @brief fromStdString Convert a std::string encoded as UTF-8 into a QString.
* @param s std::string, expected to be UTF-8 encoded.
* @return String represented as a QString.
*/
static inline QString fromStdString(const std::string & s) { return QString::fromUtf8(s.c_str(), s.size());
std::string name;
Type parent;
Type type;
Type::instantiationMethod instMethod;
};
class Type
{
//...
static void *createInstanceByName(const char* TypeName, bool bLoadModule=false);
static const Type createType(const Type parent, const char *name,instantiationMethod method = 0);
private:
unsigned int index;
static std::map<std::string,unsigned int> typemap;
static std::vector<TypeData*> typedata;
3.2.2 src/Base/BaseClass.h
Macro function is widely employed to generate boilplate code, similar with QObject macro for QT
#ifndef BASE_BASECLASS_H
#define BASE_BASECLASS_H
#include "Type.h"
// Python stuff
typedef struct _object PyObject;
namespace Base
{
/// BaseClass class and root of the type system
class BaseExport BaseClass
{
public:
static Type getClassTypeId(void);
virtual Type getTypeId(void) const;
bool isDerivedFrom(const Type type) const {return getTypeId().isDerivedFrom(type);}
public:
/// Construction
BaseClass();
/// Destruction
virtual ~BaseClass();
};
} //namespace Base
#endif // BASE_BASECLASS_H
///////////////////////////////////////////////////////////////////////////////
3.2. TYPE, BASECLASS, PYOBJECTBASE 25
#include "PreCompiled.h"
#ifndef _PreComp_
# include <assert.h>
#endif
//**************************************************************************
// separator for other implemetation aspects
void BaseClass::init(void)
{
assert(BaseClass::classTypeId == Type::badType() && "don't init() twice!") ;
/* Make sure superclass gets initialized before subclass. */
/*assert(strcmp(#_parentclass_), "inherited"));*/
/*Type parentType(Type::fromName(#_parentclass_));*/
/*assert(parentType != Type::badType() && "you forgot init() on parentclass!");*/
Type BaseClass::getClassTypeId(void)
{
return BaseClass::classTypeId;
}
/**
* This method returns the Python wrapper for a C++ object. It's in the responsibility of
26 CHAPTER 3. BASE, APP AND MAIN MODULE
* the programmer to do the correct reference counting. Basically there are two ways how
* to implement that: Either always return a new Python object then reference counting is
* not a matter or return always the same Python object then the reference counter must be
* incremented by one. However, it's absolutely forbidden to return always the same Python
* object without incrementing the reference counter.
*
* The default implementation returns 'None'.
*/
PyObject *BaseClass::getPyObject(void)
{
assert(0);
Py_Return;
}
void BaseClass::setPyObject(PyObject *)
{
assert(0);
}
3.2.3 src/Base/PyObjectBase.h
Py_Header is a macro function, PyObject is defined in <python.h>, the header for python C API.
/** The PyObjectBase class, exports the class as a python type
* PyObjectBase is the base class for all C++ classes which
* need to get exported into the python namespace.
3.2.4 src/Base/Persistence.h
3.3.1 src/Base/Unit.h
There are 7 SI base units, but FreeCAD defined Density, which is a derived unit
struct UnitSignature{
int32_t Length:UnitSignatureLengthBits;
int32_t Mass:UnitSignatureMassBits;
int32_t Time:UnitSignatureTimeBits;
int32_t ElectricCurrent:UnitSignatureElectricCurrentBits;
int32_t ThermodynamicTemperature:UnitSignatureThermodynamicTemperatureBits;
int32_t AmountOfSubstance:UnitSignatureAmountOfSubstanceBits;
int32_t LuminoseIntensity:UnitSignatureLuminoseIntensityBits;
int32_t Angle:UnitSignatureAngleBits;
int32_t Density:UnitSignatureDensityBits;
};
Predefined static Unit types: static Unit Length; ... static Unit Stress;
3.3.2 src/Base/Quantity.h
Quantity is value + unit. Common quantities defined as static instances. Quantity string can be parsed into value and unit
by quantitylexer
Utilities
• MeasureDistance.h Measure distance between two entity
• ColorModel.h Color bar like grayscale, inverse gray scale, Tria,
Color class is defined here, constructed from uint32_t or 4 float number for RGBA.
• Material.h appearance: color and transparency for rendering of 3D object
define a few standard material MaterialObject is derived from DocumentObject and contains data from Material
class. [Material.cpp, MaterialObject.cpp, MaterialPyImp.cpp, Material.h, MaterialObject.h, MaterialPy.xml]
• MaterialObject.h DocumentObject store key-valve pair for material information
physical property of *.ini style FCMat files, under src/Mod/Material/StandardMaterial/<MaterialName>.FCMat
Fem::MechanicalMaterial is python class derived from this class
App::GeoFeature and derived classes
• GeoFeature.h Base class of all geometric document objects
Derived from DocumentObject, contains only PropertyPlacement, see [GeoFeature.cpp]
• Plane.h Object Used to define planar support for all kind of operations in the document space
sketch is done on planes, derived from App::GeoFeature which is derived from DocumentObject
• Placement.h define six degree of freedom (orientation and position ) for placing a part in space
derived from App::GeoFeature, A placement defines an orientation (rotation) and a position (base) in 3D space.
It is used when no scaling or other distortion is needed.
• InventorObject.h derived from App::GeoFeature wiht only 2 properties: PropertyString Buffer, FileName;
• VRMLObject.h derived from App::GeoFeature
App::Data namespace and ComplexGeoData class
• ComplexGeoData.h store data to represent complex geometry in line, facet(triangle) and segment
declare Segment, and ComplexGeoData, which has ref counting, in App::Data namespace. class AppExport
ComplexGeoData: public Base::Persistence, public Base::Handled
Property name should begin with uppercase like “ThisPropertyName”, and it will show as “This Property Name” in property
editor There is indeed the logic to split property names on capital letters and insert a space. But that’s only for visual
purposes and doesn’t affect changing a property value.
30 CHAPTER 3. BASE, APP AND MAIN MODULE
3.5.2 src/App/PropertyStandard.h
Define property for common C++ data type: PropertyBool, PropertyInteger (long), PropertyString (utf8/std::string),
PropertyFloat (double), PropertyPath (boost::filesystem::path), PropertyFont, PropertyColor, PropertyMaterial,PropertyUuid,
PropertyStringLists, PropertyMap(std::map)
PropertyIntegerConstraint is PropertyInteger with upper and lower Bound.
struct Constraints { long LowerBound, UpperBound, StepSize; };
void setConstraints(const Constraints* sConstraint); /// get the constraint struct const Constraints* getConst
For PropertyList derived class, it is possible to set and get values in std::vector<> reference setValues(std::vector<T>&),
std::vector& getValues()‘
PropertyFloatList actually holds double precision data (float64)
src/App/PropertyLinks.cpp link to other document object in the this document. For example, FemMeshObject has a link to
Part object.
There are scennarios where link to sub feature are needed, e.g. faces of a part.
3.5.7 PropertyMap
implements a key/value list as property. The key ought to be ASCII the Value should be treated as UTF8 to be save
3.5. PROPERTY FRAMEWROK 31
src/App/PropertyUnits.cpp
TYPESYSTEM_SOURCE(App::PropertyDistance, App::PropertyQuantity);
PropertyDistance::PropertyDistance()
{
setUnit(Base::Unit::Length);
}
3.5.9 src/App/Property.h
3.5.10 src/App/PropertyContainer.h
enum PropertyType
{
Prop_None = 0,
Prop_ReadOnly = 1,
Prop_Transient= 2,
Prop_Hidden = 4,
Prop_Output = 8
};
void addProperty(const PropertyContainer *container,const char* PropName, Property *Prop, const char* Propert
32 CHAPTER 3. BASE, APP AND MAIN MODULE
private:
static PropertyData propertyData;
};
3.6.1 src/App/Document.h
3.6.2 src/App/DocumentObject.h
class AppExport DocumentObject: public App::PropertyContainer , Base class of all Classes handled in the Document.
see http://www.freecadweb.org/api/DocumentObject.html, some important methods (excluding methods from
App::PropertyContainer) are extracted here:
• state enumeration.
enum ObjectStatus {
Touch = 0, Error = 1, New = 2, Recompute = 3,
Restore = 4, Expand = 16
}
• __setstate__(value) allows to save custom attributes of this object as strings, so they can be saved when saving the
FreeCAD document
• touch() marks this object to be recomputed
• purgeTouched() removes the to-be-recomputed flag of this object
• execute() this method is executed on object creation and whenever the document is recomputed
Implementation: [src/App/DocumentObject.h] and src/App/DocumentObject.cpp
protected:
/* get called by the document to recompute this feature
* Normaly this method get called in the processing of Document::recompute().
* In execute() the outpupt properties get recomputed with the data from linked objects and objects own p
*/
virtual App::DocumentObjectExecReturn *execute(void);
protected: // attributes
Py::Object PythonObject;
/// pointer to the document this object belongs to
App::Document* _pDoc;
// Connections to track relabeling of document and document objects
boost::BOOST_SIGNALS_NAMESPACE::scoped_connection onRelabledDocumentConnection;
boost::BOOST_SIGNALS_NAMESPACE::scoped_connection onRelabledObjectConnection;
3.6.4 App::DocumentObjectExecReturn
std::string Why;
DocumentObject* Which;
};
3.6.5 FeaturePython
DocumentObjectExecReturn *FeaturePythonImp::execute()
{
// Run the execute method of the proxy object.
Base::PyGILStateLocker lock;
try {
Property* proxy = object->getPropertyByName("Proxy");
if (proxy && proxy->getTypeId() == PropertyPythonObject::getClassTypeId()) {
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
if (feature.hasAttr("__object__")) {
Py::Callable method(feature.getAttr(std::string("execute")));
Py::Tuple args;
method.apply(args);
}
else {
Py::Callable method(feature.getAttr(std::string("execute")));
3.6. DOCUMENT-VIEW-OBSERVER PATTERN 35
Py::Tuple args(1);
args.setItem(0, Py::Object(object->getPyObject(), true));
method.apply(args);
}
}
}
catch (Py::Exception&) {
Base::PyException e; // extract the Python error text
e.ReportException();
std::stringstream str;
str << object->Label.getValue() << ": " << e.what();
return new App::DocumentObjectExecReturn(str.str());
}
return DocumentObject::StdReturn;
}
This template helps to expose document object derived class to python as/like DocumentObjectPy see chapters on Fem
module code analysis and python wrapping for details
template <class FeaturePyT>
class FeaturePythonPyT : public FeaturePyT
{
public:
static PyTypeObject Type;
static PyMethodDef Methods[];
public:
FeaturePythonPyT(DocumentObject *pcObject, PyTypeObject *T = &Type);
virtual ~FeaturePythonPyT();
/** @name callbacks and implementers for the python object methods */
//@{
static int __setattr(PyObject *PyObj, char *attr, PyObject *value);
/// callback for the addProperty() method
static PyObject * staticCallback_addProperty (PyObject *self, PyObject *args);
/// implementer for the addProperty() method
PyObject* addProperty(PyObject *args);
/// callback for the removeProperty() method
static PyObject * staticCallback_removeProperty (PyObject *self, PyObject *args);
/// implementer for the removeProperty() method
PyObject* removeProperty(PyObject *args);
/// callback for the supportedProperties() method
static PyObject * staticCallback_supportedProperties (PyObject *self, PyObject *args);
/// implementer for the supportedProperties() method
PyObject* supportedProperties(PyObject *args);
//@}
protected:
std::map<std::string, PyObject*> dyn_methods;
36 CHAPTER 3. BASE, APP AND MAIN MODULE
private:
};
} //namespace App
#include "FeaturePythonPyImp.inl" // Type structure of FeaturePythonPyT
/// Methods structure of FeaturePythonPyT
template<class FeaturePyT>
PyMethodDef FeaturePythonPyT<FeaturePyT>::Methods[] = {
...
protected:
virtual void onBeforeChange(const Property* prop) {
FeatureT::onBeforeChange(prop);
imp->onBeforeChange(prop);
}
virtual void onChanged(const Property* prop) {
imp->onChanged(prop);
FeatureT::onChanged(prop);
}
private:
FeaturePythonImp* imp;
DynamicProperty* props;
PropertyPythonObject Proxy;
};
This framework is added in 0.17, see feature annoucement in forum discussion: Developer Feature: Extensions
In FreeCAD normally inheritance is a chain, it is not possible to use multiple inheritance.
https://github.com/FreeCAD/FreeCAD/blob/master/src/App/ExtensionContainer.h#L36 **************************************
main()
{
try {
// Init phase ===============================
// sets the default run mode for FC, starts with command prompt
//if not overridden in InitConfig...
App::Application::Config()["RunMode"] = "Exit";
// cleans up
Application::destruct();
return 0;
}
3.7.2 src/Main/MainPy.py
Application::Application(ParameterManager * /*pcSysParamMngr*/ ,
ParameterManager * /*pcUserParamMngr*/ ,
std::map<std::string,std::string> &mConfig)
://_pcSysParamMngr(pcSysParamMngr),
//_pcUserParamMngr(pcUserParamMngr),
_mConfig(mConfig),
_pActiveDoc(0)
{
//_hApp = new ApplicationOCC;
mpcPramManager["System parameter"] = _pcSysParamMngr;
mpcPramManager["User parameter"] = _pcUserParamMngr;
if (mConfig["RunMode"] == "Cmd") {
// Run the comandline interface
3.8. FREECADGUI START UP PROCESS 39
This main function is similar with src/Main/MainCmd.cpp, except it supports both Gui and nonGui mode
App::Application::init(argc, argv); and App::Application::destruct(); are still called!
QCoreApplication is defined for WIN32, see src/Main/MainGui.cpp, text banner is defined here
main()
{
App::Application::init(argc, argv);
Gui::Application::initApplication(); // extra InitApplication();
// Only if 'RunMode' is set to 'Gui' do the replacement
if (App::Application::Config()["RunMode"] == "Gui")
Base::Interpreter().replaceStdOutput();
try {
if (App::Application::Config()["RunMode"] == "Gui")
Gui::Application::runApplication();
else
App::Application::runApplication();
}
...
App::Application::destruct();
}
void Application::runApplication(void)
{
GUIApplication mainApp(argc, App::Application::GetARGV(), systemExit);
40 CHAPTER 3. BASE, APP AND MAIN MODULE
// close the lock file, in case of a crash we can see the existing lock file
// on the next restart and try to repair the documents, if needed.
flock.unlock();
lock.close();
fi.deleteFile();
}
3.8.3 src/Main/FreeCADGuiPy.cpp
PyMODINIT_FUNC initFreeCADGui()
{
try {
Base::Interpreter().loadModule("FreeCAD");
App::Application::Config()["AppIcon"] = "freecad";
App::Application::Config()["SplashScreen"] = "freecadsplash";
App::Application::Config()["CopyrightInfo"] = "\xc2\xa9 Juergen Riegel, Werner Mayer, Yorik van Havre 2
Gui::Application::initApplication();
Py_InitModule("FreeCADGui", FreeCADGui_methods);
}
catch (const Base::Exception& e) {
PyErr_Format(PyExc_ImportError, "%s\n", e.what());
}
catch (...) {
PyErr_SetString(PyExc_ImportError, "Unknown runtime error occurred");
}
}
42 CHAPTER 3. BASE, APP AND MAIN MODULE
Chapter 4
43
44 CHAPTER 4. OVERVIEW OF GUI MODULE
• DownloadManager.h
• NetworkRetriever.h
subfolders in Gui
• 3Dconnexion 3D mouse 3Dconnexion’s supporting lib
• Inventor Inventor 3D rendering lib
• TaskView TaskView Framework for FreeCAD Gui
• QSint Collection of extra Qt widgets from community
• iisTaskPanel Task panel UI widgets, now part of QSint
• propertyeditor Widget for property edit for DocumentObject
• Language translation for FreeCADGui
• Icons icon for commands
4.2.1 Gui::Application
Gui::Application::Instance->activeDocument()
4.2.2 Gui::Document
http://iesensor.com/FreeCADDoc/0.16-dev/df/d3c/classGui_1_1BaseView.html
PropertyView has PropertyEditor,
class PropertyView : public QWidget, public Gui::SelectionObserver
class PropertyDockView : public Gui::DockWindow
The key C++ API are defined in src/Gui/Selection.h. see the doxygen document: http://free-cad.sourceforge.net/SrcDocu/
d4/dca/classGui_1_1SelectionSingleton.html
The SelectionSingleton keeps track of the selection state of the whole application. It gets messages from all entities which
can alter the selection (e.g. tree view and 3D-view) and sends messages to entities which need to keep track on the selection
state. SelectionObserver class implements the observer pattern; SelectionGate class enables the selective pickup for the
specific type (filtering).
The selection consists mainly out of following information per selected object: - document (pointer) - Object (pointer) - list of
subelements (list of strings) - 3D coordinates where the user clicks to select (Vector3d)
Also the preselection is managed. That means you can add a filter to prevent selection of unwanted objects or subelements.
src/Gui/Selection.h
struct SelObj {
const char* DocName;
const char* FeatName;
const char* SubName;
const char* TypeName;
App::Document* pDoc;
App::DocumentObject* pObject;
float x,y,z;
};
/// returns the selected DocumentObject or NULL if the object is already deleted
const App::DocumentObject *getObject(void) const;
/// returns the selected DocumentObject or NULL if the object is already deleted
App::DocumentObject *getObject(void);
4.3.1 Gui::ViewProvider
General interface for all visual stuff in FreeCAD. This class is used to generate and handle all around visualizing and presenting
objects from the FreeCAD App layer to the user. This class and its descendents have to be implemented for any object type
in order to show them in the 3DView and TreeView.
Inventor object will be create and ref in the constructor if defined in this base class; while in destructor, pyViewObject.unref()
is called, in addition to unref open inventor objects. show() and hide() are virtual functions, but they have implemetation,
intensive implementation happens in Gui::DocumentObjectViewProvider. PyObject* ViewProvider::getPyObject() has
its implemetation so all the derived classes for the specific python type, however, only one PyObject destruction is only
happend in this based class (pyViewObject.unref() is called).
Base class ViewProvider, derived from PropertyContainer , is surprisingly short in coding; the derived classes have
implementation. Some important methods for python module developer are listed: - Object returns the DocumentObject this
ViewProvider is associated to - RootNode returns the Root coin node of this object - toString() returns a string representation
of the coin node of this object - update() this method is executed whenever any of the properties of this ViewProvider changes
see details python API manual at http://www.freecadweb.org/api/ViewProvider.html
grouped in doxygen document
OpenInventor related objects () are declared as protected var:
SoSeparator * pcAnnotation // The root separator for annotations.
SoSwitch * pcModeSwitch // this is the mode switch, all the different viewing modes are collected here
4.3.2 Gui::DocumentObjectViewProvider
This is the counterpart of the DocumentObject in the GUI space. It is only present when FreeCAD runs in GUI mode (
e.g. show(), hide(), update() ). It contains all that is needed to represent the DocumentObject in the 3D view and the FreeCAD
CombiView. It implements show() hide() attach(), also restores view provider from document file loaded: virtual void
finishRestoring () and virtual void startRestoring ().
this class has detailed doxygen code documentation in this header file, Similar with ViewProvider class, show() hide() are
virtual member functions but with implemetation.
src/Gui/ViewProviderDocumentObject.cpp This class defines 2 new Properties in constructor.
ADD_PROPERTY(DisplayMode,((long)0));
ADD_PROPERTY(Visibility,(true));
Thereby, onChanged(const App::Property* prop) is reimplemented
void ViewProviderDocumentObject::onChanged(const App::Property* prop)
{
if (prop == &DisplayMode) {
setActiveMode();
}
else if (prop == &Visibility) {
// use this bit to check whether show() or hide() must be called
if (Visibility.testStatus(App::Property::User2) == false) {
Visibility.setStatus(App::Property::User2, true);
Visibility.getValue() ? show() : hide();
Visibility.setStatus(App::Property::User2, false);
}
}
ViewProvider::onChanged(prop);
}
DisplayMode related code is found inattach()
Gui::MDIView* ViewProviderDocumentObject::getActiveView() const
viewer->getSoRenderManager()->getViewportRegion());
viewer->getSoRenderManager()->getCamera();
Similar with ViewProvider class, show() hide() are virtual member functions but with implemetation.
4.3. VIEWPROVIDER FRAMEWORK AND 3D REDERRING 51
void ViewProviderDocumentObject::updateView()
{
std::map<std::string, App::Property*> Map;
pcObject->getPropertyMap(Map);
4.3.3 Gui::ViewProviderGeometryObject
The base class for all view providers that display geometric data, like mesh, point cloudes and shapes. drag, select(pick),
boundingbox, sensorCallback()
src/Gui/ViewProviderGeometryObject.cpp
ADD_PROPERTY(ShapeColor,(r, g, b));
ADD_PROPERTY(Transparency,(0));
Transparency.setConstraints(&intPercent);
App::Material mat(App::Material::DEFAULT);
ADD_PROPERTY(ShapeMaterial,(mat));
ADD_PROPERTY(BoundingBox,(false));
ADD_PROPERTY(Selectable,(true));
4.3.4 Fem::ViewProviderFemConstraint
This class draw some visual objects, arrows and cubes in 3D view, see src/Mod/Fem/Gui/ViewProviderFemConstraint.cpp
src/Mod/Fem/Gui/ViewProviderFemFluidBoundary.cpp
src/Mod/Part/Gui/ViewProvider.h
The base class for all CAD features like boolean operation, fillet, etc, implemented by OpenCASCADE.
TopoDS_Shape getShape (const SoPickedPoint *) const
Standard_Boolean computeEdges (SoGroup *root, const TopoDS_Shape &myShape)
Standard_Boolean computeFaces (SoGroup *root, const TopoDS_Shape &myShape, double defl)
Standard_Boolean computeVertices (SoGroup *root, const TopoDS_Shape &myShape)
src/Mod/Part/Gui/ViewProviderPartExt.cpp has concrete code to render OpenCASCADE CAD object in 3D view
class PartGuiExport ViewProviderPart : public ViewProviderPartExt
{
SoCoordinate3 * coords;
SoBrepFaceSet * faceset;
SoNormal * norm;
SoNormalBinding * normb;
SoBrepEdgeSet * lineset;
SoBrepPointSet * nodeset;
}
class ViewProviderShapeBuilder : public Gui::ViewProviderBuilder
src/Mod/Part/Gui/ViewProviderPython.cpp it is possible to access ViewProvider property in Python by aggregation: typedef
Gui::ViewProviderPythonFeatureT<ViewProviderPart> ViewProviderPython;
src/Mod/Part/Gui/ViewProviderCylinderParametric.cpp class PartGuiExport ViewProviderCylinderParametric:public
ViewProviderPart
src/Mod/Part/Gui/DlgPartCylinderImp.cpp no concrete code
src/Mod/Part/App/FeaturePartBox.h
/** App::Feature: Base class of all shape feature classes in FreeCAD */
class PartExport Feature : public App::GeoFeature
if (L < Precision::Confusion())
return new App::DocumentObjectExecReturn("Length of box too small") ;
if (W < Precision::Confusion())
return new App::DocumentObjectExecReturn("Width of box too small") ;
if (H < Precision::Confusion())
return new App::DocumentObjectExecReturn("Height of box too small") ;
try {
// Build a box using the dimension attributes
BRepPrimAPI_MakeBox mkBox(L, W, H);
TopoDS_Shape ResultShape = mkBox.Shape();
this->Shape.setValue(ResultShape);
}
catch (Standard_Failure) {
Handle_Standard_Failure e = Standard_Failure::Caught();
return new App::DocumentObjectExecReturn(e->GetMessageString());
4.3. VIEWPROVIDER FRAMEWORK AND 3D REDERRING 53
return App::DocumentObject::StdReturn;
}
}
//
class View3DInventorPy : public Py::PythonExtension<View3DInventorPy>
class View3DInventorViewerPy : public Py::PythonExtension<View3DInventorViewerPy>
Note: Quarter::SoQTQuarterAdaptor is derived from QGraphicsView
class GuiExport View3DInventorViewer : public Quarter::SoQTQuarterAdaptor, public Gui::SelectionSingleton::Obse
Gui::MDIView* ViewProviderDocumentObject::getInventorView() const
{
App::Document* pAppDoc = pcObject->getDocument();
Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc);
return mdi;
}
if (root) {
pcViewProviderRoot->addChild(root);
_ViewProviderMap[root] = pcProvider;
}
if (fore)
foregroundroot->addChild(fore);
if (back)
backgroundroot->addChild(back);
pcProvider->setOverrideMode(this->getOverrideMode());
_ViewProviderSet.insert(pcProvider);
}
setSceneGraph(pcViewProviderRoot);
4.4.1 src/Gui/Selection.h
This file has defiend important classes: SelectionObserver SelectionChanges SelectionObserverPython SelectionGate - Selec-
tionGate: allows or disallows selection of certain types. - SelectionObserver: observer pattern - SelectionChanges: as message
for Observer
This file is well documented, see the header file for all API src/Gui/Selection.h
class GuiExport SelectionSingleton : public Base::Subject<const SelectionChanges&>
bool SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectName, const char* pSubName, floa
4.4. SELECTION FRAMEWORK 55
/// returns the selected DocumentObject or NULL if the object is already deleted
const App::DocumentObject *getObject(void) const;
...
namespace Gui {
namespace DockWnd {
QListWidget* selectionView;
This class builds up a type/count tree out of a string to test very fast a selection or object/subelement type against it.
Example strings are: “SELECT Part::Feature SUBELEMENT Edge”, “SELECT Robot::RobotObject”, “SELECT
Robot::RobotObject COUNT 1..5”
4.4.5 src/Gui/MouseSelection.h
if (selection.size() != 1) {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"),
QObject::tr("Select an edge, face or body. Only one body is allowed."));
return;
}
56 CHAPTER 4. OVERVIEW OF GUI MODULE
if (!selection[0].isObjectTypeOf(Part::Feature::getClassTypeId())){
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong object type"),
QObject::tr("Fillet works only on parts"));
return;
}
In GUI applications many commands can be invoked via a menu item, a toolbar button or an accelerator key. The answer of
Qt to master this challenge is the class QAction. A QAction object can be added to a popup menu or a toolbar and keep the
state of the menu item and the toolbar button synchronized.
For example, if the user clicks the menu item of a toggle action then the toolbar button gets also pressed and vice versa. For
more details refer to your Qt documentation.
Since QAction inherits QObject and emits the triggered() signal or toggled() signal for toggle actions it is very convenient to
connect these signals e.g. with slots of your MainWindow class. But this means that for every action an appropriate slot of
MainWindow is necessary and leads to an inflated MainWindow class. Furthermore, it’s simply impossible to provide plugins
that may also need special slots – without changing the MainWindow class.
To solve these problems we have introduced the command framework to decouple QAction and MainWindow. The base
classes of the framework are Gui::CommandBase and Gui::Action that represent the link between Qt’s QAction world and the
FreeCAD’s command world.
The Action class holds a pointer to QAction and CommandBase and acts as a mediator and – to save memory – that gets
created (Gui::CommandBase::createAction()) not before it is added (Gui::Command::addTo()) to a menu or toolbar.
Now, the implementation of the slots of MainWindow can be done in the method activated() of subclasses of Command
instead.
For example, the implementation of the “Open file” command can be done as follows.
class OpenCommand : public Command
{
public:
OpenCommand() : Command("Std_Open")
{
// set up menu text, status tip, ...
sMenuText = "&Open";
sToolTipText = "Open a file";
sWhatsThis = "Open a file";
sStatusTip = "Open a file";
sPixmap = "Open"; // name of a registered pixmap
sAccel = "Shift+P"; // or "P" or "P, L" or "Ctrl+X, Ctrl+C" for a sequence
}
4.6. TASKVIEW FRAMEWORK: UI FOR INTERACTIVE DESIGN 57
protected:
void activated(int)
{
QString filter ... // make a filter of all supported file formats
QStringList FileList = QFileDialog::getOpenFileNames( filter,QString::null, getMainWindow() );
for ( QStringList::Iterator it = FileList.begin(); it != FileList.end(); ++it ) {
getGuiApplication()->open((*it).latin1());
}
}
};
An instance of OpenCommand must be created and added to the Gui::CommandManager to make the class known to FreeCAD.
To see how menus and toolbars can be built go to the Workbench Framework.
Both Qt C++ and python (file names start with TaskPanel) are used to design the UI (*.ui file generated by QtDesigner) for
FreeCAD. Related to setEdit(), unsetEdit() in ViewProvider class. Another Qt library ** is used. An image shows the
taskpanel is welcomed here!
}
src/Gui/TaskView/TaskView.h
class GuiExport TaskGroup : public QSint::ActionBox, public TaskContent
class GuiExport TaskView : public QScrollArea, public Gui::SelectionSingleton::ObserverType
{
//boost::signal connection + slot to App::Document
https://github.com/FreeCAD/FreeCAD/blob/master/src/Gui/TaskView/TaskView.h
// this is an example of QObject event system and boost::signal
}
class GuiExport TaskWatcher : public QObject, public Gui::SelectionFilter
/// List of TaskBoxes of that dialog
std::vector<QWidget*> Content;
58 CHAPTER 4. OVERVIEW OF GUI MODULE
public:
static ControlSingleton& instance(void);
static void destruct (void);
public Q_SLOTS:
void accept();
void reject();
void closeDialog();
/// raises the task view panel
void showTaskView();
private Q_SLOTS:
/// This get called by the TaskView when the Dialog is finished
void closedDialog();
private:
Gui::TaskView::TaskView *getTaskPanel();
private:
struct status {
std::bitset<32> StatusBits;
} CurrentStatus;
std::stack<status> StatusStack;
4.7. EXPRESSION AND QUANTITY 59
Gui::TaskView::TaskDialog *ActiveDialog;
private:
/// Construction
ControlSingleton();
/// Destruction
virtual ~ControlSingleton();
4.6.3 TaskDriver
src/Gui/PropertyPage.h
class GuiExport PreferencePage : public QWidget class GuiExport PreferenceUiForm : public PreferencePage
src/Mod/Fem/Gui/DlgSettingsFemCcxImp.h src/Mod/Fem/Gui/PrefWidgets.h wideges with save
class DlgSettingsFemCcxImp : public Gui::Dialog::PreferencePage, public Ui_DlgSettingsFemCcxImp
This section is mainly copied from FreeCAD documentation, see Internationalization with FreeCAD Doxygen document
position: Module->Gui->Internationalization with FreeCAD
The internationalization of FreeCAD makes heavy use of the internationalization support of Qt. For more details refer to
your Qt documentation. As FreeCAD will migrated to Qt5 in the future, QString::fromLatin1() should be used to convert
C-style char array and std::string in GUI code.
To integrate a new language into FreeCAD or one of its application modules you have to perform the following steps:
60 CHAPTER 4. OVERVIEW OF GUI MODULE
First you have to generate a .ts file for the language to be translated. You can do this by running the lupdate tool in the bin
path of your Qt installation. As argument you can specify either all related source files and the .ts output file or a Qt project
file (.pro) which contains all relevant source files.
To translate the english string literals into the language you want to support you can open your .ts file with QtLinguist and
translate all literals by hand. Another way for translation is to use the tool tsauto from Sebastien Fricker.This tool uses the
engine from Google web page (www.google.com).ts auto supports the languages
To get most of the literals translated you should have removed all special characters (like &, !, ?, . . . ). Otherwise the
translation could fail. After having translated all literals you can load the .ts file into QtLinguist and invoke the menu item
Release which generates the binary .qm file.
The .qm file should now be integrated into the GUI library (either of FreeCAD itself or its application module). The .qm file
will be embedded into the resulting binary file. So, at runtime you don’t need any .qm files any more. Indeed you will have a
bigger binary file but you haven’t any troubles concerning missing .qm files.
To integrate the .qm file into the executable you have to create a resource file (.qrc), first. This is an XML file where you can
append the .qm file. For the .qrc file you have to define the following curstom build step inside the Visual Studio project file:
For the gcc build system you just have to add the line .qrc to the BUILT_SOURCES sources section of the Makefile.am, run
automake and configure (or ./confog.status) afterwards.
4.8.2.4 Q_INIT_RESOURCE
Finally, you have to add a the line Q_INIT_RESOURCE(resource); where resource is the name of the .qrc file. That’s all!
“In FreeCAD we have our own little framework to create Python bindings for cpp classes but these classes are not prepared to
be sub-classed in Python.”
see example in src/Mod/TemplatePyMod/FeaturePython.py#113
def makeBox():
FreeCAD.newDocument()
a=FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Box")
Box(a)
if FreeCAD.GuiUp:
ViewProviderBox(a.ViewObject)
63
64 CHAPTER 5. INTRODUCTION TO PYTHON WRAPPING
There must be one cpp DocumentObject derived type like Part::Feature added to Document. Python class must ref/link to
the underlying cpp object, during __init__() It is the same for ViewProviderBox(a.ViewObject), which has a method of
attach(). more example can be found in Fem module
5.1.3 What is the limitation of pure python module except for performance?
Scripted objects pure python feature One particularity must be understood, those objects are saved in FreeCAD FcStd files
with python’s json module. cPickle is avoid for securtiy reason.
That module turns a python object as a string, allowing it to be added to the saved file. On load, the json module uses that
string to recreate the original object, provided it has access to the source code that created the object.
5.1.5 DocumentObjectPy
DocumentObjectPy is python export class for App::DocumentObject, “DocumentObjectPy.h” is not manually coded but
generated from DocumentObjectPy.xml file, and its implementation is coded in src/App/DocumentObjectPyImp.cpp.
Can ViewProviderPy, DocumentObjectPy be subclassed in python?
Yes, but it is not what FreeCAD usually do. Due to this the normal way is to do things by aggregation
(FeaturePythonT<>), if you insist on doing it by sub-classing a further Python wrapper class is needed.
If only new properties are needed for the derived class, just declare FeaturePythonT<> and extend DocumentObjectPy in
python. see FemSolver example in Fem module analysis.
• By far the most important change: All python strings are now unicode. So all strings-related methods are gone.
Fortunately since shoogen and werner already made a great job at spreading the use of UTF8 everywhere, this doesn’t
give major problems, it even simplifies (no more of that question to use Latin1 or Utf8)
• PyObject->ob_type doesn’t exist anymore, use Py_TYPE(PyObject)
• Class definition (PyTypeObject) has changed a lot:
• different way to initialize it (PyVarObject_HEAD_INIT instead of PyObject_HEAD_INIT)
• tp_getattr and tp_setattr slots are being deprecated. Use tp_getattro and tp_setattro instead (they take a PyObject
instead of a char*)
• several numeric handlers have been removed: divide (everything is remainder now), long (everything is long now, but
the handler to keep is int :? ), coerce, oct and hex.
For python code (97% problems in FreeCAD source are print() and key of dict):
• print(“something”,“something else”) doesn’t work in py2 unless “from future import print_function”
• for key, value in dict.iteritems() becomes for key,value in dict.items(), works in py2 too
• except Error,message becomes except Error(message) works in py2 too
• import submodule becomes from . import submodule
namespace py = pybind11;
PYBIND11_PLUGIN(example) {
py::module m("example", "pybind11 example plugin");
66 CHAPTER 5. INTRODUCTION TO PYTHON WRAPPING
py::class_<Pet>(m, "Pet")
.def(py::init<const std::string &>())
.def("setName", &Pet::setName)
.def("getName", &Pet::getName);
return m.ptr();
}
The python API of FreeCAD is mostly created by hand. See for example all files ending with *Py.cpp in the source code.
However, the reason not to use SWIG or SIP is that they are a bit overkilled and too complex. BTW, from one
version to another in SWIG there are always some slight internal changes which makes it very difficult to keep the
sources in shape that they work OK with different SWIG versions. We have made this experience with pivy – a
Python binding for Coin. This is a constant point of trouble we run into on different platforms Another even more
important reason is that we in most cases do not want to wrap the interface 1:1 of the Python and C++ class.
• C and C++ Python API Both <python.h> C API and pyCXX c++API are directly used. Wrapping FreeCAD cpp
code is kind of writing C module for python, emphasizing performance.
• Qt wrapping tool sip is not a choice, since FreeCAD BaseClass is not derived from QObject. However, it is possible
to design all FreeCAD classes derived from QObject with pros and cons. FreeCAD can be run without GUI, so the
FreeCAD objects should not depends/mixed with QObject.
• swig, It is used only to generate pivy objects from FreeCADGui. swig code can be found at the end of source file
src/Base/Interpreter.cpp There is no stable ABI for wrapping, each time swig upgrade, even a mino upgrade from 3.0 to
3.1, a compilation is needed.
There are also a lot of helper structures and templates to ease with that tedious process. For example, in the
different src/Mod folders, when you see a Py.xml file together with a PyImp.cpp, the xml files contains a structure
that will automatically generate Py.h and Py.cpp files at build time, to which the *.PyImp.cpp will be merged.
It is possible to parse C++ header file to generate the interface definiation like the xml file Implementing a code generator
with libclang
Direct usage of C API is NOT recommended, since C API is not compatible for the migration from python 2.7 to python 3.3+
Recently, Python 3.x defined a set of Stable Application Binary Interface(ABI), see https://docs.python.org/3/c-api/stable.html
If module developer wants to mimic some new feature from existent code, understanding of common API in python.h is
essential
5.2.6 other tools to automatically generate wrapping code for Python Scripting
Py++ uses GCC C++ compiler to parse C++ source files and allows you to expose C++ code to Python in quick and elegant
way using the Boost.Python library.
It uses the following steps to do so: - source code is passed to GCC-XML - GCC-XML passes it to GCC C++ compiler -
GCC-XML generates an XML description of a C++ program from GCC’s internal representation. - Py++ uses pygccxml
package to read GCC-XML generated file.
For module developer who works only at the DocumentObject level, usage of FeaturePythonT could be sufficient without
touching PyObject*
FeaturePythonT Generic Python feature class which allows to behave every DocumentObject derived class as Python feature –
simply by subclassing. FeatureT
!DocumentObjectPy__inherit__graph
This file is generated by src/Tools/generateTemaplates/templateClassPyExport.py out of the XML file
Automaticall python wrapping code can be generated by python script in bulding tools.
src/Mod/Part/App/ConePy.xml is are built by hand (which could be generate from text definition file or swig scanning
from header file in the future), then there is a cmake macro that converts them in Py.h and Py.cpp files at build time (an
accompanying *PyImp.cpp file must be present).
In the src/Mod/Part/App/AppPart.cpp, this python type is registered to interpreter Base::Interpreter().addType(&Part::ConePy
::Type,partModule,"Cone"); which is implemeted in src/Base/Interpreter.cpp
void InterpreterSingleton::addType(PyTypeObject* Type,PyObject* Module, const char * Name)
{
// NOTE: To finish the initialization of our own type objects we must
// call PyType_Ready, otherwise we run into a segmentation fault, later on.
// This function is responsible for adding inherited slots from a type's base class.
if (PyType_Ready(Type) < 0) return;
union PyType_Object pyType = {Type};
PyModule_AddObject(Module, Name, pyType.o);
}
68 CHAPTER 5. INTRODUCTION TO PYTHON WRAPPING
Then this cpp type/class is registered into cpp type system in src/Mod/Part/AppPart.cpp Part::Cone ::init();
void BaseClass::init(void)
{
assert(BaseClass::classTypeId == Type::badType() && "don't init() twice!");
/* Make sure superclass gets initialized before subclass. */
/*assert(strcmp(#_parentclass_), "inherited"));*/
/*Type parentType(Type::fromName(#_parentclass_));*/
/*assert(parentType != Type::badType() && "you forgot init() on parentclass!");*/
Proxy is a property of App::PropertyPythonObject Proxy;. Both methods defined in Python and cpp will be called, see []
Python needs not to specify which class is derived, just provide the methods(API).
Todo: This section is not completed!!! Sequence? derived from *Imp
def attach(self, vobj):
self.ViewObject = vobj
self.Object = vobj.Object
self.bubbles = None
The ViewProvider attachment happens here src/Gui/ViewProviderPythonFeature.cpp#L299
protected:
virtual void onChanged(const App::Property* prop) {
if (prop == &Proxy) {
if (ViewProviderT::pcObject && !Proxy.getValue().is(Py::_None())) {
if (!_attached) {
_attached = true;
imp->attach(ViewProviderT::pcObject);
ViewProviderT::attach(ViewProviderT::pcObject);
// needed to load the right display mode after they're known now
ViewProviderT::DisplayMode.touch();
ViewProviderT::setOverrideMode(viewerMode);
}
ViewProviderT::updateView();
}
}
else {
imp->onChanged(prop);
ViewProviderT::onChanged(prop);
}
}
5.3. EXTENDING CPP CLASS FUNCTION IN PYTHON 69
src/App/FeaturePythonPyImp.h FeaturePyT
// Special Feature-Python classes
typedef FeaturePythonT<DocumentObject> FeaturePython;
typedef FeaturePythonT<GeoFeature > GeometryPython;
src/App/FeaturePython.h
// Helper class to hide implementation details
class AppExport FeaturePythonImp
...
template <class FeatureT>
class FeaturePythonT : public FeatureT
{
...
/// recalculate the Feature
virtual DocumentObjectExecReturn *execute(void) {
try {
bool handled = imp->execute();
if (!handled)
return FeatureT::execute();
}
catch (const Base::Exception& e) {
return new App::DocumentObjectExecReturn(e.what());
}
return DocumentObject::StdReturn;
}
...
private:
FeaturePythonImp* imp;
DynamicProperty* props;
PropertyPythonObject Proxy;
};
FemSolverObject is derived from DocumentObject without any proeprty added in C++, but it can be extended in Python.
Look at the template class FeaturePythonT, it is of the form: src/App/FeaturePython.h
template <class FeatureT>
class FeaturePythonT : public FeatureT
template FeatureT is the parent class of FeaturePythonT.
src/Mod/Fem/App/FemSolverObject.h class AppFemExport FemSolverObject : public App::DocumentObject typedef
App::FeaturePythonT<FemSolverObject> FemSolverObjectPython; FemSolverObjectPython is a type of sub-class of
Fem::Fem::FemSolverObject.
src/Mod/Fem/App/FemSolverObject.cpp
PyObject *FemSolverObject::getPyObject()
{
if (PythonObject.is(Py::_None())){
// ref counter is set to 1
PythonObject = Py::Object(new DocumentObjectPy(this),true);
}
return Py::new_reference_to(PythonObject);
}
namespace App {
/// @cond DOXERR
PROPERTY_SOURCE_TEMPLATE(Fem::FemSolverObjectPython, Fem::FemSolverObject)
template<> const char* Fem::FemSolverObjectPython::getViewProviderName(void) const {
return "FemGui::ViewProviderSolverPython";
}
5.3.4 Gui::ViewProviderPythonFeatureT
NB, if imp->setEdit(ModNum) return true, ViewProviderT::setEdit(ModNum); is not called! Why? It is same for unset()
and doubleClicked(void)
/// is called by the document when the provider goes in edit mode
virtual bool setEdit(int ModNum)
{
bool ok = imp->setEdit(ModNum);
if (!ok) ok = ViewProviderT::setEdit(ModNum);
return ok;
}
// Path + radius
if (!PyArg_ParseTuple(args, "O!d|sii", &(TopoShapePy::Type), &pshape, &radius, &scont, &maxdegree, &maxsegm
return 0;
std::string str_cont = scont;
int cont;
if (str_cont == "C0")
cont = (int)GeomAbs_C0;
else if (str_cont == "C1")
cont = (int)GeomAbs_C1;
else if (str_cont == "C2")
cont = (int)GeomAbs_C2;
else if (str_cont == "C3")
cont = (int)GeomAbs_C3;
else if (str_cont == "CN")
cont = (int)GeomAbs_CN;
else if (str_cont == "G1")
cont = (int)GeomAbs_G1;
else if (str_cont == "G2")
cont = (int)GeomAbs_G2;
else
cont = (int)GeomAbs_C0;
72 CHAPTER 5. INTRODUCTION TO PYTHON WRAPPING
try {
const TopoDS_Shape& path_shape = static_cast<TopoShapePy*>(pshape)->getTopoShapePtr()->_Shape;
TopoShape myShape(path_shape);
TopoDS_Shape face = myShape.makeTube(radius, tolerance, cont, maxdegree, maxsegment);
return new TopoShapeFacePy(new TopoShape(face));
}
catch (Standard_Failure) {
Handle_Standard_Failure e = Standard_Failure::Caught();
PyErr_SetString(PartExceptionOCCError, e->GetMessageString());
return 0;
}
}
C wrapper funtions defined in src/Mod/Part/App/AppPartPy.cpp are registered into an Array of PyCMethodDef
/* registration table */
struct PyMethodDef Part_methods[] = {
{"open" ,open ,METH_VARARGS,
"open(string) -- Create a new document and load the file into the document."},
...
/** If the application starts we release immediately the global interpreter lock
* (GIL) once the Python interpreter is initialized, i.e. no thread -- including
* the main thread doesn't hold the GIL. Thus, every thread must instantiate an
* object of PyGILStateLocker if it needs to access protected areas in Python or
* areas where the lock is needed. It's best to create the instance on the stack,
* not on the heap.
*/
class BaseExport PyGILStateLocker
{
public:
PyGILStateLocker()
{
gstate = PyGILState_Ensure();
}
~PyGILStateLocker()
{
PyGILState_Release(gstate);
}
private:
PyGILState_STATE gstate;
};
/**
* If a thread holds the global interpreter lock (GIL) but runs a long operation
* in C where it doesn't need to hold the GIL it can release it temporarily. Or
* if the thread has to run code in the main thread where Python code may be
* executed it must release the GIL to avoid a deadlock. In either case the thread
* must hold the GIL when instantiating an object of PyGILStateRelease.
* As PyGILStateLocker it's best to create an instance of PyGILStateRelease on the
* stack.
*/
class BaseExport PyGILStateRelease
{
public:
PyGILStateRelease()
5.4. ADVANCED TOPICS: GIL AND MANUALLY WRAPPING 73
{
// release the global interpreter lock
state = PyEval_SaveThread();
}
~PyGILStateRelease()
{
// grab the global interpreter lock again
PyEval_RestoreThread(state);
}
private:
PyThreadState* state;
};
Here follows a short description of how your own workbench can be added to a module.
First you have to subclass either Workbench or StdWorkbench and reimplement the methods setupMenuBar(), setupToolBars(),
setupCommandBars() and setupDockWindows().
The difference between both classes is that these methods of Workbench are pure virtual while StdWorkbench defines already
the standard menus and toolbars, such as the ‘File’, ‘Edit’, . . . , ‘Help’ menus with their common functions.
If your class derives from Workbench then you have to define your menus, toolbars and toolbox items from scratch while
deriving from StdWorkbench you have the possibility to add your preferred functions or even remove some unneeded functions.
class MyWorkbench : public StdWorkbench
{
...
protected:
MenuItem* setupMenuBar() const
{
MenuItem* root = StdWorkbench::setupMenuBar();
// your changes
return root;
}
ToolBarItem* setupToolBars() const
75
76 CHAPTER 6. MODULAR DESIGN OF FREECAD (PLUGIN SYSTEM)
{
ToolBarItem* root = StdWorkbench::setupToolBars();
// your changes
return root;
}
ToolBarItem* setupCommandBars() const
{
ToolBarItem* root = StdWorkbench::setupCommandBars();
// your changes
return root;
}
};
//or
If you want to customize your workbench by adding or removing items you can use the ToolBarItem class for customizing
toolbars and the MenuItem class for menus. Both classes behave basically the same. To add a new menu item you can do it
as follows
Toolbars can be customized the same way unless that you shouldn’t create subitems (there are no subtoolbars).
6.1. WORKBENCH FRAMEWORK: KEY TO MODULAR DESIGN 77
Once you have implemented your workbench class you have to register it to make it known to the FreeCAD core system.
You must make sure that the step of registration is performed only once. A good place to do it is e.g. in the global
function initMODULEGui in AppMODULEGui.cpp where MODULE stands for the name of your module. Just add the line
MODULEGui::MyWorkbench::init(); somewhere there.
Though your workbench has been registered now, at this stage you still cannot invoke it yet. Therefore you must create
an item in the list of all visible workbenches. To perform this step you must open your InitGui.py (a Python file) and do
some adjustments. The file contains already a Python class MODULEWorkbench that implements the Activate() method (it
imports the needed library). You can also implement the GetIcon() method to set your own icon for your workbench, if not,
the default FreeCAD icon is taken, and finally the most important method GetClassName(). that represents the link between
Python and C++. This method must return the name of the associated C++ including namespace. In this case it must the
string ModuleGui::MyWorkbench. At the end you can change the line from
Gui.addWorkbench("MODULE design",MODULEWorkbench()) to Gui.addWorkbench("My workbench",MODULEWorkbench())
or whatever you want.
6.1.1.5 Note
You must make sure to choose a unique name for your workbench (in this example “My workbench”). Since FreeCAD doesn’t
provide a mechanism for this you have to care on your own.
One of the key concepts of the workbench framework is to load a module at runtime when the user needs some function
that it provides. So, if the user doesn’t need a module it never gets loaded into RAM. This speeds up the startup procedure
of FreeCAD and saves memory. At startup FreeCAD scans all module directories and invokes InitGui.py. So an item for
a workbench gets created. If the user clicks on such an item the matching module gets loaded, the C++ workbench gets
registered and activated.
The user is able to modify a workbench (Edit|Customize). E.g. he can add new toolbars or items for the toolbox and add his
preferred functions to them. But he has only full control over “his” toolbars, the default workbench items cannot be modified
or even removed.
FreeCAD provides also the possibility to define pure Python workbenches. Such workbenches are temporarily only and are
lost after exiting the FreeCAD session. But if you want to keep your Python workbench you can write a macro and attach it
with a user defined button or just perform the macro during the next FreeCAD session. Here follows a short example of how
to create and embed a workbench in Python
w=Workbench() # creates a standard workbench (the same as StdWorkb
w.MenuText = "My Workbench" # the text that will appear in the combo box
dir(w) # lists all available function of the object
FreeCADGui.addWorkbench(w) # Creates an item for our workbenmch now
# Note: We must first add the workbench to run some
# Then we are ready to customize the workbench
list = ["Std_Test1", "Std_Test2", "Std_Test3"] # creates a list of new functions
w.appendMenu("Test functions", list) # creates a new menu with these functions
w.appendToolbar("Test", list) # ... and also a new toolbar
It is error-prone to mix C++ and Python. Furtunately, it is possible using Python only to develop plugin, Cfd or ‘plot’
workbench is the example.
class CfdWorkbench(Workbench):
"CFD workbench object"
def __init__(self):
self.__class__.Icon = FreeCAD.getResourceDir() + "Mod/Fem/Resources/icons/FemWorkbench.svg"
self.__class__.MenuText = "CFD"
self.__class__.ToolTip = "CFD workbench"
def Initialize(self) :
import Fem
import FemGui
import _CommandCfdAnalysis
import _CommandCfdSolverFoam
import _CommandCfdSolverControl
import _CommandCfdResult
def GetClassName(self):
return "Gui::PythonWorkbench"
Gui.addWorkbench(CfdWorkbench())
Icon could be XPM embedded into source code, or just pick up one from other module. Python workbench could has its own
“Resource” folder under module folder, instead of “Mod/ModName/Gui/Resource”.
Translation:
cpp developer can get unlimited acess to FreeCAD API
Gui code should go here, like classes derived from TaskView, ViewProvider
C++ code in App subfolder
• App/PreCompiled.h include some headers shared by most source code files
• App/PreCompiled.cpp include some headers shared by most source code files
• App/CMakeLists.txt cmake config file to generate dll or so shared dynamically linkable lib
• Gui/AppFem.cpp init_type,init DocumentObject
• Gui/AppFemPy.cpp register types, methods exported to Python
#methods can be accessed in python: import Fem dir(Fem)
C++ code in Gui subfolder
• Gui/Workbench.h to declare module workbench derived from Gui::Workbench
• Gui/Workbench.cpp
• Gui/AppFemGui.cpp
within function of initFemGui(): - Fem_Import_methods[] - load comand.cpp, - workbench and ViewProvider
init(), - Base::Interpreter().loadModule('python modules') - register preferences pages - load resource,
mainly translation
• Gui/AppFemGuiPy.cpp wrapping code to export functions to python
/* registration table */ struct PyMethodDef FemGui_Import_methods[]
• Gui/PreCompiled.h include some headers shared by most source code files
• Gui/PreCompiled.cpp contains single line #include "PreCompiled.h"
• Gui/CMakeLists.txt cmake config file to generate dll or so shared dynamically linkable lib
• Gui/Command.cpp to add Toolbar and MenuItem to module workbench
• Gui/Resources/Fem.qrc file contains translattion for Qt widgets
•
The module code is organized similar with FreeCAD source in the module folder. Gui related C++ code is located in “Gui”
subfolder, whle nonGui code are put into “App” subfolder. A module folder structure with essential code for the new module
can be geneated by fcbt script
Samole plugin has a standard folder structure
The generate code has no comment, and extra code should be included and trimmed by module developer .
Some good example and best practice should be included.
Insert command: CM
Please enter a name for your application:Cfd
Copying files... from _TEMPLATE_ folder and modify them
...
Modifying files...
../Mod/Cfd/InitGui.py
../Mod/Cfd/Init.py
../Mod/Cfd/CMakeLists.txt
../Mod/Cfd/App/PreCompiled.h
../Mod/Cfd/App/AppCfd.cpp
../Mod/Cfd/App/PreCompiled.cpp
../Mod/Cfd/App/CMakeLists.txt
../Mod/Cfd/App/AppCfdPy.cpp
../Mod/Cfd/Cfd.dox
../Mod/Cfd/Gui/PreCompiled.h
../Mod/Cfd/Gui/Workbench.cpp
../Mod/Cfd/Gui/AppCfdGui.cpp
../Mod/Cfd/Gui/PreCompiled.cpp
../Mod/Cfd/Gui/CMakeLists.txt
../Mod/Cfd/Gui/Command.cpp
../Mod/Cfd/Gui/AppCfdGuiPy.cpp
../Mod/Cfd/Gui/Workbench.h
../Mod/Cfd/Gui/Resources/Cfd.qrc
Modifying files done.
Cfd module created successfully.
Python Init.py registered import and export file types, and “InitGui.py” append command class or othe UI elements to
module workbench
C++ side registered type and export to python, a similar but much simplier process as [src/App/Applicaton.cpp] and
src/App/ApplicatonPy.cpp
For example, src/Mod/Fem/Gui/AppFemGui.cpp registered all viewProvider types, C++ commands classes defined in
command.cpp, load extra python module.
• MakeBottle.py good example of making complex gemoetry from points to wires to face to solid
**class _PartJoinFeature** is a community contributed pure python, extending Part::FeaturePython, self.Type
= “PartJoinFeature”
• JoinPartFeature.py good exaple of pure python implemented Feature
• App/FT2FC.h FreeType font to FreeCAD python related tool
• App/FeatureReference.h Base class of all shape feature classes in FreeCAD
class PartExport FeatureReference : public App::GeoFeature
• App/PartFeature.h feature like Loft, Sweep, etc
• App/PartFeatures.h feature like Loft, Sweep, etc
• App/Primitive.h define primitive Vertex, Line,Plane, Cube,Sphere, Cone, Torus, Helix
class PartExport Primitive : public Part::Feature
6.3. PART MODULE 81
- [App/CrossSection.h](https://github.com/FreeCAD/FreeCAD/tree/master/src/Mod/Part/App/CrossSection.h)
- [App/FeatureBoolean.h](https://github.com/FreeCAD/FreeCAD/tree/master/src/Mod/Part/App/FeatureBoolean.h)
• Gui/
6.3.2 src/Mod/Part/App/PartFeature.h
}
src/Mod/Part/App/PartFeature.cpp
Feature::Feature(void)
{
ADD_PROPERTY(Shape, (TopoDS_Shape()));
}
PyObject *Feature::getPyObject(void)
{
if (PythonObject.is(Py::_None())){
// ref counter is set to 1
PythonObject = Py::Object(new PartFeaturePy(this),true);
}
return Py::new_reference_to(PythonObject);
}
82 CHAPTER 6. MODULAR DESIGN OF FREECAD (PLUGIN SYSTEM)
src/Mod/PartDesign/App/Feature.h
class PartDesignExport Feature : public Part::Feature static TopoDS_Shape getSolid(const TopoDS_Shape&);
src/Mod/PartDesign/App/FeaturePad.h FeaturePad<- FeatureAdditive <- SketchBased <- PartDesign::Feature
App::PropertyLinkSub UpToFace; // refer to face (subfeature) of another Feature
App::PropertyLink Sketch; // 2D sketch for Pad
src/Mod/PartDesign/App/FeatureDressUp.h App::PropertyLinkSub Base; // class PartDesignExport Face : public
Part::Part2DObject
VIS component provides adaptation functionality for visualization of OCCT topological shapes by means of VTK library.
OCC has been released under LGPL in 2013 , not from OCC license any more.
http://www.opencascade.com/doc/occt-6.9.0/overview/html/technical_overview.html
App:: Open CASCADE Application Framework (OCAF). Base:: Open CASCADE Foundation Classes GUI: why not choose
Open CASCADE visualization module, but Coin3D
Topology defines relationships between simple geometric entities. A shape, which is a basic topological entity, can be divided
into components (sub-shapes):
6.4. EXTRA ADDONS/PLUGINS AND INSTALLATION 83
Besides modules included in official source code src/Mod, extra modules can be found and installed from add-ons repository
for FreeCAD https://github.com/FreeCAD/FreeCAD-addons
• bolts:
• fasteners:
• sheetmetal: metalsheeting
• symbols_library:
Acknowledge of Fem module developers: Bernd, PrzemoF, etc., of course, three core developers.
This module is usable in v0.16 (install netgen and calculix first) and extended dramatically to multiple CAE solver support in
v0.17.
Basically, the whole process is to mesh the solid part into small element (currently tetragen only), add boundary condition and
material information, write all these information into case file that external solver can accept, launch the external solver (only
Calculix is supported for the time being), finally load result from solver output file and show result in FreeCAD workbench.
For example, error message “CalculiX ccx binary not found! Please set it manually in FEM preferences.” is solved by
For Ubuntu 64bit linux, ccx_2.10 single file executable can be downloaded from http://www.dhondt.de/ the dependency
libgfortran.so can be made available from symbolic link:
ln -s <source_path> /lib/x86_64-linux-gnu/libgfortran.so
Althouth most of the class started with the prefix of “Fem”, they works for various CAE application( mechanical, fluidic,
elctromagnetic).
85
86 CHAPTER 7. FEM MODULE SOURCE CODE ANALYSIS
Fem module specific file types import and export, this is implemented in src/Mod/Fem/App/FemAppPy.cpp Various mesh
format can be imported and exported, see the list in FreeCAD wiki for Fem
class Module : public Py::ExtensionModule<Module>
{
public:
Module() : Py::ExtensionModule<Module>("Fem")
{
add_varargs_method("open",&Module::open,
"open(string) -- Create a new document and a Mesh::Import feature to load the file into the documen
);
add_varargs_method("insert",&Module::insert,
"insert(string|mesh,[string]) -- Load or insert a mesh into the given or active document."
);
add_varargs_method("export",&Module::exporter,
"export(list,string) -- Export a list of objects into a single file."
);
add_varargs_method("read",&Module::read,
"Read a mesh from a file and returns a Mesh object."
);
add_varargs_method("show",&Module::show,
"show(shape) -- Add the shape to the active document or create one if no document exists."
);
initialize("This module is the Fem module."); // register with Python
}
functionality of src/Mod/Fem/Gui/AppFemGui.cpp
• FemGui_Import_methods[] all method in python module FemGui should be regestered
• load commands defiend in command.cpp,
• workbench and ViewProvider types init(),
• Base::Interpreter().loadModule('some python modules in Mod/Fem folder')
• Register preferences pages new Gui::PrefPageProducer<FemGui::DlgSettingsFemImp> ("FEM");
• load resource, mainly icons and translation
In short, python scripts for Fem should be loaded/imported in InitGui.py to avoid cyclic dependency.
see Forum discussion: cyclic dependency FemCommands and FemGui modules
There seems a cyclic dependency. When you try to load FemCommands.py it internally tries to load FemGui. However, at
this stage FemCommands.py is not yet loaded and FemGui also tries to load FemCommands.py.
Then there are two flaws inside initFemGui:
1. Base::Interpreter().loadModule() MUST be inside a try/catch block and in case an exception occurs the initFemGui
must be aborted and an import error must be raised. Fixed with git commit abd6e8c438c
7.3. KEY CLASSES ANALYSIS 87
2. The FemGui must not be initialized before dependent modules are fully loaded. Fixed with git commit 60c8180079f20
The latter fix currently causes the FemGui not to load any more but that’s because of the cyclic dependency. IMO, there are
two ways to fix:
1. Do not load any of the Python module inside initFemGui because I don’t see why they should be needed there. It’s
much better to move this to the Initialize() method of the Workbench class (InitGui.py)
2. Alternatively, make sure that the Python modules loaded inside initFemGui does not load FemGui in the global scope
but do it locally where it’s really needed.
This is the container DocumentObject in this module. It is NOT a DocumentGroup object, but accepts drag-and-drop
of only FEM specific DocumentObjects. Only Fem related DocumentObject can be dragged into, see ViewProviderFem-
Analysis::canDragObject in src/Mod/Fem/Gui/ViewProviderAnalysis.h. And any new Fem specific DocumentObject should
registered here.
bool ViewProviderFemAnalysis::canDragObject(App::DocumentObject* obj) const
{
if (!obj)
return false;
if (obj->getTypeId().isDerivedFrom(Fem::FemMeshObject::getClassTypeId()))
return true;
else if (obj->getTypeId().isDerivedFrom(Fem::FemSolverObject::getClassTypeId()))
return true;
else if (obj->getTypeId().isDerivedFrom(Fem::Constraint::getClassTypeId()))
return true;
else if (obj->getTypeId().isDerivedFrom(Fem::FemSetObject::getClassTypeId()))
return true;
else if (obj->getTypeId().isDerivedFrom(Base::Type::fromName("Fem::FeaturePython")))
return true;
else if (obj->getTypeId().isDerivedFrom(App::MaterialObject::getClassTypeId()))
return true;
else
return false;
}
It has no 3D representation in Inventor/Coin scenegraph, different from FemMeshObject or Fem::Constraint. It has an
Documdent Observer in GUI part. see src/Mod/Fem/Gui/ActiveAnalysisObserver.h It is a singleton instance static
ActiveAnalysisObserver* instance();, from which FemGui.getActiveAnalysis() is possible from python.
see void ActiveAnalysisObserver::setActiveObject(Fem::FemAnalysis* fem) in src/Mod/Fem/Gui/ActiveAnalysisObserver.cp
for activeView and activeDocument are managed
namespace FemGui {
void setActiveObject(Fem::FemAnalysis*);
Fem::FemAnalysis* getActiveObject() const;
88 CHAPTER 7. FEM MODULE SOURCE CODE ANALYSIS
7.3.2 src/Mod/Fem/MechanicalMaterial.py
This class defines necessary proeprty to show result, e.g. contour, in 3D scene. This class should be extended in python to
deal with result from different solver (different result file type).
Bottom-up analysis of this Object:
1. FemResultObject is derived from DocumdentObject with some properties, defined in src/Mod/Fem/App/FemResultObject.h
App::PropertyIntegerList ElementNumbers;
/// Link to the corresponding mesh
App::PropertyLink Mesh;
/// Stats of analysis
App::PropertyFloatList Stats;
/// Displacement vectors of analysis
App::PropertyVectorList DisplacementVectors;
/// Lengths of displacement vestors of analysis
App::PropertyFloatList DisplacementLengths;
/// Von Mises Stress values of analysis
App::PropertyFloatList StressValues;
implemented in src/Mod/Fem/App/FemResultObject.cpp. Most of code is standard, with the defiend properties instantiated
in constructor.
2. ViewProvider: [src/Mod/Fem/Gui/ViewProviderResult.h] and src/Mod/Fem/Gui/ViewProviderResult.cpp
3. add Command class and appened to workbench menu in Python
src/Mod/Fem/_CommandShowResult.py
class _CommandMechanicalShowResult:
"the Fem JobControl command definition"
def GetResources(self):
return {'Pixmap': 'fem-result',
'MenuText' : QtCore.QT_TRANSLATE_NOOP("Fem_Result", "Show result"),
'Accel' : "S, R",
'ToolTip' : QtCore.QT_TRANSLATE_NOOP("Fem_Result", "Show result information of an analysis")}
def Activated(self):
self.result_object = get_results_object(FreeCADGui.Selection.getSelection())
if not self.result_object:
QtGui.QMessageBox.critical(None, "Missing prerequisite", "No result found in active Analysis")
return
7.3. KEY CLASSES ANALYSIS 89
taskd = _ResultControlTaskPanel()
FreeCADGui.Control.showDialog(taskd)
def IsActive(self):
return FreeCADGui.ActiveDocument is not None and results_present()
In this class, sPixmap = "fem-result" and helper function get_results_object is worth of explanation src/Mod/Fem/Gui/Resources
SVG file naming: lowercase with module name as suffix, connected with dash
def get_results_object(sel):
if (len(sel) == 1):
if sel[0].isDerivedFrom("Fem::FemResultObject"):
return sel[0]
for i in FemGui.getActiveAnalysis().Member:
if(i.isDerivedFrom("Fem::FemResultObject")):
return i
return None
If implemented in C++, this class must derived from Command, see example in src/Mod/Fem/Gui/Command.cpp
DEF_STD_CMD_A is a macro defined in src/Gui/Command.h
DEF_STD_CMD_A(CmdFemConstraintFixed);
CmdFemConstraintFixed::CmdFemConstraintFixed()
: Command("Fem_ConstraintFixed")
{
sAppModule = "Fem";
sGroup = QT_TR_NOOP("Fem");
sMenuText = QT_TR_NOOP("Create FEM fixed constraint");
sToolTipText = QT_TR_NOOP("Create FEM constraint for a fixed geometric entity");
sWhatsThis = "Fem_ConstraintFixed";
sStatusTip = sToolTipText;
sPixmap = "fem-constraint-fixed";
}
if(getConstraintPrerequisits(&Analysis))
return;
doCommand(Gui,"Gui.activeDocument().setEdit('%s')",FeatName.c_str());
}
bool CmdFemConstraintFixed::isActive(void)
{
return hasActiveDocument();
}
At the end of this file:
void CreateFemCommands(void)
{
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
90 CHAPTER 7. FEM MODULE SOURCE CODE ANALYSIS
...
rcCmdMgr.addCommand(new CmdFemConstraintFixed());
4. TaskView: defined in python: src/Mod/Fem/_TaskPanelShowResult.py
5. python script to read result data file: src/Mod/Fem/ccxFrdReader.py
7.4 FemConstraint
• DocumentObject: FemConstraint is derived from
• ViewProvider: ViewProviderFemConstraint
• TaskPanel and TaskDlg:
• CMakeList.txt: adding those source files into CMakeList.txt
• SVG icon file in researce folder and XML file
Actually drawing is defined in ViewProviderFemConstraint.cpp
createSymmetry(sep, HEIGHT, WIDTH);
createPlacement(pShapeSep, b, SbRotation(SbVec3f(0,1,0), ax));
// gear , change colorProperty, it is a new , aspect ratio, it is new constraint
This is an DocumentObject with basic 3D Inventor, and a good start point to learn drawing in 3D scence. Fem::Constrait is
the base class for all the other specific constraints, or boundary conditions, in other domain like CFD.
class FemGuiExport ViewProviderFemConstraint : public Gui::ViewProviderGeometryObject
#define CUBE_CHILDREN 1
void ViewProviderFemConstraint::createCube(SoSeparator* sep, const double width, const double length, const dou
{
SoCube* cube = new SoCube();
cube->width.setValue(width);
cube->depth.setValue(length);
cube->height.setValue(height);
sep->addChild(cube);
}
SoSeparator* ViewProviderFemConstraint::createCube(const double width, const double length, const double height
{
SoSeparator* sep = new SoSeparator();
createCube(sep, width, length, height);
return sep;
}
void ViewProviderFemConstraint::updateCube(const SoNode* node, const int idx, const double width, const double
{
const SoSeparator* sep = static_cast<const SoSeparator*>(node);
SoCube* cube = static_cast<SoCube*>(sep->getChild(idx));
cube->width.setValue(width);
cube->depth.setValue(length);
cube->height.setValue(height);
}
src/Mod/Fem/Gui/ViewProviderFemConstraintPressure.h draws symbol to represent constrain attachment in 3D view scene
7.5. FEMMESH, BASED ON SALOME SMESH 91
7.4.3 TaskFemConstraint
src/Mod/Fem/Gui/TaskFemConstraint.h onChange(): Constraint only record geometry reference, not femCellSet, accept()
add into Analysis src/Mod/Fem/Gui/TaskFemConstraintPressure.cpp
#include "moc_TaskFemConstraintPressure.cpp"
[/src/Mod/Fem/Gui/TaskFemConstraintPressure.h] task panel to select geometry face the pressure constrain is applied to,
also the pressure magnitude and direction.
class TaskFemConstraintPressure : public TaskFemConstraint
class TaskDlgFemConstraintPressure : public TaskDlgFemConstraint accept/reject/open
SMESH: Salome Mesh, supporting both FEM and CFD meshing. Python script is possible in Salome platform.
7.5.2 src/Mod/Fem/App/FemMesh.h
FreeCAD Fem mesh is based on 3party lib: SMESH, the meshing facility used in Salome. This SMESH is powerful but also
chanllenging. It is a deep water zone, just ignore this class if you are not going to extend Fem meshing facility.
SMDS_Mesh
SMESH_Mesh
SMESHDS_Mesh
SMESH_SMDS.hxx
Important classes:
class AppFemExport FemMesh : public Data::ComplexGeoData
class AppFemExport FemMeshObject : public App::GeoFeature
class AppFemExport FemMeshShapeObject : public FemMeshObject
class AppFemExport FemMeshShapeNetgenObject : public FemMeshShapeObject //with Fineness property
src/Mod/Fem/App/FemMeshShapeNetgenObject.cpp
FemMeshShapeNetgenObject.cpp has no python corresponding object, to set and recompute mesh in python???
src/Mod/Fem/Gui/TaskDlgMeshShapeNetgen.cpp accept() should have some macro recording code like TaskFemConstraint-
Force’s src/Mod/Fem/Gui/TaskTetParameter.h
Gmsh is supported mainly by Macro, GUI supported Netgen plugin only
src/Mod/Fem/Gui/TaskCreateNodeSet.cpp nodeset
FemSetObject::FemSetObject()
{
ADD_PROPERTY_TYPE(FemMesh,(0), "MeshSet link",Prop_None,"MeshSet the set belongs to");
}
src/Mod/Fem/Gui/TaskCreateNodeSet.cpp nodeset
7.6.1 FemResult
src/Mod/Fem/Gui/FemResultObject.h defined several properties to hold data like: Time, Temperature, Displacement,etc.
Result mesh can be different from mesh written to solver. It is defined only for solid mechanics, not for fluid dynamics.
This is class has implemented FeatureT<> template, thereby, it can be extended in python into CfdResult for CFD module.
related files:
src/Mod/Fem/App/FemPostObject.h src/Mod/Fem/App/FemPostPipeline.h src/Mod/Fem/App/FemPostFilter.h
src/Mod/Fem/App/FemPostFunction.h
Task panel and view providers in src/Mod/Fem/Gui
It could be thought of miniature paraview pipeline. Implemented in cpp only, perhaps for speed concern.
namespace FemGui {
public:
7.8. QT SPECIFIC UI DESIGN 93
protected:
void saveSettings();
void loadSettings();
void changeEvent(QEvent *e);
};
• src/Mod/Fem/Gui/DlgSettingsFem.ui
• src/Mod/Fem/Gui/DlgSettingsFemImp.cpp
The implementation is surprisingly convenient, just calling onSave() and onRestore() methods of standard PrefWidget defined
in src/Gui/PrefWidgets.h
This UI file uses some FreeCAD costumed widgets, e.g. <widget class="Gui::PrefCheckBox" name="cb_int_editor">
Those PrefWidgets needs to be registered into QtDesigner.
In short, You need to compile src/Tools/plugins/widget and register that library with Qt-designer in order to get the
FreeCAD-specific widgets in Qt-designer."
If you want to develop Qt stuff for FreeCAD, you’ll need the Qt Designer plugin that provides all custom widgets of FreeCAD.
Go to src/Tools/plugins/widget
So far we don’t provide a makefile – but calling qmake plugin.pro creates it. Once that’s done, calling make will
create the library libFreeCAD_widgets.so. To make this library known to Qt Designer you have to copy the file to
$QTDIR/plugin/designer
A practical example is found in forum How to save preferences or how to setup Qt Designer #include "moc_DlgSettingsFemImp.cpp"
set(FemGui_MOC_HDRS
...
TaskFemConstraintForce.h
...
fc_wrap_cpp(FemGui_MOC_SRCS ${FemGui_MOC_HDRS})
python script needs not such a compilation, in-situ parse the ui file by FreeCADGui.PySideUic.loadUi().
Solidworks provides not only FEM function, but also CFD function. see SolidWorks flow-simulation. It is desirable that
FreeCAD can have such a feature.
Instead of creating a new CFD or CAE module, I am trying to add CFD function to the the current Fem workbench and
reuse most of the infracture structure
see Appendix FreeCAD From Fem workbench towards a full-fledged CAE workbench
CFD simulation needs more complicate setup and dedicate mesh, thereby, in FreeCAD an engineering accurate simulation is
not the design aim. Import FreeCAD model into other pre-processing tools for meshing and tweak the experiment setup many
times is needed for serious study.
OpenFoam is not the only free open source CFD solver, but it is powerful, but free GUI case setup tool is missing (arguably).
It is not designed for windows, but usable via Cygwin : see FreeFoam. It is possible to add Cygwin to the PATH as
C:\cygwin\bin, then run the solver from command line. Furthermore, it can be run in container, or even the ubuntu on
windows subsystem as in Windows 10.
Requirement anslysis: see appendix FreeCAD combination the strength of FreeCAD and Salome
Free Solver selection: External solver, it is potential use solver of any license.
8.1.3 Roadmap
• Current limitation of : Fem is designed only for MechanicalAnalysis and Solver is tightly coupled with analysis object,
not pluggable design. JobControlTaskView should be reusable by CFD solver after some refactoring work.
• case writer is the primary task for function of CFD simulation
• FemMesh export into UNV format, but it does not export boundary condition.
• Only Solid mechancial materail is defined in Materail module, but no fluid material.
• BoundaryCondition for CFD is not defined, could be derived from Fem::Constraint
• View result back to FreeCAD is highly chanllenging task, thus external
95
96 CHAPTER 8. DEVELOPING CFD MODULE BASED ON FEMWORKBENCH
It is possible to extend function of DocumentObject and ViewProvider in python. The howto and limitation of developing
module in python has been discussed in prevous chapters.
example code for type checking in cpp
(obj->getTypeId().isDerivedFrom(Fem::FemSolverObject::getClassTypeId()))
analysis_obj.isDerivedFrom('')
TypeId is string repr; documentObj.Name is binary repr, Label is unicode string
Detailed documentation and excellent code are equally important to survival of open source project. Thereby, user can enjoy
the function smoothly and new developer can extend feature/maintain the code.
For a workbench developer, it is good to have a page on FreeCAD to introduce the feature, manual and progress.
First of all, write a good in-source documentation and detailed Readme.md in git repo.
Seconly, publish the workbench on FreeCAD wiki
1. apply for a wiki account via forum private message, see How to get wiki editing permissions
2. once approved, read before edit wiki pages
3. add an item to external workbenches section, not directly to user hub, which is for official workbench
4. add one unique page for the workbench.
In this section, developing workbench in python and extending c++ defined class is explained, for example, extending
FemSolverObject into solver specific Python classes. The
• Fluid material is supported via FemMaterial in Fem Module > MechanicalMaterial.py is Fem is refactered into a general
material object for CAE. Material properties are grouped into mechanical, fluidic, electromagnetic. see forum discussion
. CfdWorkbench
• FemMesh and Netgen and Gmsh meshing taskpanel can be imported directly into CFD workbench
• VTK pipeline for post-processing
• FemSolverObject: abstract DocumentObject for any CAE solver; concrete solver is developed in C++
• FemConstraintFluidBoundary: a single type to represent all boundary condition in CFD, pressure, velocity, turbulence
and thermal > see later section for design and implementation of FemConstraintFluidBoundary in C++
• VTK mesh import and export
• CFD result exported as VTK file format for building up VTK pipe
8.2. DESIGN OF CFD WORKBENCH 97
This is a pure python module/workbench. A template of empty workbench could be downloaded from Bernd’ git:
• load commands into workbench, which will load new python module as in cpp mode: src/Mod/Fem/Gui/AppFemGui.cpp
• add MenuItem and Toolbar items for this module
• Resoure (icon files)
• Translation
• example and testing
• cmake setting (module cmakelists.txt and a global option to activate CFD workbench)
As an addon module of pure Python, no module CMakeLists.txt is needed, just download this module folder ‘Cfd’
into ~/.FreeCAD/Mod/ or FreeCAD installation folder /Mod/
8.2.4 CfdAnalysis.py
• makeCfdAnalysis() create a FemAnalysis object into the current document, defined in CfdAnalysis.py
• no need to Extend Fem::FemAnalysisObject into CfdAnalysis class in python
• ViewProviderCfdAnalysis python class is necessary as double-click will activate CFD workbench
UNV to foam, mesh renumbering, thereby, a result mesh is needed to show Result
https://github.com/OpenFOAM/OpenFOAM-2.2.x/blob/master/applications/utilities/mesh/conversion/ideasUnvToFoam/
ideasUnvToFoam.C
This class and its derived, equal to FemTools.py family, hides solver specific implementation. Thereby, TaskPanelCfdSolver-
Control can be shared by any CFD solver. The Cfd runnable write solver input file, run the solving proces and finally load
the result back to FreeCAD.
This class extracts information from FreeCAD GUI for FoamCaseBuilder, e.g. mesh, material, solver settup and boundary,
while the actually case builder is done by FoamCaseBuilder
This is an independent python module, it will be developed in parallel with FreeCAD CFD workbench
• export UNV mesh with boundary conditions FaceSet
• case setup by setting boundary condition in workbench
• case build up from scrach by generating OpenFOAM case setting files
• case check or update case setup
• TestBuilder.py show a tutorial to build up a case in script once mesh file is ready
• CfdResult.py: This class only difined properties representing CFD result, pressure, velocity, temperature, etc.
It is extended from c++ class: FemResultObject, shared by any CFD solver.
• CfdResultFoamVTK.py: load result from OpenFOAM solver
98 CHAPTER 8. DEVELOPING CFD MODULE BASED ON FEMWORKBENCH
OpenFOAM result is exported in VTK legacy file, then read by python-vtk6 module to show as FemResultObject in FreeCAD.
Only scalers like pressure can be illustrated as different color in FemMesh nodes. Velocity vector will not be supported, but
FemPostPipeline is a promising solution.
This module will be reimplemented in c++ to save computation time, since CFD meshes are always huge.
• TaskPanelCfdResult.py: select scalar to be show as colormap on FemMesh, via modifying ViewProviderFemMesh
properties
The Solver class provide information for QProcess to start external solver. It is mainly designed for CFD for the moment,
but any solver like Fem, could use it. Another commandline property could be added, or built from current property,so
JobControlTaskPanel will be reused by renaming Calculix (QProcess Object) -> SolverProcessObject or like name. Although
ccx works perfect now, we are not locked to only one Fem solver.
Solver should be pluggable, swappable. Analysis is a pure containner (DocumentObjectGroup) to search for Mesh and Solver
Object, from my perspective. Currently, some properties are added into AnalysisObjects, but in Salome or Ansys workbench,
Solver is an object equal to Mesh. A lot of parameters, switches are needed to tweak solver, they are not belong to Analysis,but
solver specific.
Define a SolverObject can do persistence and replay of solver setup, and work without GUI. SolverObject can be subclass in
python to deal with specific solver.
#include <Mod/Fem/App/FemSolverObject.h>
DEF_STD_CMD_A(CmdFemCreateSolver);
...
• add cmd class into workbench
void CreateFemCommands(void){
{
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
...
rcCmdMgr.addCommand(new CmdFemCreateSolver());
• src/Mod/Fem/Gui/Workbench.cpp
Gui::ToolBarItem* Workbench::setupToolBars() const
{
Gui::ToolBarItem* root = StdWorkbench::setupToolBars();
Gui::ToolBarItem* fem = new Gui::ToolBarItem(root);
fem->setCommand("FEM");
...
<< "Fem_CreateSolver"
...
Gui::MenuItem* Workbench::setupMenuBar() const
{
Gui::MenuItem* root = StdWorkbench::setupMenuBar();
Gui::MenuItem* item = root->findItem("&Windows");
Gui::MenuItem* fem = new Gui::MenuItem;
root->insertItem(item, fem);
fem->setCommand("&FEM");
...
<< "Fem_CreateSolver"
• add new SVG icon file “fem-solver.svg” in Gui/Resource
• add “fem-solver.svg” file into Fem.qrc XML file http://doc.qt.io/qt-5/resources.html resource icon images are built into
bindary file FemGui.so or FemGui.dll. cmake has one line to rebuilt resources.
• add or update Translation This is temporally left behind, until the code is stable.
• git add <the aboved newly added file> If you forget to work in a branch, you can git stash branch testchanges
see https://git-scm.com/book/en/v1/Git-Tools-Stashing
Class Fem::ConstraintFluidBoundary should be derived from FemConstraint and adapted from some concrete class like
FemConstraintFixed, to reduce the work. As python has limitation, e.g. Coin3D scene, there must be coded in C++. The
closest class is FemConstraintForce, which is derived from FemConstraint, except no PythonFeature, but adding TaskPanel.
Modelled after CFX, a commercial CFD tool, boundary conditions are grouped into 5 categories, inlet, outlet, symmetry,
wall, openning (freestream/far field in other tools). BoundaryType Combobox is used to select from the categories. For each
categories, there is another combobox for Subtype, e.g. inlet and outlet has different valueType: pressure, flowrate, velocity,
etc. A task panel containing properties like: Value, Direction, Reversed, could be hidden if no value is needed for any
specific boundary subtype.
“Symmetry” should be named more generally as “interface”, which can be any special boundaries: wedge(axisymmetry),
empty(front and back face for 2D domain, single layer 3D mesh), coupled(FSI coupling interface), symmetry, interior (baffle),
processor(interface for domain decomposition), cyclic ( Enables two patches to be treated as if they are physically connected),
etc.
8.4. BOUNDARY CONDITION SETINGS FOR CFD 101
inlet {totalPressure, velocity, flowrate} outlet {pressure, velocity, inletOutlet} wall {fixed, moving, slip} freestream {freestream}
interface {empty, symmetry, cyclic, wedge}
Only uniform value boundary type is supported in GUI, user should edit the case file for OpenFOAM supported csv or
function object non-uniform boundary.
The turbulent inlet and thermal boundary condition is editable in the tab of boundary condition, which is accessed by tab in
boundary control panel
Other solver control, like gravity, reference pressure, is the internal field initialisation/body force for pressure and velocity.
• DocumentObject Fem::ConstraintFluidBoundary
• add file names into App/CMakeList.txt
• type initialisaton App/FemApp.cpp
• ViewProvider FemGui::ViewProviderConstraintFluidBoundary
• TaskPanel and ui “
• add file names Gui/CMakeList.txt
• type initialisaton Gui/FemGuiApp.cpp
• add svg icon file and update XML resource file Fem.qrc
• add menuItem in FemWorkbench <Gui/Command.cpp> and <Gui/Workbench.cpp>
8.4.4 ViewProviderConstraintFluidBoundary.h
(changed combobox type should trigger a redraw) Only outlet, will show arrow as FemConstrainForce, inlet has the arrow
but in reverse direction (flow into the geometry) Other boundary types will shows as FemConstrainFixed. However, simply
merging codes of two viewProviders into ViewProviderFemConstraintFluidBoundary.cpp does not work properly.
void ViewProviderFemConstraintFluidBoundary::updateData(const App::Property* prop) only update property
data, while actural drawing is done in base class method: ViewProviderFemConstraint::updateData(prop);
//change color to distinguish diff subtype
App::PropertyColor FaceColor;
// comment out *createCone* will make draw "interface" type and "freestream" type of fluid boundary
void ViewProviderFemConstraint::createFixed(SoSeparator* sep, const double height, const double width, const bo
{
createCone(sep, height-width/4, height-width/4);
createPlacement(sep, SbVec3f(0, -(height-width/4)/2-width/8 - (gap ? 1.0 : 0.1) * width/8, 0), SbRotation()
createCube(sep, width, width, width/4);
}
adding header and init function into src/Mod/Fem/Gui/AppFemGui.cpp This module is not designed to be extended in
python as other FemConstraint class, thereby only cpp type are declared.
#include "ViewProviderFemConstraintFluidBoundary.h"
...
PyMODINIT_FUNC initFemGui()
{
FemGui::ViewProviderFemConstraintFluidBoundary ::init();
8.4.5 TaskFemConstraintFluidBoundary
I use inkscape to make new svg icon for this class and add file name into src/Mod/Fem/Gui/Resources/Fem.qrc
8.5. EXAMPLE OF EXTENDING FEMSOLVEROBJECT IN PYTHON 103
CfdSolverFoam.py _FemSolverCalculix.py
• add dialog UI into update property of FemSolverObject
• design TaskPanelCfdSolverControl.ui dialog GUI form by QtDesigner
• add _TaskPanelCfdSolverControl python class
• add ViewProviderCfdSolverFoam python class
• Macro replay/ document import should work now.
update CMakeList.txt and resource
• add new files into in Gui/CMakeList.txt
• deprecated class _FemAnalysis _ViewProviderFemAnalysis (feature dropped)
• rename and refactoring of _JobControlTaskPanel.py (feature dropped)
• create new icons file
makeCfdSolverFoam is the magic connection between cpp class and Python class. It returns a document object derived
type “Fem::FemSolverObjectPython”, which is defined in c++ using FeatureT template. Extra properties can be added by
CfdSolverFoam(obj) constructor. Furthermore, ViewProvider can be extended by _ViewProviderCfdSolverFoam python
class.
def makeCfdSolverFoam(name="OpenFOAM"):
obj = FreeCAD.ActiveDocument.addObject("Fem::FemSolverObjectPython", name)
CfdSolverFoam(obj)
if FreeCAD.GuiUp:
from _ViewProviderCfdSolverFoam import _ViewProviderCfdSolverFoam
_ViewProviderCfdSolverFoam(obj.ViewObject)
return obj
CfdSolver is a generic class for any CFD solver, defining shared properties
class CfdSolver(object):
def __init__(self, obj):
self.Type = "CfdSolver"
self.Object = obj # keep a ref to the DocObj for nonGui usage
obj.Proxy = self # link between Fem::FemSolverObjectPython to this python object
# API: addProperty(self,type,name='',group='',doc='',attr=0,readonly=False,hidden=False)
obj.addProperty("App::PropertyEnumeration", "TurbulenceModel", "CFD",
"Laminar,KE,KW,LES,etc")
obj.TurbulenceModel = list(supported_turbulence_models)
obj.TurbulenceModel = "laminar"
OpenFOAM specific properties go into CfdSolverFoam
104 CHAPTER 8. DEVELOPING CFD MODULE BASED ON FEMWORKBENCH
class CfdSolverFoam(CfdSolver.CfdSolver):
def __init__(self, obj):
super(CfdSolverFoam, self).__init__(obj)
self.Type = "CfdSolverFoam"
**_ViewProviderCfdSolverFoam.py**
import FreeCAD
import FreeCADGui
import FemGui
class _ViewProviderCfdSolverFoam:
"""A View Provider for the Solver object, base class for all derived solver
derived solver should implement a specific TaskPanel and set up solver and override setEdit()
"""
def getIcon(self):
"""after load from FCStd file, self.icon does not exist, return constant path instead"""
return ":/icons/fem-solver.svg"
taskd = _TaskPanelCfdSolverControl(foamRunnable)
taskd.obj = vobj.Object
FreeCADGui.Control.showDialog(taskd)
return True
def __getstate__(self):
return None
=====================================================
https://github.com/qingfengxia/Cfd This workbench is designed to fit in more solvers. To solve CFD problem in other
solvers, you may reuse some of the code in CfdWorkbench, which is a split from FemWorkbench. FluidMaterial.py has several
implementation, currently needs a unification. FemConstraintFluidBoundary.cpp can be reused for common CFD boundary
types
• CfdSolverElmer.py
derived from CfdSolver which defined most of setting for CFD, include elmer specific settings, example are CfdSolver-
Foam.py
• CfdCaseWriterElmer.py
expose only a write_case() method. write_mesh (which should be similar as Bernd has done)and write boundary
condition
• CfdRunnableElmer.py
call the calculation and retrieve the result and display, CfdSolverControl task panel can be reused with tiny modification
• then add _CommandCfdSolverElmer.py _ViewProviderCfdSolverElmer.py, icon svg file can be adapted from *Foam.py
add into CfdWorkbench via InitGui.py
+CfdResultVTKElmer.py
load result . curently this piece of code (CfdResult and taskpanel) is under review, there is no need to
106 CHAPTER 8. DEVELOPING CFD MODULE BASED ON FEMWORKBENCH
Chapter 9
For python coding, a text editor with grammer highlight + QtDesigner is enough to code in the first case. QtDesigner can
be used to generate and edit Qt ui files. Spyder, which comes with Anaconda, is a good and lightweight Python IDE with
debugger support.
Various c++ IDE are available to support Cmake project. Visual Studio 2015 is essential for developement on Windows.
Cmake project can be mapped to a VS solution (*.sln) with a bundle of projects corresponding to
Latest QtCreator works with Qt 4.x and Qt 5.x; it also support CMake project.
• InkScape to generate SVG icon Great vector drawing programm. Adhers to the SVG standard and is used to draw
Icons and Pictures. Get it at http://www.inkscape.org
• Doxygen to generate doc A very good and stable tool to generate source documentation from the .h and .cpp files.
• Gimp to edit XPM icon file Not much to say about the Gnu Image Manipulation Program. Besides it can handle .xpm
files which is a very convenient way to handle Icons in QT Programms. XPM is basicly C-Code which can be compiled
into a programme. Get the GIMP here: http://www.gimp.org
107
108 CHAPTER 9. TESTING AND DEBUGGING MODULE
First of all, make sure you can build the official source once done git clone which confirms you can install all the lib
dependent.
compiler’s warning is the first place to spot error and potential bugs.
after changing an ui-file like this one (https://github.com/FreeCAD/FreeCAD/blob ... ces-ifc.ui) I have to run
Re: make clean after changing an *.ui file Postby wmayer » Thu Aug 06, 2015 4:46 pm
In this case cd into the Arch directory first before running "make clean" because then it only rebuilds this mo
Qt debug http://doc.qt.io/qt-4.8/debug.html
The console class This class manage all the stdio stuff. here is the generated document for src/Base/Console.h
9.3. STEP-BY-STEP DEBUGGING VIA GDB 109
This includes Messages, Warnings, Log entries and Errors. The incomming Messages are distributed with the
FCConsoleObserver. The FCConsole class itself makes no IO, it’s more like a manager. ConsoleSingleton is a
singleton! That means you can access the only instance of the class from every where in c++ by simply using:
#include <Base/Console.h>
//...
Base::Console().Log("Stage: %d",i);
src/Base/Tools.h
struct BaseExport Tools
{
/**
* @brief toStdString Convert a QString into a UTF-8 encoded std::string.
* @param s String to convert.
* @return A std::string encoded as UTF-8.
*/
static inline std::string toStdString(const QString& s)
{ QByteArray tmp = s.toUtf8(); return std::string(tmp.constData(), tmp.size()); }
/**
* @brief fromStdString Convert a std::string encoded as UTF-8 into a QString.
* @param s std::string, expected to be UTF-8 encoded.
* @return String represented as a QString.
*/
static inline QString fromStdString(const std::string & s)
{ return QString::fromUtf8(s.c_str(), s.size()); }
}
example usage of QString from std::string, #include <Base/Tools.h> Base::Tools::fromStdString()
• remember : modified python file will not take effect until FreeCAD is relauched
110 CHAPTER 9. TESTING AND DEBUGGING MODULE
print "Error Message" does not work in FreeCAD, neither PythonConsole in GUI mode, or terminal starting freecad
program (stdout can be viewed in ReportView, by activating this view). By changing the default preference, it is possible to
show print message from python module.
Read this first if you want to write code for FreeCAD Some guide lines for contribute code to FreeCAD Roadmap of FreeCAD:
search FreeCAD and roadmap to get the last
This is a bit terse, it may be worth of demonstration.
If you want to contribute new feature to FreeCAD, you should know if someone else has already done that or just in the
progress of implementing that.
“Get yourself known to the comminity” by post your ideal onto the forum and hear feedback from community.
• github cheatsheat This section will explain in details: How you can contribute to FreeCAD project
• git from the bottom up
• The 11 Rules of GitLab Flow link to Chinese translation of The 11 Rules of GitLab Flow
• github tuotirals
• google if you run into trouble
113
114 CHAPTER 10. CONTRIBUTE CODE TO FREECAD
Uisng merge GUI tool for 2-way merge git mergetool --tool=meld each time when there is a conflict. After solving the
conflict , git rebase --continue again.
git checkout A
git rebase B # rebase A on top of B
local is B,
remote is A
Instead of interacive mode, git rebase master will give you a list of conflicts. Graphical merge GUI tool can be used and
git rebase --continue
If you start three-pane merging tool (e.g. meld, kdiff3 and most of the others), you usually see LOCAL on the left (official
remote master), merged file in the middle and REMOTE (your dev branch) on the right pane. It is enough
for everyday usage. Edit only the merged file in the middle, otherwise, modification on the left and right will lead to
trouble/repeating manually merge conflict many times.
What you don’t see is teh BASE file (the common ancestor of $LOCAL and $REMOTE), how it looked like before it was
changed in any way.
advanced topic
Meld has a hidden 3-way merge feature activated by passing in the 4th parameter:
meld $LOCAL $BASE $REMOTE $MERGED The right and left panes are opened in read-only mode, so you can’t accidentally
merge the wrong way around. The middle pane shows the result of merge.For the conflicts it shows the base version so that
you can see all the important bits: original text in the middle, and conflicting modifications at both sides. Finally, when you
press the “Save” button, the $MERGED file is written - exactly as expected by git. The ~/.gitconfig file I use contains the
following settings:
[merge]
tool = mymeld
conflictstyle = diff3
[mergetool "mymeld"]
cmd = meld --diff $BASE $LOCAL --diff $BASE $REMOTE --diff $LOCAL $BASE $REMOTE --output $MERGED
this opens meld with 3 tabs, 1st and 2nd tab containing the simple diffs I’m trying to merge, and the 3rd tab, open by default,
shows the 3-way merge view.
1) $LOCAL=the file on the branch where you are merging; untouched by the merge process when shown to you
2) $REMOTE=the file on the branch from where you are merging; untouched by the merge process when shown to you
3) $BASE=the common ancestor of $LOCAL and $REMOTE, ie. the point where the two branches started diverting the
considered file; untouched by the merge process when shown to you
4) $MERGED=the partially merged file, with conflicts; this is the only file touched by the merge process and, actually,
never shown to you in meld
The middle pane show (BASE) initially and it turns/saved into (MERGED) as the result of merging. Make sure you move
your feature code (LOCAL) from left to the middle and move upstream updated code from the right pane (REMOTE)
http://stackoverflow.com/questions/11133290/git-merging-using-meld
http://lukas.zapletalovi.com/2012/09/three-way-git-merging-with-meld.html
After ignore the official master for half year, I found it is not possible to rebase my feature. It is ended up with kind of merge,
instead of smooth playing back my feature commit.
I start to split my feature into C++ section, which is more stable, into a clean branch for pull request. Instead of
http://meldmerge.org/features.html
116 CHAPTER 10. CONTRIBUTE CODE TO FREECAD
• line encoding
For upstream master, line endings is \r\n.
git config --global core.autocrlf input
# Configure Git on OS X or Linux to properly handle line endings
After test, git commit it and even git push to your repo. make a copy of the changed folder of the merged-with-upstream
feature branch.
git checkout master and copy the folder back.
git status will show all the changed files in feature branch.
git checkout -b feature_branch_clean will make a patch/diff of all feature change wihte upstream master. git commit it
after testing
git push origin feature_branch_clean and make a pull request online
Testing by macro or scripting
I taught myself a painful lession by attempting modifying many file before testing. Finally, I start again to refactoring single
file and pass the test.
Unit test would be recommended, feeding predefined data input to automate the testing.
GUI debugging is time-consuming. FreeCAD has the macro-recording function, which can be used to save time on GUI testing
by playing back macro.
FreeCAD is still under heavy development, testers are welcomed in every modules.
10.2.11 Procedure for user without a online forked repo (not tested ,not recommended)
As you don’t have push (write) access to an upstream master repository, then you can pull commits from that repository into
your own fork.
• Open Terminal (for Mac and Linux users) or the command prompt (for Windows users).
118 CHAPTER 10. CONTRIBUTE CODE TO FREECAD
It is recommended to submit small, atomic, manageable pull request to master, definitely after a full test.
After you push your commit to your fork/branch, you can compare your code with master. It is worth of coding style checking.
For python code, using flake, PEP8 etc. , cppcheck for C++ code.
Follow the standard github pull request routine, plus create a new post to describe the pull request, and wait for core
developers/collobrators to merge.
Spot out the bug: naming bug in Fem module: StanardHypotheses should be StandardHypotheses
1. find the bug and plan for bugfix
Assuming, current folder is Fem /opt/FreeCAD/src/Mod/Fem, find a string in all files in a folder, including subfolders: grep
-R 'StanardHypotheses' ./ output:
./App/FemMesh.cpp:void FemMesh::setStanardHypotheses()
./App/FemMesh.h: void setStanardHypotheses();
./App/FemMeshPyImp.cpp:PyObject* FemMeshPy::setStanardHypotheses(PyObject *args)
./App/FemMeshPyImp.cpp: getFemMeshPtr()->setStanardHypotheses();
./App/FemMeshPy.xml: <Methode Name="setStanardHypotheses">
If not, then use find, try this: find ./ -type f -exec grep -H 'yourstring' {} +
2. make the patch and test locally
pull from the most updated upstream master, then make a new branch and checkout this branch git checkout renamingFem
replace a string in all files in a folder, including subfolders
grep -rl StanardHypotheses ./ | xargs sed -i 's/StanardHypotheses/StandardHypotheses/g'
check the result of replacement: There should be no output: grep -R 'StanardHypotheses' ./ Then again: grep -R
'StandardHypotheses' ./, should match the file and lines number found in step 1
git add ./App/FemMesh.cpp
git add ./App/FemMesh.h
git add ./App/FemMeshPyImp.cpp
git add ./App/FemMeshPy.xml
git commit -m "correct spelling StanardHypotheses to StandardHypotheses"
Compile the source, make sure it can compile and function as expected. This function is not used in other module, so there is
no need for function test.
3. submit pull request to upstream master
10.3. CODE REVIEW 119
the push target is not official master, but developered github repo, see git remote -v git push origin renamingFem
On your project page of the github website, select this fork and creat pull request to official master.A good description of bug
and bugfix will make this pull request easier to be approciated.
Do it as quick as as possible, or this pull request will not be automatically merge with official master.
=================================================================
Phabricator looks really promising - there are tons of options, so I’ll be posting things that might be useful for us.
1. We should use “review” workflow (commit is reviewed before is included in the master branch). More here [1]
2. Phabricator can host git repository, can tract remote repo (that’s what is configured right now) and can use mirrors.
What we need is not clear for me yet.
3. We’d need at least virtual server to set it up - there are some tweaks in mysql/php required, so a normal cloud hosting
might not be enough.
4. The system right now runs on raspberry pi model 2 B (4 core, 1GB, 100Mb ethernet), and is connected over my home
broadband (240/20Mb), so any virtual server should be more than enough to run it.
5. Configuration of the system is “I’ll guide you by the hand” (Please set variable X = Y in file /etc/myslq/whatever) or
GUI driven. It’s easy.
6. It’s handy to have mail server configured (postfix), for notifications/password reset.
7. Setting up dashboard (page that users see as the main page) - it’s gui driven and very easy.
8. There are github integration options - I did not explore them yet.
121
122 CHAPTER 11. FREECAD CODING STYLE
type prefix for function parameter is not as useful as for member Variable,
• i: integer
• s: char const*, std::string
• p for pointer (and pp for pointer to pointer)
• pc: pointer of C++ class
• py: pointer of Python object
• _privateMember
for example: App::DocumentObject *pcFeat
It is more Coin3D style,except “So” namspace suffix is not used. In 2003, C++ compilers are not so powerful and standardised
to support even template and namespace in a cross-platform way. visual c++ was really bad to surport C++ standard for
some time.
• Namespace is enforced for each module, using “Export”
• class name (CamelClass) , Acronyms are camel-cased like ‘XmlWriter’
• private members:
• member function name (begins with lowerCase).
• no tab but 4 spaces indentation
PyCXX (Py::Object) should be used as possible, it may give better python2.x and python 3.x compability over the raw C
API in <Python.h>
return PyObject* and Py::Object has different impact
pep8 and pyflake to check coding style: sudo apt-get install python3-flake8 flake8 flake8 --ignore E265,E402,E501,E266
yourfile.py
https://github.com/google/yapf
Python IDE would suggest confliction with flake8 and avoid trailing spaces in c++ IDE
124 CHAPTER 11. FREECAD CODING STYLE
Gui::Application::Instance
Gui::MainWindow::getInstance();
Gui::getMainWindow();
Chapter 12
125
126 CHAPTER 12. CMAKE CHEAT SHEET
12.3.1 1.commands
• CMAKE_CXX_COMPILER
• CMAKE_LINK_LIBRARY_CFLAGS
platform like: WIN32, APPLE, UNIX (linux is UNIX but not APPLE), MYSYS, CYGWIN compilers: MSVC, MINGW,
iNTEL; CMAKE_COMPILER_IS_GNUCXX, CLANG
cmake --variable-list
cmake --command-list
cmake --module-list
cmake -h #show all generator
https://cmake.org/Wiki/CMake_Useful_Variables
cmake is a new language, why should we learn a new language instead of json or python (as in scons)?
2. multiple programming lang support and cross-platform with generators for majourity of IDE tools
7. tool to generate cmakelists.txt from scratch or convert from existent build system