FreeCAD源码初步了解

FreeCAD简介

FreeCAD是基于OpenCASCADE的开源CAD/CAE软件,完全开源(GPL的LGPL许可证),官方源码地址,详情可参考维基百科,百度百科等等。

如果要编译FreeCAD,最好采用官方提供的LibPack,否则第三方库难以找全。FreeCAD LibPack官方看起来使用VS2008以及VS2013,这二个版本最常用。github上可以下载,但以前的sourceforge地址还可以找到vs2010以及vs2012的,可能没有那么齐全或者新。


准备开始

我以前的编译删除了,当初专门安装了VS2013,不过也没怎么好好看,而现在已经卸载了;考虑到只有VS2012 x64版的FreeCAD LibPack,不想大动干戈安装VS2013,所以还是使用VS2012 x64版编译。不知道官方什么时候能够出个 VS2015/VS2017 的LibPack,事实上只需要VS2015就够了,因为vs2015/vs2017在二进制上可以兼容。

解压FreeCAD-0.16.6712.zip至文件夹,再解压FreeCADLibs_11.0_x64_VC11.7z至同级目录,以前者为cmake的source目录,后者为cmake的binary目录。

FreeCADLibs_11.0_x64_VC11.7z 缺少boost 1.55版的boost python库,此为必要库,无法忽略,下载boost 1.55版的x64预编译包,拷贝出其中boost python的lib与dll文件分别放至解压目录之下的lib/dll目录。没有这一步操作,cmake会在src/Mod/Path/libarea里的CMakeLists.txt查找boost_python失败。

在FreeCAD-0.16.6712源码里,打开文件夹下的CMakeLists.txt,修改第125行处的 set(FREECAD_LIBPACK_DIR ${CMAKE_SOURCE_DIR} CACHE PATH "Directory of the FreeCAD LibPack"),其中的${CMAKE_SOURCE_DIR}替换成为${CMAKE_BINARY_DIR},调用CMake构建工程,默认不做别的修改,生成vs2012 x64的项目工程文件。

编译时,在 src/App/Document.cpp 内会在 tie(edge, inserted) 处由于 tie 的调用不明确导致失败,可以明确在添加调用 boost::tie(edge, inserted),该文件内总计有二个位置需要修改。

为什么不使用最新的源码以及LibPack,因为官方不提供vs2015,vs2017版本的,完全自己补充所有第三方库,会有很大的工作量。

第三方库粗略记录

FreeCADLibs_10.0_x64_VC11.7z 里在include目录是按第三方库名称各自放置的,比较明晰,但是发现后面版本的FreeCAD LibPack都不再是这样的了。

没列举齐全的第三方库表格如下:

Lib Name version in LibPack_11.0_x64_VC11 Link to get it
Python Python 2.7.8 http://www.python.org/
PySide 1.2.2 http://wiki.qt.io/PySide
shiboken 1.2.2 http://shiboken.readthedocs.io/en/latest/
Qt Qt 4 https://www.qt.io/
Boost 1.55.0 http://www.boost.org/
Coin3D 4.0 https://bitbucket.org/Coin3D/coin/wiki/Home
SoQt 1.2 https://bitbucket.org/Coin3D/soqt
OpenCASCADE oce-16(对应6.7.1) http://www.opencascade.com

在以上第三方库中,PySide是Python与Qt的融合,可以使用Python语言构建Gui。建议是在Python2.7系列中使用,支持部分Python3,与PySide有相同功能的PyQt相对成熟一些,但商用需要付费,估计这是FreeCAD采用PySide的原因,PySide是Qt官方出的,完成了对Qt4.8版本的完整实现,支持Qt5的PySide2也可以在github中找到。shiboken作何用途,我还没弄得很明白。

FreeCAD对Python是重度依赖,不能缺失。如果自己来搞第三方库,就必须先搞定Python,PySide,Boost,shiboken,所以使用官方提供的LibPack是可以节省不少麻烦的。


工程项目的构成

认为在FreeCAD_trunk之下的项目可以分为三类:基础工程项目框架(仅包括FreeCAD打头的7个工程);模块项目(比如Start/StartGui、Part/PartGui等,位于src/Mod文件夹之下);辅助依赖类(比如area等)。此分类或有遗漏,不过最重要的应该是前面二个分类的工程项目。

基础框架工程项目

工程项目 产生文件 备注
FreeCADMain FreeCAD.exe FreeCAD主执行启动文件,main()函数所在地
FreeCADMainPy FreeCAD.pyd 兼容Python的扩展dll,导出initFreeCAD()
FreeCADMainCmd FreeCADCmd.exe App::Application::Config()["RunMode"]有三种模式,Gui、Exit、Console,这里对应的是Exit模式,执行(argc, argv)之后自动退出
FreeCADGui FreeCADGui.dll 命名空间Gui,Command,Workbench,View3dInventor(视图),Gui层的Document
FreeCADGuiPy FreeCADGui.pyd 兼容Python的扩展dll
FreeCADBase FreeCADBase.dll 代码基础与上层,命名空间Base,Type类型体系,InterpreterSingleton脚本解释器
FreeCADApp FreeCADApp.dll 表征exe执行所在的应用,命名空间App,App::GetApplication()可以获取唯一的那个pcSingleton指针,App层Document

