博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
FreeCAD源码分析:FreeCADGui模块
阅读量:742 次
发布时间:2019-03-22

本文共 45579 字,大约阅读时间需要 151 分钟。

FreeCAD源码分析:FreeCADGui模块

济南友泉软件有限公司

FreeCADGui项目实现了界面操作、模型显示与交互等相关功能,项目构建生成FreeCAD(_d).dll动态链接库。

FreeCADGuiPy项目在FreeCADGui基础之上,构建生成FreeCADGui(_d).pyd,实现对Python中“import FreeCADGui”语句的支持。

FreeCADGuiPy比较简单,因此,本文主要针对FreeCADGui进行分析。

一、模块功能概述

FreeCADGui模块基于文档-视图架构实现了多文档CAD软件开发的框架。不仅提供了基于Workbench的界面管理,而且提供了大量的用于完成数据对象渲染的视图。主要功能包括:

  • Workbench管理

Workbench实际上针对特定应用的工具及其界面显示。FreeCAD提供了拓展性较强的基于Workbench的软件开发思路,可以通过Workbench定义软件的外观及其具体的功能。

  • 停靠窗口管理

停靠窗口实际上是窗口部件的容器,可以将内嵌的窗口部件显示在主窗口的四周,也可以悬浮在桌面。

  • 命令管理

Workbench由根据具体问题定义的命令工具集来实现,FreeCAD提供了CommandMnanger用于完成对Command及其子类的管理。

  • 菜单管理

菜单(包括工具按钮)实际上是不分命令集的界面表现,通过关联Qmenu与QAction将全部或者部分命令集显示出来,同时建立界面操作与命令响应之间的关联。

  • 视图显示

视图用于显示文档对象数据,同时可以将用户界面输入转换成数据对象操作。基于BaseView、MDIView及其子类,实现了文档-视图架构,可用于在界面窗口中显示数据对象。

  • 属性系统

基于Qt Model-View架构,实现数据对象显示、编辑与Qt部件的关联。

 

二、Gui::Appication

Gui::Application提供了多文档应用程序开发的框架,支持文档操作、模块管理、软件启动等相关服务,其内部ApplicationP类型对象维护了文档列表、视图列表、命令管理器、宏管理器等相关数据对象。

struct ApplicationP{    ApplicationP() :    activeDocument(0L),    isClosing(false),    startingUp(true)    {        // create the macro manager        macroMngr = new MacroManager();    }    ~ApplicationP()    {        delete macroMngr;    }    /// list of all handled documents    std::map
documents; /// Active document Gui::Document* activeDocument; MacroManager* macroMngr; /// List of all registered views std::list
passive; bool isClosing; bool startingUp; /// Handles all commands CommandManager commandManager;};

存在全局唯一的一个Application对象,

static Application* Application::Instance;

FreeCAD启动时,会通过Application定义的若干静态成员函数完成配置加载、初始化、模块加载、创建界面窗口等工作。

static void Application::initApplication(void);static void Application::initTypes(void);static void Application::initOpenInventor(void);static void Application::runInitGuiScript(void);static void Application::runApplication(void);

三、主窗口

Gui::MainWindow是FreeCAD在QMainWindow的基础之上提供的主窗口,该类提供了子窗口管理、弹出菜单、事件处理等功能。

存在全局唯一的Gui::MainWindow对象instance,

static MainWindow* MainWindow::instance;static MainWindow* MainWindow::getInstance();

Gui::MainWindow属性存储在MainWindowP类型的结构体中,

struct MainWindow::MainWindowP* d;struct MainWindowP{    QLabel* sizeLabel;    QLabel* actionLabel;    QTimer* actionTimer;    QTimer* activityTimer;    QTimer* visibleTimer;    QMdiArea* mdiArea;    QPointer
activeView; QSignalMapper* windowMapper; QSplashScreen* splashscreen; StatusBarObserver* status; bool whatsthis; QString whatstext; Assistant* assistant; QMap
> urlHandler;};

       

变量名

类型

描述

sizeLabel

QLabel*

显示当前视图的几何维度

actionLabel

QLabel*

显示操作信息

actionTimer

QTimer*

在状态栏显示操作信息5秒

activityTimer

QTimer*

每300毫秒触发一次,更新界面命令的显示

visibleTimer

QTimer*

主窗口MainWindow显示定时器

mdiArea

QMdiArea*

多文档窗口显示的区域

activeView

QPointer<MDIView>

同一时刻,最多只有一个视图作为当前视图并处于激活状态

windowMapper

QSignalMapper*

信号映射器,

splashscreen

QSplashScreen*

FreeCAD启动时的欢迎界面

status

StatusBarObserver*

监控控制台输出,通过CustomMessageEvent类型事件将消息(黑色)、警告(黄色)、错误(红色)输出到状态栏。

whatsthis

bool

是否显示帮助提示信息(What’s This)

whatstext

QString

帮助提示信息(What’s This)

assistant

Assistant*

调用 Qt assistant显示FreeCAD文档

urlHandler

QMap<QString, QPointer<UrlHandler> >

加载Url资源

  3.1 欢迎界面

SplashScreen类不仅提供了自动加载欢迎界面,而且通过监控控制台输出以显示模块的加载进度。

MainWindow在FreeCAD启动的时候,会弹出欢迎界面窗口。Gui::MainWindow提供了以下两个函数完成欢迎界面窗口的创建与销毁。

void MainWindow::startSplasher(void);void MainWindow::stopSplasher(void);QPixmap MainWindow::splashImage() const;

实际上,这两个函数调用发生在void Application::runApplication(void)可以在FreeCADMain MainGui.cpp中配置欢迎界面,

App::Application::Config()["SplashScreen"] = "freecadsplash";App::Application::Config()["SplashAlignment" ] = "Bottom|Left";App::Application::Config()["SplashTextColor" ] = "#ffffff"; // whiteApp::Application::Config()["SplashInfoColor" ] = "#c8c8c8"; // light grey

其中图像资源”freecadsplash”在FreeCADGui resource.qrc中进行了定义。

