乐于分享
好东西不私藏

FreeCAD的App模块架构分析

FreeCAD的App模块架构分析

App 模块是 FreeCAD 的 核心业务逻辑层 ,构建在 Base 层之上,定义了文档、属性、特征、表达式、事务等核心概念,是整个 FreeCAD 的"心脏"。App 模块约有 120+ 个源文件,是整个 FreeCAD 的灵魂所在,涵盖了 文档生命周期、属性系统、表达式引擎、事务/撤销、扩展机制、链接系统 等所有核心业务抽象。Base 层 : 提供"语言"——类型系统、Python绑定、序列化原语、设计模式基础;App 层 : 使用"语言"构建"产物"——文档、属性、特征、表达式、事务等业务概念

架构总览

类继承层次图

核心子系统详解

1. Application — 应用单例

  • 全局访问 : App::GetApplication()
  • 文档管理 : newDocument() / getDocument() / closeDocument()
  • 参数管理 : GetUserParameter() / GetSystemParameter() → 管理 user.cfg / system.cfg
  • 路径管理 : 通过 ApplicationDirectories 管理资源、宏、插件等路径
  • 链接解析 : getLinksTo() / getLinkedObject() 管理对象间的引用关系
  • 信号系统 : 文档打开/关闭、对象变更等全局信号
  • 动态加载 : 按需加载 .pyd 模块文件

2. Document — 文档容器

  • 继承: PropertyContainer — 文档自身也有属性(如 Comment、Company、Author、License)
  • 对象管理: addObject() / removeObject() / getObject()
  • 事务栈: 维护 Undo/Redo 事务历史
  • 依赖图: 维护对象间的依赖关系,驱动重计算排序
  • 重计算引擎: recompute() 按拓扑序重新执行所有标记为 Touched 的对象
  • 状态管理: 30+ 种状态标志位(Closable, Restoring, Recomputing, ...)
  • XML序列化: 通过 Save / Restore 将整个文档写入 .FCStd 文件

3. Property 属性系统

FreeCAD 最核心的抽象之一,所有可持久化数据都通过属性系统管理:

每个属性有 32 位的 StatusBits 控制行为:

  • Touched — 是否需要重计算
  • Immutable — 是否不可变
  • ReadOnly / Hidden — 编辑器中只读/隐藏
  • Transient — 不保存到文件
  • Output — 输出属性(不触发父对象重计算)
  • NoRecompute — 变更不触发重计算
  • CopyOnChange — 变更时复制链接对象

4. DocumentObject — 文档对象 (★最核心的类)

class DocumentObject: public TransactionalObject

{

PropertyString Label; // 用户可见标签

PropertyString Label2; // 描述

PropertyExpressionEngine ExpressionEngine; // 表达式引擎

PropertyBool Visibility; // 可见性

// 信号

signal<void(...)> signalBeforeChange; // 属性即将变更

signal<void(...)> signalChanged; // 属性已变更

signal<void(...)> signalEarlyChanged; // 早期变更通知

// 核心方法

virtual DocumentObjectExecReturn* recompute(); // 重计算

virtual DocumentObjectExecReturn* execute(); // 执行

virtual PyObject* getPyObject(); // Python绑定

virtual const char* getViewProviderName(); // 关联ViewProvider跨层桥接

};

关键状态位 ( ObjectStatus ):

5. 事务系统 (Transaction)

  • Transaction : 原子事务记录,包含对象的增/删/属性变更
  • Undo/Redo : 通过 apply(doc, forward=true/false) 实现撤销/重做
  • 跨文档事务 : 支持同一事务ID关联多个文档的操作,实现联合撤销
  • AutoTransaction : RAII风格的自动事务,函数返回时自动提交
  • 事务类型: addObjectNew , addObjectDel , addObjectChange , addOrRemoveProperty , renameProperty

6. 表达式系统 (Expression)

基于 LALR 解析器的表达式引擎,支持:

  • 算术运算 : + , - , * , / , ^ , %
  • 比较运算 : == , != , < , > , <= , >=
  • 逻辑运算 : && , || , !
  • 条件表达式 : test ? true_value : false_value
  • 函数调用 : sin() , cos() , sqrt() , min() , max() , round() , abs() 等
  • 属性引用 : Box.Length , Spreadsheet.A1 等跨对象引用
  • 范围 : start:end:step 语法
  • 单位支持 : 表达式结果自动处理单位转换

7. 扩展系统 (Extension)

FreeCAD 用 Extension 模式 代替多重继承:

一个对象可以注册多个扩展,例如 DocumentObjectGroup = DocumentObject + GroupExtension 。

8. 链接系统 (Link)

  • 26+ 种 PropertyLink 变体 :

PropertyLink — 单个对象引用

PropertyLinkSub — 引用子元素(如面、边)

PropertyXLink — 跨文档链接

PropertyLinkList — 对象列表引用

PropertyLinkSubList — 子元素列表引用

  • Link 对象 : App::Link — 创建对象的实例/引用副本
  • LinkGroup : App::LinkGroup — 管理一组链接实例
  • LinkElement : App::LinkElement — 引用单个子元素的链接
  • 链接解析 : 通过 getLinkedObject(true) 递归解析到最终目标

依赖关系

数据流 / 重计算流程

核心设计原则

1. 属性即一切 : 所有持久化数据必须通过 Property 系统,确保类型安全、序列化、信号通知

2. Extension 替代多重继承 : 通过 ExtensionContainer + Extension 实现灵活的行为组合

3. 不可变 API : 属性变更通过 GIL + 事务 + 重计算引擎保证一致性

4. 依赖驱动 : 对象间通过链接属性建立依赖,自动推导重计算顺序

5. App/Gui 分离 : App 层完全无 GUI 依赖,通过 getViewProviderName() 实现弱引用

6. FeaturePython : 通过 FeaturePythonImp 将 Python 方法注入 C++ 对象,支持宏录制