以上这些就建构了FreeCAD运行的基础框架,它们实际上仅使用了Python, Boost, PySide, shiboken, xerces-c, zlib, coin3d等这些,其余的Mod都是模块扩展,依赖诸如OCE,PCL,Eigen3,libqhull等等,每个Mod工程项目分为带Gui的及不带Gui的版本。带Gui的项目定义了可用的Command,Workbench(工作台),显示等这些。

当前FreeCAD所持有的模块

  • Start, StartGui
  • Part, PartGui
  • Points, PointsGui
  • Sketcher, SketcherGui
  • PartDesign, PartDesignGui
  • ......


FreeCAD启动流程

启动模式

FreeCAD 有三种启动方式,Gui模式,Exit控制台,Python脚本模式;分别对应的起始工程项目是 FreeCADMain, FreeCADMainCmd, FreeCADMainPy。

FreeCAD对Python的扩展

在 FreeCADApp 项目内的 App::Application 的构造函数里,可以看到 PyObject* pAppModule = Py_InitModule3("FreeCAD", Application::Methods, FreeCAD_doc); 语句,这应该是定义一个Python的扩展模块,名称为FreeCAD,扩展的成员函数在静态成员数据Mehods数组内。

在 FreeCADGui 项目内的 Gui::Application 的构造函数里,可以看到 PyObject* module = Py_InitModule3("FreeCADGui", Application::Methods, "The functions in the FreeCADGui module ....."); 语句,定义一个名称为 FreeCADGui 的Python扩展模块。

而在 FreeCADBase 项目内有 Base::Interpreter() 函数返回 InterpreterSingleton::Instance() ; 它的init(int argc, char* argv[])成员函数,在初始化Python,嵌入解释器。

在 FreeCADInit.py脚本里,会简化设定 App = FreeCAD, Gui = FreeCADGui。

本质上FreeCAD是运行在Python解释器上的,也就是说它是通过扩展Python,然后所有的命令(Command类,仅在Gui模式下会有)都是通过执行Python的语句来实现的。所以FreeCAD可以没有Gui界面执行,但是不能没有python,本身内部嵌入了python的解释器。

例举一个Box的创建命令是这样的:

C++
void CmdPartBox::activated(int iMsg)
{
    QString cmd;
    cmd = qApp->translate("CmdPartBox","Cube");
    openCommand((const char*)cmd.toUtf8());

    doCommand(Doc,"App.ActiveDocument.addObject(\"Part::Box\",\"Box\")");
    cmd = QString::fromLatin1("App.ActiveDocument.ActiveObject.Label = \"%1\"")
        .arg(qApp->translate("CmdPartBox","Cube"));
    doCommand(Doc,(const char*)cmd.toUtf8());
    commitCommand();
    updateActive();
    doCommand(Gui, "Gui.SendMsgToActiveView(\"ViewFit\")");
}

里边这些都是发送给解释器的Python语句,执行后会在界面上的Python Console窗口播报Python执行语句,这样录制宏就简单极了,此外也可以全部采用Python来建模或者写个功能实现,比如就有个SheetMetal就完全是用Python来实现。

启动代码

需要注意的是 FreeCADApp 内有 App::Application, FreeCADGui 内有 Gui::Application。

启动流程就是 App::Application::init(argc, argv); 如果是 Exit模式,则执行 App::Application::runApplication() 估计是处理了一下命令参数完毕之后就退出了。而如果是 Gui模式,则还要 Gui::Application::initApplication,再有 Gui::Application::runApplication。

与Qt的经典启动模式相比,这些代码都位于 Gui::Application::runApplication 内。GUISingleApplication mainApp; 对应 QApplication;而 Gui::MainWindow mv; 对应 QMainWindow。

因为可以没有Gui界面启动,所以文档也分为 App::Document, Gui::Document,具体的内容存储全在 App::Document 内。

Mod下的模块启动

  • 在 App::Application::initApplication() 里会执行名为 FreeCADInit.py 的脚本;该脚本会遍历 Mod 下所有目录,执行 Init.py
  • 在 Gui::Application::runApplication() 里会执行名为 FreeCADGuiInit.py 的脚本;此脚本会遍历 Mod 下所有目录,执行 InitGui.py

界面与命令描述

在 Gui 内有个 CommandManager,所有的命令Command都往这里边加。Command命令的目标是产生NewObject加入Document,特征附有属性,然后recompute()执行计算,这样就可以重生成,响应修改属性的结果。

它的QAction的变灰动作是通过在 MainWindow 里定义一个计时器,调用 updateActions() 函数,遍历每一个Command,调用testActivate(); 以设定QAction是否可用。

Workbench 设计得不错,成员函数 activate() 会做setupToolBars(), setupDockWindows(), setupMenuBar()等设定菜单,工具栏,停靠窗口 (注意:此函数非虚函数,不能被重载)。觉得不好的地方是定义得太细了,得经常切换Workbench或者使用Complete Workbench来得到所有的可用命令。


使用感觉

觉得不习惯的地方,FreeCAD有点反操作,比如倒圆角,你必须首先选择了一条边缘,然后才能去倒圆角。(指的是PartDesign下面的Fillet(只接受一条边缘),Part下边也有Fillet(估计可以多选))。

posted @ 2019-03-29 18:00 bitbybit3d 阅读(...) 评论(...) 编辑 收藏