  3.2 命令管理

CommandManager管理所有注册的Command及其派生类型对象,通过将Command与QAction相关联,可以将界面操作转换成Command对象的命令响应。

通常在界面模块文件中,利用CommandManager完成相关命令对象的注册。例如,对于CfdGui模块,在其AppCfdGui.cpp中,

// use a different name to CreateCommand()void CreateCfdCommands(void);//void loadStartResource()//{//    // add resources and reloads the translators//    Q_INIT_RESOURCE(Cfd);//    Gui::Translator::instance()->refresh();//}namespace CfdGui {class Module : public Py::ExtensionModule
{public: Module() : Py::ExtensionModule
("CfdGui") {Base::Interpreter().loadModule("Cfd"); initialize("This module is the CfdGui module."); // register with Python } virtual ~Module() {}private:};PyObject* initModule(){ return (new Module)->module().ptr();}} // namespace CfdGui/* Python entry */PyMOD_INIT_FUNC(CfdGui){ if (!Gui::Application::Instance) { PyErr_SetString(PyExc_ImportError, "Cannot load Gui module in console application."); PyMOD_Return(0); } // load dependent module try { Base::Interpreter().runString("import Cfd"); } catch(const Base::Exception& e) { PyErr_SetString(PyExc_ImportError, e.what()); PyMOD_Return(0); } catch (Py::Exception& e) { Py::Object o = Py::type(e); if (o.isString()) { Py::String s(o); Base::Console().Error("%s\n", s.as_std_string("utf-8").c_str()); } else { Py::String s(o.repr()); Base::Console().Error("%s\n", s.as_std_string("utf-8").c_str()); } // Prints message to console window if we are in interactive mode PyErr_Print(); } PyObject* mod = CfdGui::initModule(); Base::Console().Log("Loading GUI of CFD module... done\n"); // register preferences pages //new Gui::PrefPageProducer
("Cfd"); // instantiating the commands CreateCfdCommands(); CfdGui::Workbench::init(); // add resources and reloads the translators //loadStartResource(); PyMOD_Return(mod);}void CreateCfdCommands(void){ Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager(); // part, analysis, solver //rcCmdMgr.addCommand(new CmdFemAddPart()); //rcCmdMgr.addCommand(new CmdFemCreateAnalysis()); // Analysis is created in python //rcCmdMgr.addCommand(new CmdFemCreateSolver()); // Solver will be extended and created in python // constraints // mesh rcCmdMgr.addCommand(new CmdMeshImport()); // vtk post processing #ifdef FC_USE_VTK #endif}

  3.3 主菜单

在Workbench创建之后,会采用MenuManager创建已注册命令相对应的菜单项,仅需要重写Workbench::setMenuBar()即可轻松完成主菜单的定制。

Gui::MenuItem* Workbench::setupMenuBar() const;

  3.4 环境菜单

环境菜单是根据界面上下文显示部分已注册的Command命令。通过重写Workbench::setupContextMenu()可以完成环境菜单的定制。

void Workbench::setupContextMenu(const char* recipient,Gui::MenuItem* item) const;

  3.5 工具栏

ToolBarManager根据ToolBarItem创建Command相关联的工具按钮,可以通过Workbench::setupToolBars()以定制工具栏。

Gui::ToolBarItem* Workbench::setupToolBars() const;

  3.6 状态栏

状态栏用于显示界面操作提示信息、操作执行进度、切换鼠标导航等功能,实际开发中很少涉及状态栏部分的修改。

  3.7 停靠窗口

DockWindowManager用于管理停靠窗口的注册与显示等功能,根据需要重写Workbench:: setupDockWindows()以定制主窗口内的停靠窗口。

Gui::DockWindowItems* Workbench::setupDockWindows() const;

FreeCAD内置了常用的停靠窗口,如表所示

类型

名称

描述

ToolBox

toolBox

 

TreeView

 

 

PropertyView

 

 

SelectionView

 

 

ComboView

 

 

ReportView

 

 

PythonView

 

 

DAGView

 

 

  3.8 视图

视图用于关联文档对象,将文档中的全部或者部分数据对象显示到子窗口内。MDIView在BaseView的基础之上,提供了文档数据的显示功能。

  3.9 数据拖放

通过拖拽文件到FreeCAD界面程序,可以自动创建文档对象并完成文档打开、文档数据渲染等工作。

  3.10 事件处理

Qt中事件由一个特定的QEvent及其子类来表示,事件类型由QEvent类的枚举类型QEvent::Type来表示。一旦有事件发生。Qt便构建一个相应的QEvent子类的对象,然后将其传递给相应的QObject对象或者子对象。

事件先经过事件过滤器,然后是该对象的event()函数,最后是该对象的事件处理函数。

在MainWindow::event()中,主要完成了帮助信息等工作。

bool MainWindow::event(QEvent *e);

事件过滤器用来在一个QObject中监控其他多个QObject的事件。事件过滤器是由两个函数组成的一种操作,用来完成一个QObject对其他QObject的事件监视,这两个函数分别是installEventFilter()与eventFilter()。

void QObject::installEventFilter(QObject *filterObj);bool QObject::eventFilter(QObject *watched, QEvent *event);

在MainWindow中,当增加子窗口时,会安装view对应的事件过滤器,当view有时间发生的时候,则会调用MainWindow的eventFilter函数,

void MainWindow::addWindow(MDIView* view);bool MainWindow::eventFilter(QObject* o, QEvent* e);

而MainWindow::eventFilter也只是用于显示帮助提示信息等工作。

  3.11 配置保存与加载

MainWindow在创建的时候,会自动加载已保存的窗口布局、尺寸等相关的信息;当MainWindow销毁之前,会保存窗口相关的布局信息。

/// Loads the main window settings.void MainWindow::loadWindowSettings();/// Saves the main window settings.void MainWindow::saveWindowSettings();

四、Workbench管理

Workbench定义了主窗口工具栏、菜单栏、停靠窗口等外观,提供了不同的功能以供用户使用,仅将模块相关的功能加载到内存,提高了内存的使用效率。

当以Gui模式启动FreeCAD时,即执行

void  Gui::Application::runApplication(void)

在这个函数中,会根据App::Application::Config()["StartWorkbench"]与配置文件选项型的确定Workbench的名字,然后创建Workbench(默认类型是NoneWorkbench

bool Gui::Application::activateWorkbench(const char* name);

其中App::Application::Config()["StartWorkbench"]在MainGui.cpp中被复制为"StartWorkbench"

默认Workbench类型是NonWorkbench

  4.1 Workbench

虚基类Workbench定义了创建工具栏、菜单栏、停靠窗口等主窗口子部件的接口。当Workbench生成之后,会调用activate()函数完成工具栏、菜单栏、停靠窗口等部件的创建。

bool Workbench::activate(){    ToolBarItem* tb = setupToolBars();    setupCustomToolbars(tb, "Toolbar");    ToolBarManager::getInstance()->setup( tb );    delete tb;    ToolBarItem* cb = setupCommandBars();    setupCustomToolbars(cb, "Toolboxbar");    ToolBoxManager::getInstance()->setup( cb );    delete cb;    DockWindowItems* dw = setupDockWindows();    DockWindowManager::instance()->setup( dw );    delete dw;    MenuItem* mb = setupMenuBar();    MenuManager::getInstance()->setup( mb );    delete mb;    setupCustomShortcuts();    return true;}

可以看到,Workbench实际上是通过调用对应的虚函数完成具体的部件创建工作。

/** Returns a MenuItem tree structure of menus for this workbench. */virtual MenuItem* setupMenuBar() const=0;/** Returns a ToolBarItem tree structure of toolbars for this workbench. */virtual ToolBarItem* setupToolBars() const=0;/** Returns a ToolBarItem tree structure of command bars for this workbench. */virtual ToolBarItem* setupCommandBars() const=0;/** Returns a DockWindowItems structure of dock windows this workbench. */virtual DockWindowItems* setupDockWindows() const=0;

因此,仅需要重写创建工具栏、菜单栏、命令栏、停靠窗口等部件的虚函数,就可以很容易的完成主窗口界面的设计。而StdWorkbench、BlankWorkbench、NoneWorkbench、TestWorkbench、PythonBaseWorkbench、PythonBlankWorkbench、PythonWorkbench等提供了默认的实现方式,可以根据需要从它们派生来自定义Workbench。(这些Workbench基类的具体使用后续教程会分章节详细介绍。

  4.2 WorkbenchManager

存在全局唯一的WorkbenchManager类型的对象管理所有的Workbench对象。

class GuiExport WorkbenchManager  {public:    /** Creates the only instance of the WorkbenchManager. */    static WorkbenchManager* instance();    static void destruct();    /** Searches for and returns an existing workbench object with name \a name. If no     * such workbench exists then a workbench of class \a className gets created, if possible.     * If the workbench cannot be created 0 is returned.     */    Workbench* createWorkbench (const std::string& name, const std::string& className);    /** Removes the workbench with name \a name. If there is no such     * workbench exists nothing happens.     */    void removeWorkbench(const std::string& name);    /** Returns an instance of the workbench with name \a name. If there is     * no such workbench 0 is returned.     */    Workbench* getWorkbench (const std::string& name) const;    /** Activates the workbench with name \a name. */    bool activate(const std::string& name, const std::string& className);    /** Returns the active workbench. */    Workbench* active() const;    /** Returns a list of all created workbench objects. */    std::list
workbenches() const;protected: WorkbenchManager(); ~WorkbenchManager();private: static WorkbenchManager* _instance; Workbench* _activeWorkbench; std::map
_workbenches;};

可以看到,WorkManager提供了WorkBench创建、激活、销毁等功能。当访问这个全局对象的时候,会根据需要创建。

WorkbenchManager* WorkbenchManager::instance(){    if (_instance == 0)        _instance = new WorkbenchManager;    return _instance;}

在Gui::Application对象析构的时候会销毁这个全局唯一的WorkManager对象,

Application::~Application(){    Base::Console().Log("Destruct Gui::Application\n");    WorkbenchManager::destruct();    SelectionSingleton::destruct();    Translator::destruct();    WidgetFactorySupplier::destruct();    BitmapFactoryInst::destruct();#if 0    // we must run the garbage collector before shutting down the SoDB    // subsystem because we may reference some class objects of them in Python    Base::Interpreter().cleanupSWIG("SoBase *");    // finish also Inventor subsystem    SoFCDB::finish();#if (COIN_MAJOR_VERSION >= 2) && (COIN_MINOR_VERSION >= 4)    SoDB::finish();#elif (COIN_MAJOR_VERSION >= 3)    SoDB::finish();#else    SoDB::cleanup();#endif#endif    {    Base::PyGILStateLocker lock;    Py_DECREF(_pcWorkbenchDictionary);    }    // save macros    try {        MacroCommand::save();    }    catch (const Base::Exception& e) {        std::cerr << "Saving macros failed: " << e.what() << std::endl;    }    //App::GetApplication().Detach(this);    delete d;    Instance = 0;}

而在Gui::Application::activateWorkbench函数中会根据类型名称调用Gui::WorkbenchManager::activate创建对应的Workbench对象。

bool Application::activateWorkbench(const char* name) ;bool WorkbenchManager::activate(const std::string& name, const std::string& className){    Workbench* wb = createWorkbench(name, className);    if (wb) {        _activeWorkbench = wb;        wb->activate();        return true;    }      return false;}

  4.3 自定义Workbench

FreeCAD最大的特点是可以根据需要自行定义不同的Workbench来实现不同的界面外观,以满足不同的应用需求。

下面关于FreeCAD python模块的编写方法后续文档会进行详细讲解,这里仅以CfdWorkbench定义过程为例,着重分析自定义Workbench的主要步骤。

    4.3.1 定义Workbench子类

从Workbench派生子类,然后重写创建工具栏、菜单栏、停靠窗口的虚函数。为了简化Workbench子类的创建,StdWorkbench已经实现了默认的工具栏、菜单、停靠窗口等创建工作。

//CfdWorkbench定义头文件/** * The CfdWorkbench class defines a workbench for Computationl Fluid Dynamics (CFD). * Right now, only the incompressible flow model is considered. * @author Nene */class CfdWorkbench : public StdWorkbench{TYPESYSTEM_HEADER();public:CfdWorkbench();~CfdWorkbench();protected:MenuItem* setupMenuBar() const{MenuItem* root = StdWorkbench::setupMenuBar();// your changesreturn root;}ToolBarItem* setupToolBars() const{ToolBarItem* root = StdWorkbench::setupToolBars();// your changesreturn root;}ToolBarItem* setupCommandBars() const{ToolBarItem* root = StdWorkbench::setupCommandBars();// your changesreturn root;}};//CfdWorkbench源文件// --------------------------------------------------------------------TYPESYSTEM_SOURCE(Gui::CfdWorkbench, Gui::StdWorkbench)CfdWorkbench::CfdWorkbench(): StdWorkbench(){}CfdWorkbench::~CfdWorkbench(){}

    4.3.2 注册Workbench

当完成CfdWorkbench,需要注册CfdWorkbench的类型信息,可以在Gui::Application:: initTypes(void)完成,即

void Application::initTypes(void){    // views    Gui::BaseView                               ::init();    Gui::MDIView                                ::init();    Gui::View3DInventor                         ::init();    Gui::AbstractSplitView                      ::init();    Gui::SplitView3DInventor                    ::init();    // View Provider    Gui::ViewProvider                           ::init();    Gui::ViewProviderExtension                  ::init();    Gui::ViewProviderExtensionPython            ::init();    Gui::ViewProviderGroupExtension             ::init();    Gui::ViewProviderGroupExtensionPython       ::init();    Gui::ViewProviderGeoFeatureGroupExtension   ::init();    Gui::ViewProviderGeoFeatureGroupExtensionPython::init();    Gui::ViewProviderOriginGroupExtension       ::init();    Gui::ViewProviderOriginGroupExtensionPython ::init();    Gui::ViewProviderExtern                     ::init();    Gui::ViewProviderDocumentObject             ::init();    Gui::ViewProviderFeature                    ::init();    Gui::ViewProviderDocumentObjectGroup        ::init();    Gui::ViewProviderDocumentObjectGroupPython  ::init();    Gui::ViewProviderDragger                    ::init();    Gui::ViewProviderGeometryObject             ::init();    Gui::ViewProviderInventorObject             ::init();    Gui::ViewProviderVRMLObject                 ::init();    Gui::ViewProviderAnnotation                 ::init();    Gui::ViewProviderAnnotationLabel            ::init();    Gui::ViewProviderPointMarker                ::init();    Gui::ViewProviderMeasureDistance            ::init();    Gui::ViewProviderPythonFeature              ::init();    Gui::ViewProviderPythonGeometry             ::init();    Gui::ViewProviderPlacement                  ::init();    Gui::ViewProviderOriginFeature              ::init();    Gui::ViewProviderPlane                      ::init();    Gui::ViewProviderLine                       ::init();    Gui::ViewProviderGeoFeatureGroup            ::init();    Gui::ViewProviderGeoFeatureGroupPython      ::init();    Gui::ViewProviderOriginGroup                ::init();    Gui::ViewProviderPart                       ::init();    Gui::ViewProviderOrigin                     ::init();    Gui::ViewProviderMaterialObject             ::init();    Gui::ViewProviderMaterialObjectPython       ::init();    Gui::ViewProviderTextDocument               ::init();    // Workbench    Gui::Workbench                              ::init();    Gui::StdWorkbench                           ::init();    Gui::BlankWorkbench                         ::init();    Gui::NoneWorkbench                          ::init();    Gui::TestWorkbench                          ::init();    Gui::PythonBaseWorkbench                    ::init();    Gui::PythonBlankWorkbench                   ::init();    Gui::PythonWorkbench                        ::init();    Gui::CfdWorkbench                        ::init();    // register transaction type    new App::TransactionProducer
(ViewProviderDocumentObject::getClassTypeId());}

上面的代码同时完成了View、Workebench的注册等工作。

    4.3.3 编写Python模块

在Mod目录下,创建CfdWorkbench目录,同时增加Init.py与InitGui.py文件。

Init.py文件内容为:

# FreeCAD init script of the Surface module# (c) 2001 Juergen Riegel LGPL

 

InitGui.py的文件内容为:

# Cfd gui init module# (c) 2001 Juergen Riegel LGPLclass CfdWorkbench ( Workbench ):    "Cfd workbench object"    Icon = """        """    MenuText = "Cfd"    ToolTip = "Cfd workbench: Create and edit complex surfaces"    def Initialize(self):        # load the module        import SurfaceGui        import FreeCADGui        import Surface        # Set path to icon labels        FreeCADGui.addIconPath('./Gui/Resources/Icons/')    def GetClassName(self):        return "Gui::CfdWorkbench"Gui.addWorkbench(CfdWorkbench())

程序运行结果,

五、命令框架

在Qt中,通过菜单项、工具按钮、快捷键等就可以触发Qaction对应的槽函数。但是,当主窗口包含的Qaction较多时,就需要在MainWondow里面添加许多的槽函数,很显然,这样就会造成MainWindow类比较臃肿。而且,大多数情况下,以插件的形式提供功能通常都需要修改MainWindow界面。因此,这就需要将QAction与MainWindow进行解耦。

Command(派生于Gui::CommandBase)用于FreeCAD命令的具体实现;而Action则用于关联QAction与Gui::Command。

  5.1 Action

Action类实际上维护了QAction与Command之间的关联关系。

class GuiExport Action : public QObject{    Q_OBJECTpublic:    Action (Command* pcCmd, QObject * parent = 0);    /// Action takes ownership of the 'action' object.    Action (Command* pcCmd, QAction* action, QObject * parent);    virtual ~Action();    virtual void addTo (QWidget * w);    virtual void setEnabled(bool);    virtual void setVisible(bool);    void setCheckable(bool);    void setChecked (bool);    bool isChecked() const;    void setShortcut (const QString &);    QKeySequence shortcut() const;    void setIcon (const QIcon &);    void setStatusTip (const QString &);    QString statusTip() const;    void setText (const QString &);    QString text() const;    void setToolTip (const QString &);    QString toolTip() const;    void setWhatsThis (const QString &);    QString whatsThis() const;    void setMenuRole(QAction::MenuRole menuRole);public Q_SLOTS:    virtual void onActivated ();    virtual void onToggled   (bool);protected:    QAction* _action;    Command *_pcCmd;};Action::Action (Command* pcCmd, QObject * parent)  : QObject(parent), _action(new QAction( this )), _pcCmd(pcCmd){    _action->setObjectName(QString::fromLatin1(_pcCmd->getName()));    connect(_action, SIGNAL(triggered(bool)), this, SLOT(onActivated()));}void Action::onActivated (){    _pcCmd->invoke(0);}

从代码中可以看出,Action将Qaction::triggered(bool)信号关联到了Gui::Command::invoke(int)函数。

  5.2 Command

Command派生于CommandBase类,主要用于实现FreeCAD命令的具体操作。

在invoke()函数中,实际上是通过虚函数activated()完成具体的工作。

virtual void Command::activated(int iMsg)=0;

因此,仅需要自泪花Gui::Command,需要实现activated()虚函数,

例如:

class OpenCommand : public Gui::Command{public:OpenCommand() : Gui::Command("Std_Open"){// set up menu text, status tip, ...sMenuText = "&Open";sToolTipText = "Open a file";sWhatsThis = "Open a file";StatusTip = "Open a file";sPixmap = "Open"; // name of a registered pixmapsAccel = "Shift+P"; // or "P" or "P, L" or "Ctrl+X, Ctrl+C" for a sequence}protected:void activated(int){QString filter ... // make a filter of all supported file formatsQStringList FileList = QFileDialog::getOpenFileNames(filter, QString::null, getMainWindow());for (QStringList::Iterator it = FileList.begin(); it != FileList.end(); ++it) {getGuiApplication()->open((*it).latin1());}}};

  5.3 CommandManager

Gui::Application中存在一个CommandManager类型的静态对象用于管理所有的命令对象。CommandManager提供了命令对象添加、删除、运行等操作。

class GuiExport CommandManager{public:    /// Construction    CommandManager();    /// Destruction    ~CommandManager();    /// Insert a new command into the manager    void addCommand(Command* pCom);    /// Remove a command from the manager    void removeCommand(Command* pCom);    /// Adds the given command to a given widget    bool addTo(const char* Name, QWidget* pcWidget);    /** Returns all commands of a special App Module     *  delivers a vector of all commands in the given application module. When no     *  name is given the standard commands (build in ) are returned.     *  @see Command     */    std::vector 
getModuleCommands(const char *sModName) const; /** Returns all commands registered in the manager * delivers a vector of all commands. If you intereted in commands of * of a special app module use GetModuleCommands() * @see Command */ std::vector
getAllCommands(void) const; /** Returns all commands of a group * delivers a vector of all commands in the given group. */ std::vector
getGroupCommands(const char *sGrpName) const; /** Returns the command registered in the manager with the name sName * If nothing is found it returns a null pointer * @see Command */ Command* getCommandByName(const char* sName) const; /** * Runs the command */ void runCommandByName (const char* sName) const; /// method is OBSOLETE use GetModuleCommands() or GetAllCommands() const std::map
& getCommands() const { return _sCommands; } /// get frequently called by the AppWnd to check the commands are active. void testActive(void); void addCommandMode(const char* sContext, const char* sName); void updateCommands(const char* sContext, int mode);private: /// Destroys all commands in the manager and empties the list. void clearCommands(); std::map
_sCommands; std::map
> _sCommandModes;};

从CommandManager的定义可以看出,添加、删除命令对象使用的方法为,

void CommandManager::addCommand(Command* pCom);void CommandManager::removeCommand(Command* pCom);

  5.4 注册命令

在Gui::Application构造函数中,通过调用createStandardOperations()函数完成了向CommandManager注册命令。

void Application::createStandardOperations(){    // register the application Standard commands from CommandStd.cpp    Gui::CreateStdCommands();    Gui::CreateDocCommands();    Gui::CreateFeatCommands();    Gui::CreateMacroCommands();    Gui::CreateViewStdCommands();    Gui::CreateWindowStdCommands();    Gui::CreateStructureCommands();    Gui::CreateTestCommands();}

六、菜单管理

在Workbench的创建过程中,最终会通过MenuItem、MenuManager这两个类完成对应的菜单创建及关联CommandManager已注册的命令对象。

bool Gui::Application::activateWorkbench(const char* name)virtual MenuItem* Workbench::setupMenuBar() const=0;

  6.1 MenuItem

MenuItem保存了菜单名字及子菜单等信息,菜单名字保存在私有变量_name

std::string MenuItem::_name;

可以设置菜单的名字

void MenuItem::setCommand(const std::string&);std::string MenuItem::command() const;

一个菜单项可以包含若干个子菜单,存放在私有成员变量_items

QList
MenuItem::_items

同时,MenuItem提供了增加、删除子菜单项的方法,

void MenuItem::appendItem(MenuItem*);bool MenuItem::insertItem(MenuItem*, MenuItem*);MenuItem* MenuItem::afterItem(MenuItem*) const;void MenuItem::removeItem(MenuItem*);void MenuItem::clear();

  6.2 MenuManager

MenuManager用于辅助创建主窗口菜单的类,存在全局唯一的MenuManager对象,

static MenuManager* MenuManager::getInstance();

依据MenuItem,MenuManager可以为主窗口增加菜单

void MenuManager::setup(MenuItem*) const;

  6.3 关联命令

当Workbench::activate()函数中,会完成工具栏、菜单栏、停靠窗口等窗体元素的创建。

bool Workbench::activate(){    ToolBarItem* tb = setupToolBars();    setupCustomToolbars(tb, "Toolbar");    ToolBarManager::getInstance()->setup( tb );    delete tb;    ToolBarItem* cb = setupCommandBars();    setupCustomToolbars(cb, "Toolboxbar");    ToolBoxManager::getInstance()->setup( cb );    delete cb;    DockWindowItems* dw = setupDockWindows();    DockWindowManager::instance()->setup( dw );    delete dw;    MenuItem* mb = setupMenuBar();    MenuManager::getInstance()->setup( mb );    delete mb;    setupCustomShortcuts();    return true;}

不同的Workbench子类,通过重写虚函数Workbench::setupMenuBar()完成具体的菜单项的创建,这个函数返回树状菜单列表的根指针。

virtual MenuItem* Gui::Workbench::setupMenuBar() const = 0;

在Gui::MenuManager::setup()函数中,根据MenuItem创建实际的窗口菜单项QMenu。

void MenuManager::setup(MenuItem* menuItems) const;

主菜单项MenuItem对应的QMenu创建成功之后,会递归地创建其子菜单项,同时调用CommandManager::add()函数完成Qmenu对应的Qaction与CommandManager已注册的Command子类的关联。

 

void MenuManager::setup(MenuItem* item, QMenu* menu) const;bool CommandManager::addTo(const char* Name, QWidget *pcWidget)

七、停靠窗口(DockWindow

在Qt中,停靠窗口QDockWidget可以停靠在主窗口QMainWindow中,也可以悬浮起来作为桌面顶级窗口。可以将QWidget及其派生类型的窗口部件嵌入到停靠窗口QDockWidget内,这样就为普通QWidget提供了主窗口内的自动布局功能。

  7.1 DockWindow

DockWindow实际上是派生于QWidget的普通窗口,这样DockWindow就可以嵌入到QDockWidget中。另一方面,因为DockWindow同时派生于BaseView,所以DockWindow可以关联Document对象,可以访问Document对象的数据、关联Document对象信号。

class GuiExport DockWindow : public QWidget, public BaseView{  Q_OBJECTpublic:  /** View constructor   * Attach the view to the given document. If the document is 0   * the view will attach to the active document. Be aware there isn't   * always an active document available!   */  DockWindow ( Gui::Document* pcDocument=0, QWidget *parent=0 );  /** View destructor   * Detach the view from the document, if attached.   */  virtual ~DockWindow();  /** @name methods to override   */  //@{  /// get called when the document is updated  virtual void onUpdate(void){}  /// returns the name of the view (important for messages)  virtual const char *getName(void) const { return "DockWindow"; }  /// Message handler  virtual bool onMsg(const char* ,const char** ){ return false; }  /// Message handler test  virtual bool onHasMsg(const char*) const { return false; }  /// overwrite when checking on close state  virtual bool canClose(void){return true;}  //@}Q_SIGNALS:  /// sends a message to the document  void sendCloseView(MDIView* theView);};

  7.2 DockWindowManager

DockWindowManager用于管理DockWindow的创建、删除等功能,存在全局唯一的DockWindowManager静态对象,

static DockWindowManager* DockWindowManager::instance();

在DockWindowManager内,DockWindowManagerP结构内维护了DockWindow对象列表。

struct DockWindowManager::DockWindowManagerP* d;namespace Gui {struct DockWindowManagerP{    QList
_dockedWindows; QMap
> _dockWindows; DockWindowItems _dockWindowItems;};} // namespace Gui

  7.3 停靠窗口创建过程

在MainWindow的构造函数中,首先会根据配置文件来注册停靠窗口,

bool DockWindowManager::registerDockWindow(const char* name, QWidget* widget);

实际上就是创建对应的DockWindow对象并将其存放到DockManager::_dockWindows中。

MainWindow::MainWindow(QWidget * parent, Qt::WindowFlags f)  : QMainWindow( parent, f/*WDestructiveClose*/ ){    DockWindowManager* pDockMgr = DockWindowManager::instance();    std::string hiddenDockWindows;;    const std::map
& config = App::Application::Config(); std::map
::const_iterator ht = config.find("HiddenDockWindow"); if (ht != config.end()) hiddenDockWindows = ht->second; // Show all dockable windows over the workbench facility //#if 0 // Toolbox if (hiddenDockWindows.find("Std_ToolBox") == std::string::npos) { ToolBox* toolBox = new ToolBox(this); toolBox->setObjectName(QT_TRANSLATE_NOOP("QDockWidget","Toolbox")); pDockMgr->registerDockWindow("Std_ToolBox", toolBox); ToolBoxManager::getInstance()->setToolBox( toolBox ); }#endif // Tree view if (hiddenDockWindows.find("Std_TreeView") == std::string::npos) { //work through parameter. ParameterGrp::handle group = App::GetApplication().GetUserParameter(). GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("TreeView"); bool enabled = group->GetBool("Enabled", true); group->SetBool("Enabled", enabled); //ensure entry exists. if (enabled) { TreeDockWidget* tree = new TreeDockWidget(0, this); tree->setObjectName (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Tree view"))); tree->setMinimumWidth(210); pDockMgr->registerDockWindow("Std_TreeView", tree); } } // Property view if (hiddenDockWindows.find("Std_PropertyView") == std::string::npos) { //work through parameter. ParameterGrp::handle group = App::GetApplication().GetUserParameter(). GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("PropertyView"); bool enabled = group->GetBool("Enabled", true); group->SetBool("Enabled", enabled); //ensure entry exists. if (enabled) { PropertyDockView* pcPropView = new PropertyDockView(0, this); pcPropView->setObjectName (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Property view"))); pcPropView->setMinimumWidth(210); pDockMgr->registerDockWindow("Std_PropertyView", pcPropView); } } // Selection view if (hiddenDockWindows.find("Std_SelectionView") == std::string::npos) { SelectionView* pcSelectionView = new SelectionView(0, this); pcSelectionView->setObjectName (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Selection view"))); pcSelectionView->setMinimumWidth(210); pDockMgr->registerDockWindow("Std_SelectionView", pcSelectionView); } // Combo view if (hiddenDockWindows.find("Std_CombiView") == std::string::npos) { CombiView* pcCombiView = new CombiView(0, this); pcCombiView->setObjectName(QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Combo View"))); pcCombiView->setMinimumWidth(150); pDockMgr->registerDockWindow("Std_CombiView", pcCombiView); }#if QT_VERSION < 0x040500 // Report view if (hiddenDockWindows.find("Std_ReportView") == std::string::npos) { Gui::DockWnd::ReportView* pcReport = new Gui::DockWnd::ReportView(this); pcReport->setObjectName (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Report view"))); pDockMgr->registerDockWindow("Std_ReportView", pcReport); }#else // Report view (must be created before PythonConsole!) if (hiddenDockWindows.find("Std_ReportView") == std::string::npos) { ReportOutput* pcReport = new ReportOutput(this); pcReport->setWindowIcon(BitmapFactory().pixmap("MacroEditor")); pcReport->setObjectName (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Report view"))); pDockMgr->registerDockWindow("Std_ReportView", pcReport); } // Python console if (hiddenDockWindows.find("Std_PythonView") == std::string::npos) { PythonConsole* pcPython = new PythonConsole(this); ParameterGrp::handle hGrp = App::GetApplication().GetUserParameter(). GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("General"); if (hGrp->GetBool("PythonWordWrap", true)) { pcPython->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); } else { pcPython->setWordWrapMode(QTextOption::NoWrap); } pcPython->setWindowIcon(Gui::BitmapFactory().iconFromTheme("applications-python")); pcPython->setObjectName (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","Python console"))); pDockMgr->registerDockWindow("Std_PythonView", pcPython); } //Dag View. if (hiddenDockWindows.find("Std_DAGView") == std::string::npos) { //work through parameter. // old group name ParameterGrp::handle deprecateGroup = App::GetApplication().GetUserParameter(). GetGroup("BaseApp")->GetGroup("Preferences"); bool enabled = false; if (deprecateGroup->HasGroup("DAGView")) { deprecateGroup = deprecateGroup->GetGroup("DAGView"); enabled = deprecateGroup->GetBool("Enabled", enabled); } // new group name ParameterGrp::handle group = App::GetApplication().GetUserParameter(). GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("DockWindows")->GetGroup("DAGView"); enabled = group->GetBool("Enabled", enabled); group->SetBool("Enabled", enabled); //ensure entry exists. if (enabled) { DAG::DockWindow *dagDockWindow = new DAG::DockWindow(nullptr, this); dagDockWindow->setObjectName (QString::fromLatin1(QT_TRANSLATE_NOOP("QDockWidget","DAG View"))); pDockMgr->registerDockWindow("Std_DAGView", dagDockWindow); } }}

然后,在Gui::Workbench::activate()函数中,会调用虚函数setupDockWindows()生成停靠窗口信息,并将其添加到QDockWidget中。

 

bool Workbench::activate(){    ToolBarItem* tb = setupToolBars();    setupCustomToolbars(tb, "Toolbar");    ToolBarManager::getInstance()->setup( tb );    delete tb;    ToolBarItem* cb = setupCommandBars();    setupCustomToolbars(cb, "Toolboxbar");    ToolBoxManager::getInstance()->setup( cb );    delete cb;    DockWindowItems* dw = setupDockWindows();    DockWindowManager::instance()->setup( dw );    delete dw;    MenuItem* mb = setupMenuBar();    MenuManager::getInstance()->setup( mb );    delete mb;    setupCustomShortcuts();    return true;}

八、文档

在文档/视图结构里,文档可视为一个应用程序的数据元素的集合,能够被逻辑地组合的一系列数据,包括文本、图形、图象和表格数据。一个文档代表了用户存储或打开的一个文件单位。文档的主要作用是把对数据的处理从对用户界面的处理中分离出来,集中处理数据,同时提供了一个与其它类交互的接口。

在Gui::Application内部维护了Gui::Document文档对象列表,

struct Gui::Application::ApplicationP* d;struct Gui::ApplicationP{    ApplicationP() :    activeDocument(0L),    isClosing(false),    startingUp(true)    {        // create the macro manager        macroMngr = new MacroManager();    }    ~ApplicationP()    {        delete macroMngr;    }    /// list of all handled documents    std::map
documents; /// Active document Gui::Document* activeDocument; MacroManager* macroMngr; /// List of all registered views std::list
passive; bool isClosing; bool startingUp; /// Handles all commands CommandManager commandManager;};

从上面可以看出,FreeCAD采用了多文档的架构,一个程序(Gui::Application)中可以包含多个文档对象(Gui::Document)。

  8.1 Gui::Document

Gui::Document内部封装了App::Document对象,主要功能是为了关联BaseView、MDIView及其子类等视图对象。

/// Attach a view (get called by the MDIView constructor)void Document::attachView(Gui::BaseView* pcView, bool bPassiv = false);/// Detach a view (get called by the MDIView destructor)void Document::detachView(Gui::BaseView* pcView, bool bPassiv = false);

当新建文档时,会调用Gui::Document::createView()函数创建View3Dinventor视图对象,

void Document::createView(const Base::Type& typeId) ;

针对MDIView及其子类,Gui::Document提供了创建、访问MDIView视图对象的接口

/// Getter for the active viewGui::MDIView* Document::getActiveView(void) const;void setActiveWindow(Gui::MDIView* view);Gui::MDIView* Document::getEditingViewOfViewProvider(Gui::ViewProvider*) const;Gui::MDIView* Document::getViewOfViewProvider(Gui::ViewProvider*) const;Gui::MDIView* Document::getViewOfNode(SoNode*) const;/// Create a clone of the given viewGui::MDIView* Document::cloneView(Gui::MDIView*);

九、中心部件视图

在文档/视图结构里,视图是数据的用户界面,可以从文档中获取数据并将其窗口中显示。视图还可提供用户与文档中数据的交互功能,将用户的输入转化为对数据的操作。

在FreeCAD中,一个视图对象(Gui::MDIView派生类)最多只能与一个文档对象(Gui::Document派生类)相关联,负责显示Gui::Document内的内容以及将用户输入转化为对文档数据的操作。

  9.1 BaseView

BaseView用于关联Gui::Document对象,用两种关联方式。第一种方式是关联固定的固定的Gui::Document文档对象;第二种方式是管理当前Gui::Document文档对象。

BaseView::BaseView(Gui::Document* pcDocument = 0);void BaseView::setDocument(Gui::Document* pcDocument);

当pcDocument为空指针时,将会关联当前文档对象,因为当前可能没有文档对象,所以BaseView可能不会与任何文档对象关联。

此外,BaseView定义若干虚函数用于响应文档对象响应的事件,

/// get called when the document is updatedvirtual void BaseView::onUpdate(void){}/// get called when the document is relabeled (change of its user name)virtual void BaseView::onRelabel(Gui::Document *){}/// get called when the document is renamed (change of its internal name)virtual void BaseView::onRename(Gui::Document *){}/// returns the name of the view (important for messages)virtual const char * BaseView::getName(void) const;/// Message handlervirtual bool BaseView::onMsg(const char* pMsg, const char** ppReturn)=0;/// Message handler testvirtual bool BaseView::onHasMsg(const char* pMsg) const=0;/// overwrite when checking on close statevirtual bool BaseView::canClose(void){return true;}/// delete itselfvirtual void BaseView::deleteSelf();

  9.2 MDIView

MDIView同时派生于QMainWindow与BaseView,这样不仅可以关联Gui::Document对象,而且支持作为子窗口、顶级窗口、全屏窗口来显示文档内容的功能。

/// MDI view mode enumenum ViewMode {    Child,      /**< Child viewing, view is docked inside the MDI application window */      TopLevel,   /**< The view becomes a top level window and can be moved outsinde the application window */      FullScreen  /**< The view goes to full screen viewing */};virtual void MDIView::setCurrentViewMode(ViewMode mode);

同时,MDIView维护了当前活动文档对象列表,可以高亮显示这些活动文档对象。

 

ActiveObjectList MDIView::ActiveObjects;template
inline _T getActiveObject(const char* name) const{ return ActiveObjects.getObject<_T>(name);}void MDIView::setActiveObject(App::DocumentObject*o, const char*n){ ActiveObjects.setObject(o, n);}bool MDIView::hasActiveObject(const char*n) const{ return ActiveObjects.hasObject(n);}

  9.3 ViewProvider

在视图窗口中,实际上是通过各种ViewProvider及其子类完成各种对象数据的显示以及与用户的交互功能。ViewProvider类定义对象数据显示控制、对象操作等主要接口。

  9.4 视图创建过程

在Application::initApplication()函数中调用Application:: init_types(),完成相关视图类型的注册,

void Application::initTypes(void){    // views    Gui::BaseView                               ::init();    Gui::MDIView                                ::init();    Gui::View3DInventor                         ::init();    Gui::AbstractSplitView                      ::init();    Gui::SplitView3DInventor                    ::init();    // View Provider    Gui::ViewProvider                           ::init();    Gui::ViewProviderExtension                  ::init();    Gui::ViewProviderExtensionPython            ::init();    Gui::ViewProviderGroupExtension             ::init();    Gui::ViewProviderGroupExtensionPython       ::init();    Gui::ViewProviderGeoFeatureGroupExtension   ::init();    Gui::ViewProviderGeoFeatureGroupExtensionPython::init();    Gui::ViewProviderOriginGroupExtension       ::init();    Gui::ViewProviderOriginGroupExtensionPython ::init();    Gui::ViewProviderExtern                     ::init();    Gui::ViewProviderDocumentObject             ::init();    Gui::ViewProviderFeature                    ::init();    Gui::ViewProviderDocumentObjectGroup        ::init();    Gui::ViewProviderDocumentObjectGroupPython  ::init();    Gui::ViewProviderDragger                    ::init();    Gui::ViewProviderGeometryObject             ::init();    Gui::ViewProviderInventorObject             ::init();    Gui::ViewProviderVRMLObject                 ::init();    Gui::ViewProviderAnnotation                 ::init();    Gui::ViewProviderAnnotationLabel            ::init();    Gui::ViewProviderPointMarker                ::init();    Gui::ViewProviderMeasureDistance            ::init();    Gui::ViewProviderPythonFeature              ::init();    Gui::ViewProviderPythonGeometry             ::init();    Gui::ViewProviderPlacement                  ::init();    Gui::ViewProviderOriginFeature              ::init();    Gui::ViewProviderPlane                      ::init();    Gui::ViewProviderLine                       ::init();    Gui::ViewProviderGeoFeatureGroup            ::init();    Gui::ViewProviderGeoFeatureGroupPython      ::init();    Gui::ViewProviderOriginGroup                ::init();    Gui::ViewProviderPart                       ::init();    Gui::ViewProviderOrigin                     ::init();    Gui::ViewProviderMaterialObject             ::init();    Gui::ViewProviderMaterialObjectPython       ::init();    Gui::ViewProviderTextDocument               ::init();    // Workbench    Gui::Workbench                              ::init();    Gui::StdWorkbench                           ::init();    Gui::BlankWorkbench                         ::init();    Gui::NoneWorkbench                          ::init();    Gui::TestWorkbench                          ::init();    Gui::PythonBaseWorkbench                    ::init();    Gui::PythonBlankWorkbench                   ::init();    Gui::PythonWorkbench                        ::init();    // register transaction type    new App::TransactionProducer
(ViewProviderDocumentObject::getClassTypeId());}

当新建文档时,会触发Application::slotNewDocument(const App::Document& Doc)响应函数的调用,

void Application::slotNewDocument(const App::Document& Doc){#ifdef FC_DEBUG    std::map
::const_iterator it = d->documents.find(&Doc); assert(it==d->documents.end());#endif Gui::Document* pDoc = new Gui::Document(const_cast
(&Doc),this); d->documents[&Doc] = pDoc; // connect the signals to the application for the new document pDoc->signalNewObject.connect(boost::bind(&Gui::Application::slotNewObject, this, _1)); pDoc->signalDeletedObject.connect(boost::bind(&Gui::Application::slotDeletedObject, this, _1)); pDoc->signalChangedObject.connect(boost::bind(&Gui::Application::slotChangedObject, this, _1, _2)); pDoc->signalRelabelObject.connect(boost::bind(&Gui::Application::slotRelabelObject, this, _1)); pDoc->signalActivatedObject.connect(boost::bind(&Gui::Application::slotActivatedObject, this, _1)); pDoc->signalInEdit.connect(boost::bind(&Gui::Application::slotInEdit, this, _1)); pDoc->signalResetEdit.connect(boost::bind(&Gui::Application::slotResetEdit, this, _1)); signalNewDocument(*pDoc); pDoc->createView(View3DInventor::getClassTypeId()); // FIXME: Do we really need this further? Calling processEvents() mixes up order of execution in an // unpredicatable way. At least it seems that with Qt5 we don't need this any more.#if QT_VERSION < 0x050000 qApp->processEvents(); // make sure to show the window stuff on the right place#endif}

可以看到,在这个函数中实际上是通过调用Document::createView()创建视图窗口通过分析代码可以发现,目前视图通过新建文档仅View3Dinventor类型视图。

 

void Document::createView(const Base::Type& typeId){    if (!typeId.isDerivedFrom(MDIView::getClassTypeId()))        return;    std::list
theViews = this->getMDIViewsOfType(typeId); if (typeId == View3DInventor::getClassTypeId()) { QtGLWidget* shareWidget = 0; // VBO rendering doesn't work correctly when we don't share the OpenGL widgets if (!theViews.empty()) { View3DInventor* firstView = static_cast
(theViews.front()); shareWidget = qobject_cast
(firstView->getViewer()->getGLWidget()); } View3DInventor* view3D = new View3DInventor(this, getMainWindow(), shareWidget); if (!theViews.empty()) { View3DInventor* firstView = static_cast
(theViews.front()); std::string overrideMode = firstView->getViewer()->getOverrideMode(); view3D->getViewer()->setOverrideMode(overrideMode); } // attach the viewproviders. we need to make sure that we only attach the toplevel ones // and not viewproviders which are claimed by other providers. To ensure this we first // add all providers and then remove the ones already claimed std::map
::const_iterator It1; std::vector
child_vps; for (It1=d->_ViewProviderMap.begin();It1!=d->_ViewProviderMap.end();++It1) { view3D->getViewer()->addViewProvider(It1->second); std::vector
children = It1->second->claimChildren3D(); child_vps.insert(child_vps.end(), children.begin(), children.end()); } std::map
::const_iterator It2; for (It2=d->_ViewProviderMapAnnotation.begin();It2!=d->_ViewProviderMapAnnotation.end();++It2) { view3D->getViewer()->addViewProvider(It2->second); std::vector
children = It2->second->claimChildren3D(); child_vps.insert(child_vps.end(), children.begin(), children.end()); } for(App::DocumentObject* obj : child_vps) view3D->getViewer()->removeViewProvider(getViewProvider(obj)); const char* name = getDocument()->Label.getValue(); QString title = QString::fromLatin1("%1 : %2[*]") .arg(QString::fromUtf8(name)).arg(d->_iWinCount++); view3D->setWindowTitle(title); view3D->setWindowModified(this->isModified()); view3D->setWindowIcon(QApplication::windowIcon()); view3D->resize(400, 300); getMainWindow()->addWindow(view3D); }}

十、属性系统

Model/View架构是用来实现数据的存储、处理及其显示的。Model是应用对象,用于来表示数据,定义了数据访问的接口;View是模型的用户界面,负责显示数据与接收用户界面输入。Model/View架构核心是将数据的具体存储方式与数据的界面显示进行分离,View可以采用统一的接口访问Model中的数据。

在Qt中,QAbstractItemModel本身并不存储数据,只是定义了访问模型数据、编辑数据的接口;QModelIndex用来表示数据项的配置;QAbstractItemView通过信号-槽机制关联QAbstractItemModel模型;QAbstractItemDelegate则具体的定义QAbstractItemView显示和编辑数据的方式。

  10.1 PropertyItem

PropertyItem是属性数据的存储节点,实际上是采用“双亲孩子法”来存储一棵属性树。节点数据存放在propertyItems链表中

std::vector
PropertyItem::propertyItems;

  10.2 PropertyModel

PropertyModel派生于QabstractItemModel,实现了访问属性结构的属性系统的功能,其内部维护了一个属性系统的根节点,

PropertyItem *PropertyModel::rootItem;

  10.3 PropertyEditor

PropertyEditor派生于QtreeView,用于以树形方式显示PropertyModel中的数据项。

 

十一、多国语言支持

待续。此部分比较简单,主要是Translator通过QTranslator来实现FreeCAD界面对多种语言的支持。

十二、帮助系统

待续。此部分较简单,主要是通过Assistant来显示安装包doc目录下帮助文档。

 

参考资料

  1. Fr

转载地址:http://tsowk.baihongyu.com/

你可能感兴趣的文章