前言
最近一个接到这样一个需求,需要在FreeCAD的中加入一个自定义的工程文件。这需要搞懂原本框架中的新建工程、打开工程到保存撤销回复再到保存另存的一整套流程,还需要搞懂工程与UI之间的对应关系。于是开始翻看FreeCAD得源代码,一边看一边惊呼,居然还这样操作。然是我才疏学浅,这源代码使我受益匪浅。
整个工程得流程,可以分为三个部分:
- Command。这部分主要往程序主框架中注册一些命令,如新建、打开、保存等这些基本操作,然后将这些命令放到界面上去供用户使用。
- UI。这个主要指工程所对应的ui,比如在一个模型工程,那么这个工程必定会有一个显示模型的一个3D视窗。这个视窗就是这里只得ui
- Document。这个类是原框架中对应工程数据的一个类。
Command
Command类是FreeCAD源代码提供的一个抽象类,如果想要实现一个自定义的命令,那么最好是继承这个类。这个类中已经实现了十分多的通用功能。
而Command则是继承了一个叫做CommandBase的类。
/** The CommandBase class
* This lightweigt class is the base class of all commands in FreeCAD. It represents the link between the FreeCAD
* command framework and the QAction world of Qt.
* @author Werner Mayer
*/
class GuiExport CommandBase
{
protected:
CommandBase(const char* sMenu, const char* sToolTip=0, const char* sWhat=0,
const char* sStatus=0, const char* sPixmap=0, const char* sAccel=0);
virtual ~CommandBase();
public:
/**
* Returns the Action object of this command, or 0 if it doesn't exist.
*/
Action* getAction() const;
/** @name Methods to override when creating a new command */
//@{
protected:
/// Creates the used Action when adding to a widget. The default implementation does nothing.
virtual Action * createAction(void);
public:
/// Reassigns QAction stuff after the language has changed.
virtual void languageChange() = 0;
/// Updates the QAction with respect to the passed mode.
virtual void updateAction(int mode) = 0;
/// The C++ class name is needed as context for the translation framework
virtual const char* className() const = 0;
//@}
/** @name Methods to get the properties of the command */
//@{
virtual const char* getMenuText () const { return sMenuText; }
virtual const char* getToolTipText() const { return sToolTipText; }
virtual const char* getStatusTip () const { return sStatusTip; }
virtual const char* getWhatsThis () const { return sWhatsThis; }
virtual const char* getPixmap () const { return sPixmap; }
virtual const char* getAccel () const { return sAccel; }
//@}
/** @name Methods to set the properties of the command */
//@{
void setWhatsThis (const char*);
void setMenuText (const char*);
void setToolTipText(const char*);
void setStatusTip (const char*);
void setPixmap (const char*);
void setAccel (const char*);
//@}
protected:
/** @name Attributes set by the inherited constructor.
*
* They set up the most important properties of the command.
* In the constructor are set default values.
* The real values should be set in the constructor of the inheriting class.
*/
//@{
const char* sMenuText;
const char* sToolTipText;
const char* sWhatsThis;
const char* sStatusTip;
const char* sPixmap;
const char* sAccel;
//@}
protected:
Action *_pcAction;
/** The CommandBase class
* This lightweigt class is the base class of all commands in FreeCAD. It represents the link between the FreeCAD
* command framework and the QAction world of Qt.
* @author Werner Mayer
*/
class GuiExport CommandBase
{
protected:
CommandBase(const char* sMenu, const char* sToolTip=0, const char* sWhat=0,
const char* sStatus=0, const char* sPixmap=0, const char* sAccel=0);
virtual ~CommandBase();
public:
/**
* Returns the Action object of this command, or 0 if it doesn't exist.
*/
Action* getAction() const;
/** @name Methods to override when creating a new command */
//@{
protected:
/// Creates the used Action when adding to a widget. The default implementation does nothing.
virtual Action * createAction(void);
public:
/// Reassigns QAction stuff after the language has changed.
virtual void languageChange() = 0;
/// Updates the QAction with respect to the passed mode.
virtual void updateAction(int mode) = 0;
/// The C++ class name is needed as context for the translation framework
virtual const char* className() const = 0;
//@}
/** @name Methods to get the properties of the command */
//@{
virtual const char* getMenuText () const { return sMenuText; }
virtual const char* getToolTipText() const { return sToolTipText; }
virtual const char* getStatusTip () const { return sStatusTip; }
virtual const char* getWhatsThis () const { return sWhatsThis; }
virtual const char* getPixmap () const { return sPixmap; }
virtual const char* getAccel () const { return sAccel; }
//@}
/** @name Methods to set the properties of the command */
//@{
void setWhatsThis (const char*);
void setMenuText (const char*);
void setToolTipText(const char*);
void setStatusTip (const char*);
void setPixmap (const char*);
void setAccel (const char*);
//@}
protected:
/** @name Attributes set by the inherited constructor.
*
* They set up the most important properties of the command.
* In the constructor are set default values.
* The real values should be set in the constructor of the inheriting class.
*/
//@{
const char* sMenuText;
const char* sToolTipText;
const char* sWhatsThis;
const char* sStatusTip;
const char* sPixmap;
const char* sAccel;
//@}
protected:
Action *_pcAction;
/** The CommandBase class * This lightweigt class is the base class of all commands in FreeCAD. It represents the link between the FreeCAD * command framework and the QAction world of Qt. * @author Werner Mayer */ class GuiExport CommandBase { protected: CommandBase(const char* sMenu, const char* sToolTip=0, const char* sWhat=0, const char* sStatus=0, const char* sPixmap=0, const char* sAccel=0); virtual ~CommandBase(); public: /** * Returns the Action object of this command, or 0 if it doesn't exist. */ Action* getAction() const; /** @name Methods to override when creating a new command */ //@{ protected: /// Creates the used Action when adding to a widget. The default implementation does nothing. virtual Action * createAction(void); public: /// Reassigns QAction stuff after the language has changed. virtual void languageChange() = 0; /// Updates the QAction with respect to the passed mode. virtual void updateAction(int mode) = 0; /// The C++ class name is needed as context for the translation framework virtual const char* className() const = 0; //@} /** @name Methods to get the properties of the command */ //@{ virtual const char* getMenuText () const { return sMenuText; } virtual const char* getToolTipText() const { return sToolTipText; } virtual const char* getStatusTip () const { return sStatusTip; } virtual const char* getWhatsThis () const { return sWhatsThis; } virtual const char* getPixmap () const { return sPixmap; } virtual const char* getAccel () const { return sAccel; } //@} /** @name Methods to set the properties of the command */ //@{ void setWhatsThis (const char*); void setMenuText (const char*); void setToolTipText(const char*); void setStatusTip (const char*); void setPixmap (const char*); void setAccel (const char*); //@} protected: /** @name Attributes set by the inherited constructor. * * They set up the most important properties of the command. * In the constructor are set default values. * The real values should be set in the constructor of the inheriting class. */ //@{ const char* sMenuText; const char* sToolTipText; const char* sWhatsThis; const char* sStatusTip; const char* sPixmap; const char* sAccel; //@} protected: Action *_pcAction;
这个类的主要做了两件事:
- 为commad创action,包括action对应的图标、menuText、whatThis、快捷键等显示相关的信息。
- 定义了一个languageChange()的纯虚函数,用于软件的语言发生改变时,改变command的显示语言。
然后时commad类,主要关注这几个函数。
virtual void activated(int iMsg)=0;
virtual void activated(int iMsg)=0;
virtual void activated(int iMsg)=0;
这个纯虚函数,当命令被执行或者对应的action被点击的时候会被调用。
virtual bool isActive(void);
virtual bool isActive(void);
virtual bool isActive(void);
当命令被注册入框架的时候,会定时调用这个函数检查命令的状态。(目前看来设置为false会导致界面上的action变成不可用的状态)
其他的就是一些静态的类,主要时对document、撤销回复、的一些操作,有些我也没搞太懂,这里就不展开了。
来看一下源代码中是如何定义一个命令并将命令注册到框架中的。
/** The Command Macro Standard + isActive() + createAction()
* This macro makes it easier to define a new command.
* The parameters are the class name
* @author Werner Mayer
*/
#define DEF_STD_CMD_AC(X) class X : public Gui::Command \
{\
public:\
X();\
virtual ~X(){}\
virtual const char* className() const\
{ return #X; }\
protected: \
virtual void activated(int iMsg);\
virtual bool isActive(void);\
virtual Gui::Action * createAction(void);\
};
DEF_STD_CMD_AC(StdCmdUndo);
StdCmdUndo::StdCmdUndo()
:Command("Std_Undo")
{
sGroup = QT_TR_NOOP("Edit");
sMenuText = QT_TR_NOOP("&Undo");
sToolTipText = QT_TR_NOOP("Undo exactly one action");
sWhatsThis = "Std_Undo";
sStatusTip = QT_TR_NOOP("Undo exactly one action");
sPixmap = "edit-undo";
sAccel = keySequenceToAccel(QKeySequence::Undo);
eType = ForEdit;
}
void StdCmdUndo::activated(int iMsg)
{
Q_UNUSED(iMsg);
// Application::Instance->slotUndo();
getGuiApplication()->sendMsgToActiveView("Undo");
App::Document* pcDoc=App::GetApplication().getActiveDocument();
//pcDoc->recompute();
pcDoc->flagNeedUpdateBoolean.setValue(0);
doCommand(Command::Gui, "DocumentTools.updateBoolean()");
}
bool StdCmdUndo::isActive(void)
{
return getGuiApplication()->sendHasMsgToActiveView("Undo");
}
void RegisterCommands(void)
{
CommandManager &rcCmdMgr = Application::Instance->commandManager();
rcCmdMgr.addCommand(new StdCmdUndo());
}
/** The Command Macro Standard + isActive() + createAction()
* This macro makes it easier to define a new command.
* The parameters are the class name
* @author Werner Mayer
*/
#define DEF_STD_CMD_AC(X) class X : public Gui::Command \
{\
public:\
X();\
virtual ~X(){}\
virtual const char* className() const\
{ return #X; }\
protected: \
virtual void activated(int iMsg);\
virtual bool isActive(void);\
virtual Gui::Action * createAction(void);\
};
DEF_STD_CMD_AC(StdCmdUndo);
StdCmdUndo::StdCmdUndo()
:Command("Std_Undo")
{
sGroup = QT_TR_NOOP("Edit");
sMenuText = QT_TR_NOOP("&Undo");
sToolTipText = QT_TR_NOOP("Undo exactly one action");
sWhatsThis = "Std_Undo";
sStatusTip = QT_TR_NOOP("Undo exactly one action");
sPixmap = "edit-undo";
sAccel = keySequenceToAccel(QKeySequence::Undo);
eType = ForEdit;
}
void StdCmdUndo::activated(int iMsg)
{
Q_UNUSED(iMsg);
// Application::Instance->slotUndo();
getGuiApplication()->sendMsgToActiveView("Undo");
App::Document* pcDoc=App::GetApplication().getActiveDocument();
//pcDoc->recompute();
pcDoc->flagNeedUpdateBoolean.setValue(0);
doCommand(Command::Gui, "DocumentTools.updateBoolean()");
}
bool StdCmdUndo::isActive(void)
{
return getGuiApplication()->sendHasMsgToActiveView("Undo");
}
void RegisterCommands(void)
{
CommandManager &rcCmdMgr = Application::Instance->commandManager();
rcCmdMgr.addCommand(new StdCmdUndo());
}
/** The Command Macro Standard + isActive() + createAction() * This macro makes it easier to define a new command. * The parameters are the class name * @author Werner Mayer */ #define DEF_STD_CMD_AC(X) class X : public Gui::Command \ {\ public:\ X();\ virtual ~X(){}\ virtual const char* className() const\ { return #X; }\ protected: \ virtual void activated(int iMsg);\ virtual bool isActive(void);\ virtual Gui::Action * createAction(void);\ }; DEF_STD_CMD_AC(StdCmdUndo); StdCmdUndo::StdCmdUndo() :Command("Std_Undo") { sGroup = QT_TR_NOOP("Edit"); sMenuText = QT_TR_NOOP("&Undo"); sToolTipText = QT_TR_NOOP("Undo exactly one action"); sWhatsThis = "Std_Undo"; sStatusTip = QT_TR_NOOP("Undo exactly one action"); sPixmap = "edit-undo"; sAccel = keySequenceToAccel(QKeySequence::Undo); eType = ForEdit; } void StdCmdUndo::activated(int iMsg) { Q_UNUSED(iMsg); // Application::Instance->slotUndo(); getGuiApplication()->sendMsgToActiveView("Undo"); App::Document* pcDoc=App::GetApplication().getActiveDocument(); //pcDoc->recompute(); pcDoc->flagNeedUpdateBoolean.setValue(0); doCommand(Command::Gui, "DocumentTools.updateBoolean()"); } bool StdCmdUndo::isActive(void) { return getGuiApplication()->sendHasMsgToActiveView("Undo"); } void RegisterCommands(void) { CommandManager &rcCmdMgr = Application::Instance->commandManager(); rcCmdMgr.addCommand(new StdCmdUndo()); }
理一下思路:
- 创建一个类,继承commad类。(真懒啊,国外的大佬,创建类都要用宏来实现)。
- 在构造函数中初始化command的信息,主要是action的显示信息以及快捷键等。
- 实现activated函数,把这个命令想要触发的东西都写在这个里面。
最后只需要调用RegisterCommand()函数就可以成功注册命令了。
文章评论