FreeCAD源码分析:FreeCADApp模块
济南友泉软件有限公司
一、模块功能概述
FreeCADApp实现了整形、浮点型等基本类型的持久化支持,在此基础之上封装了大量的文档对象。基于属性与文档对象,FreeCADApp模块主要提供了文档对象管理、文档管理、参数管理等功能。
- 常用属性
App::Property提供了持久化属性的基类,FreeCAD提供了整形、浮点数等大量常用数据类型。
- 文档对象
文档对象是文档操作的对象,提供了GeoFeature、PartFeature、MeshFeature等大量常用文档对象。
- 文档对象管理
App::Document提供了文档对象创建、访问、修改、删除等接口。
- 文档管理
App::Application实现多文档的打开、切换、保存、关闭等功能。
- 参数管理
保存系统与用户定制的相关参数信息。
二、数据持久化
数据持久化是将数据存储到硬盘等持久性介质或者从硬盘等持久性介质读取数据到内存对象。Document类提供了基于属性的持久化服务。
2.1 信号-槽机制:一种观察者模式
signals2基于Boost里另一个库signals实现了线程安全的观察者模式,基于函数回调机制实现信号/槽的绑定和触发事件。在signals2中,观察者模式被称为信号/插槽(signals and slots),它是一种函数回调机制,一个信号关联了多个插槽,当信号发出时,所有关联它的插槽都会被调用。
许多成熟的软件系统都用到了这种信号、槽机制(另一个常用的名称是事件处理机制;event/event handler),它可以很好地解耦一组相互协作的类,有的语言甚至直接内建了对它的支持,signals2以库的形式为C++增加了这个重要的功能。
signals2库位于命名空间boost::signals2中,为了使用它,需要包含头文件<boost/signals2.hpp>。
信号(signal)可以理解成为事件,在boost里面,signal是一个模板类,它定义为
template<typename Signature,
typename Combiner = boost::signals2::optional_last_value<R>,
typename Group = int, typename GroupCompare = std::less<Group>,
typename SlotFunction = boost::function<Signature>,
typename ExtendedSlotFunction = boost::function<R(const connection &, T1, T2, ..., TN)>,
typename Mutex = boost::signals2::mutex>
class signal;
插槽(slot)可以是任意的可调用对象,包括函数指针、函数对象、以及它们的bind表达式和function对象,signal内部使用function作为容器来保存这些可调用对象。
通过boost::signals2::signal的成员函数connect() 与disconnect()可以管理signal与slot之间的链接关系。
示例:
#include <boost/signals2.hpp>
#include <iostream>
struct Hello
{
void operator()() const
{
std::cout << "Hello";
}
};
struct World
{
void operator()() const
{
std::cout << ", World!" << std::endl;
}
};
int main()
{
// Signal with no arguments and a void return value
boost::signals2::signal<void()> sig;
// Connect a Hello and World slot
sig.connect(Hello());
sig.connect(World());
// Call all of the slots
sig();
}
同时,App::Document预定义基本的信号
信号 |
描述 |
signalBeforeChange |
signal before changing an doc property |
signalChanged |
signal on changed doc property |
signalNewObject |
signal on new Object |
signalDeletedObject |
signal on deleted Object |
signalBeforeChangeObject |
signal before changing an Object |
signalChangedObject |
signal on changed Object |
signalRelabelObject |
signal on relabeled Object |
signalActivatedObject |
signal on activated Object |
signalTransactionAppend |
signal on created object |
signalTransactionRemove |
signal on removed object |
signalUndo |
signal on undo |
signalRedo |
signal on redo |
signalSaveDocument |
signal on load/save document. this signal is given when the document gets streamed. you can use this hook to write additional information in the file (like the Gui::Document does). |
signalRestoreDocument |
|
signalExportObjects |
|
signalExportViewObjects |
|
signalImportObjects; |
|
signalImportViewObjects |
|
signalStartSave |
signal starting a save action to a file |
signalFinishSave |
signal finishing a save action to a file |
signalRecomputed |
|
signalRecomputedObject |
|
signalOpenTransaction |
signal a new opened transactio |
signalCommitTransaction |
signal a committed transaction |
ignalAbortTransaction |
signal an aborted transaction |
2.2 属性数据
属性用于在文档中存储特性、界面输出等参数。Property类是整型、浮点型、枚举型、字符串等其他属性类的基类。
类名 |
描述 |
主要方法 |
PropertyInteger |
长整型 |
void setValue(long) long getValue(void) const |
PropertyPath |
文件路径 |
void setValue(const char *) void setValue(const boost::filesystem::path &) boost::filesystem::path getValue(void) const |
PropertyEnumeration |
枚举类型 |
void setEnums(const char** plEnums) void setValue(const char* value) void setValue(long) void setValue(const Enumeration &source) long getValue(void) const const char * getValueAsString(void) const; Enumeration getEnum(void) const; std::vector<std::string> getEnumVector(void) const; const char ** getEnums(void) const; |
PropertyIntegerConstraint |
整数区间 |
void setConstraints(const Constraints* sConstraint) const Constraints* getConstraints(void) const |
PropertyPercent |
整数区间[0,100] |
|
PropertyIntegerList |
整数列表 |
void setValue(long) long operator[] (const int idx) const void set1Value (const int idx, long value) void setValues (const std::vector<long>& values) const std::vector<long> &getValues(void) const |
PropertyIntegerSet |
整数集合 |
void setValue(long) void setValue(void) void addValue (long value) void setValues (const std::set<long>& values) const std::set<long> &getValues(void) const |
PropertyMap |
Key-Value映射表 |
void setValue(void) void setValue(const std::string& key,const std::string& value) void setValues(const std::map<std::string,std::string>&); const std::string& operator[] (const std::string& key) const void set1Value (const std::string& key, const std::string& value) const std::map<std::string,std::string> &getValues(void) const |
PropertyFloat |
双精度浮点数 |
void setValue(double lValue) double getValue(void) const |
PropertyFloatConstraint |
实数区间 |
void setConstraints(const Constraints* sConstrain) const Constraints* getConstraints(void) const; |
PropertyPrecision |
实数精度,默认1e-3 |
|
PropertyFloatList |
实数列表 |
void setValue(double); void setValue (void){} double operator[] (const int idx) const void set1Value (const int idx, double value) void setValues (const std::vector<double>& values); const std::vector<double> &getValues(void) const |
PropertyString |
字符串 |
void setValue(const char* sString); void setValue(const std::string &sString); const char* getValue(void) const; const std::string& getStrValue(void) const bool isEmpty(void) |
PropertyUUID |
通用唯一识别码UUID |
void setValue(const Base::Uuid &); void setValue(const char* sString); void setValue(const std::string &sString); const std::string& getValueStr(void) const; const Base::Uuid& getValue(void) const |
PropertyFont |
字体名称 |
|
PropertyStringList |
字符串列表 |
void setValue(const std::string&); void setValues(const std::vector<std::string>&); void setValues(const std::list<std::string>&); const std::string& operator[] (const int idx) const void set1Value (const int idx, const std::string& value) const std::vector<std::string> &getValues(void) const |
PropertyBool |
布尔值 |
void setValue(bool lValue) bool getValue(void) const |
PropertyBoolList |
布尔值列表 |
void setValue(bool) void set1Value (const int idx, bool value) void setValues (const boost::dynamic_bitset<>& values) const boost::dynamic_bitset<> &getValues(void) const |
PropertyColor |
颜色RGB |
void setValue(const Color &col); void setValue(float r, float g, float b, float a=0.0f); void setValue(uint32_t rgba); const Color &getValue(void) const; |
PropertyColorList |
颜色列表 |
void setValue(const Color&); const Color& operator[] (const int idx) const void set1Value (const int idx, const Color& value) void setValues (const std::vector<Color>& values) const std::vector<Color> &getValues(void) const |
PropertyMaterial |
材质 |
void setValue(const Material &mat) void setAmbientColor(const Color& col) void setDiffuseColor(const Color& col) void setSpecularColor(const Color& col) void setEmissiveColor(const Color& col) void setShininess(float) void setTransparency(float) const Material &getValue(void) const |
PropertyMaterialList |
材质列表 |
void setValue(const Material&) const Material& operator[] (const int idx) const void set1Value(const int idx, const Material& value) void setValues(const std::vector<Material>& values) const std::vector<Material> &getValues(void) const |
PropertyContainer是属性容器,所有包含属性的类的基类都要继承自PropertyContainer,可以通过提供的宏来简化属性容器的定义。
示例:
//Annotation.h
class AppExport Annotation : public DocumentObject
{
PROPERTY_HEADER(App::Annotation);
public:
/// Constructor
Annotation(void);
virtual ~Annotation();
App::PropertyStringList LabelText;
App::PropertyVector Position;
/// returns the type name of the ViewProvider
const char* getViewProviderName(void) const {
return "Gui::ViewProviderAnnotation";
}
};
//Annotation.cpp
PROPERTY_SOURCE(App::Annotation, App::DocumentObject)
Annotation::Annotation()
{
ADD_PROPERTY(LabelText ,(""));
ADD_PROPERTY(Position,(Base::Vector3d()));
}
Annotation::~Annotation()
{
}
PropertyContainer内部实际上使用PropertyData类型对象存储了属性的描述信息(元数据),
static PropertyContainer::PropertyData::propertyData
PropertyData内部包含了一个PropertyData::PropertySpec类型的数组,用于描述所有的属性描述信息(元数据),主要包括名称、组、文档描述、位置、类型等。
struct PropertySpec
{
const char* Name;
const char * Group;
const char * Docu;
short Offset,Type;
};
为了方便计算PropertySpec中的offset字段,提供了PropertyData::OffsetBase类。
2.3 文档对象管理
文档对象(也称作Feature)是文档操作操作的对象,通常文档对象显示在FreeCAD treeview。关闭文档的时候,会保存文档对象;打开文档的时候,会恢复文档对象。
App::DocumentObject是所有文档对象的基类,定义了文档对象的标签、可见性等基本属性。
属性名称 |
类型 |
描述 |
Expression Engine |
PropertyExpressionEngin |
a list of expressions. |
Label |
PropertyString |
the user editable name of this object, it is an arbitrary UTF8 string. By default, it is the same as the Name |
常用的文档对象主要包括:
文档对象派生类 |
描述 |
FeaturePython |
an empty object that can be used for different purposes, depending on the added properties. |
GeoFeature |
the base object of all geometrical objects, that is, of objects that have a Placement property that defines their position in the 3D view. |
PartFeature |
derived from App GeoFeature, and the parent class of objects with 2D and 3D topological shapes. |
MeshFeature |
derived from App GeoFeature, and the parent class of objects with 2D and 3D meshes. |
2.3.1 创建文档对象
根据文档对象类型创建文档对象。
DocumentObject *Document::addObject(const char* sType, const char* pObjectName = 0, bool isNew = true);
2.3.2 访问文档对象
根据文档对象名称获取指定的文档对象。
DocumentObject * Document::getObject(const char *Name) const;
根据指定类型的文档对象列表。
template<typename T>
inline std::vector<T*> Document::getObjectsOfType() const
2.3.3 删除文档对象
void Document::removeObject(const char* sName);
2.4 状态管理
App::Document实现了基于文档对象的touch、更新等状态维护。
void purgeTouched() |
Remove all modifications. After this call The document becomes Valid again. |
bool isTouched(void) const |
check if there is any touched object in this document |
std::vector<App::DocumentObject *> getTouched(void) const |
returns all touched objects |
void setClosable(bool) |
set the document to be closable, this is on by default. |
bool isClosable() const |
check whether the document can be closed |
int recompute() |
Recompute all touched features and return the number of recalculated features |
void recomputeFeature(DocumentObject* Feat) |
Recompute only one feature |
2.5 事务管理
事务是一些列操作序列的几何。App::Document提供了基于事务的Undo、Redo功能;
void openTransaction(const char* name=0) |
Open a new command Undo/Redo, an UTF-8 name can be specified |
void commitTransaction() |
Commit the Command transaction. Do nothing If there is no Command transaction open. |
void abortTransaction() |
Abort the actually running transaction. |
bool isTransactionEmpty() const |
Check if a transaction is open and its list is empty. |
void setUndoLimit(unsigned int UndoMemSize=0) |
Set the Undo limit in Byte! |
void setMaxUndoStackSize(unsigned int UndoMaxStackSize=20) |
Set the Undo limit as stack size |
void clearUndos() |
Remove all stored Undos and Redos |
std::vector<std::string> getAvailableUndoNames() const |
Returns a list of the Undo names |
bool undo() |
Will UNDO one step, returns False if no undo was done (Undos == 0). |
int getAvailableRedos() const |
Returns the number of stored Redos. If greater than 0 Redo will be effective. |
std::vector<std::string> getAvailableRedoNames() const |
Returns a list of the Redo names. |
bool redo() |
Will REDO one step, returns False if no redo was done (Redos == 0). |
三、应用程序类
Application提供了多文档应用程序开发的框架,支持文档操作、参数管理、模块管理、软件启动等相关服务。同时,Application提供了基于boost::signals2的信号-槽机制。存在全局唯一的一个Application对象。
3.1 文档管理
文档操作包括新建、打开、关闭等操作,同时提供了活动文档的切换操作。
- 新建文档
App::Document* App::Application::newDocument(const char * Name=0l, const char * UserName=0l)
第一个参数是一个标识符,可以通过getUniqueDocumentName函数生成一个不重复的标识符,这个标识符适用于内部唯一标识一个文档对象;第二个参数是用于显示文档名字的字符串。
- 打开文档
App::Document* App::Application::openDocument(const char * FileName=0l)
用于打开指定的XML格式文档。如果文档存在,直接返回文档对象;如果不存在,则调用newDocument函数创建文档对象。
- 活动文档
活动文档是当前用户正在操作的文件数据,可以根据需要切换活动文档。
void App::Application::setActiveDocument(App::Document* pDoc);
void App::Application::setActiveDocument(const char *Name);
- 关闭文档
文档修改完毕,可以关闭文档以将修改同步更新到磁盘文件。
bool App::Application::closeDocument(const char* name);
void App::Application::closeAllDocuments(void);
3.2 参数管理
App::Application使用mConfig保存基本的软件信息,包括程序名称、用户参数目录、系统参数目录等。
std::map<std::string,std::string> Application::mConfig
App::Application使用一系列ParameterManager类型对象来管理软件参数,
std::map<std::string,ParameterManager *> mpcPramManager;
可以通过ParameterManager不仅可以加载、导出DOM格式的参数文件,而且提供多种接口以方便的获取整形、浮点型、布尔类型的参数值。mpcPramManager中有两个比较重要的参数管理器,即_pcSysParamMngr与_pcUserParamMngr,
ParameterManager *App::Application::_pcSysParamMngr;
ParameterManager *App::Application::_pcUserParamMngr;
在Application::LoadParameters()中,_pcUserParamMngr 会加载mConfig[“UserParameter”] 指定的文件(mConfig[“UserAppData”]\\user.cfg)文件来完成对;_pcSysParamMngr会加载mConfig[“SystemParameter”](默认是mConfig[“UserAppData”]\\system.cfg)
3.3 目录管理
针对不同的平台,App::Application将FreeCAD系统信息以及用户配置信息存储到不同的目录下。
描述 |
|
const char* getHomePath(void) const |
void Application::initConfig(int argc, char ** argv)调用时根据argv[0]设置. 保存在App::Application_mConfig[“AppHomePath”] |
static std::string getTempPath() |
通过GetTempPath()返回,取值为
|
static std::string getTempFileName(const char* FileName=0) |
根据FileName在TempPath中创建临时文件 |
static std::string getUserAppDataDir() |
通过SHGetFolderPath获取CSIDL_APPDATA,即 C:\\Documents and Settings\\username\\Application Data
环境变量”FREECAD_USER_DATA” |
static std::string getUserMacroDir() |
mConfig[“UserAppData”] + “Macro/” |
static std::string getResourceDir() |
mConfig[“AppHomePath”] + “data/” |
static std::string getHelpDir() |
mConfig[“AppHomePath”] + “doc/” |
3.4 模块管理
略。(后续模块开发时讲解)
3.5 软件启动
FreeCAD具有GUI、Console等运行两种模式。通过设置“Console”/“RunMode”可以指定FreeCAD的运行模式。
名称 | 取值范围 | 默认值 |
Console |
0 : 不启动控制台 1 : 启动控制台 |
0 |
RunMode |
Gui : 界面模式 Internal : |
Gui |
// Init phase ===========================================================
// sets the default run mode for FC, starts with gui if not overridden in InitConfig...
App::Application::Config()["RunMode"] = "Gui";
App::Application::Config()["Console"] = "0";
如果“Console”为“1”,则FreeCAD将启动控制台。
如果“RunMode”为“Gui”或者“Internal”,将会调用FreeCADGui模块以界面模式启动FreeCAD;否则将会调用FreeCADMainCmd以控制台模式启动FreeCAD。
// if console option is set then run in cmd mode
if (App::Application::Config()["Console"] == "1")
App::Application::runApplication();
if (App::Application::Config()["RunMode"] == "Gui" ||
App::Application::Config()["RunMode"] == "Internal")
Gui::Application::runApplication();
else
App::Application::runApplication();
暂无评论内容