0.前言
第二章中,我们分析了自己创建的PersonalReverse工作台,了解了其Gui模块的基本结构。现在,我们将在PersonalReverse工作台(简称工作台)中,创建一个简易版CmdApproxCCSurface命令【注:完整版该命令可以将Catmull-Clark网格拟合成双五次B样条曲面】
我们先来明确一下简易版CmdApproxCCSurface命令的基本要求:
- 该命令同时存在于工作台的菜单栏和工具栏中
- 该命令只有在有文档激活时,才可以被点击
- 点击该命令后,会检测是否有网格类型对象被选中,
3.1 是:打开TaskDialog
3.2 否:报错:“Please select a mesh.”
【注:该生成过程较为麻烦,第5节会开发一个脚本来处理所有代码生成工作,只需在CMakeLists.txt中略作修改就可以完成开发】
1.外部模块加载
由于该命令需要判断网格,所以我们需要加载Mesh模块,此外,OpenCASCAD也是常用的模块,这里我们将可能用到的模块都加载进来。【注:前后两条分别修改PersonalReverseGui和PersonalReverse项目】
【注:修改后右键CMakeLists.txt编译】
【注:修改后右键这两个项目生成:PersonalReverse,PersonalReverseGui】
PersonalReverseGui/CMakeLists.txt
//在PersonalReverseGui项目下的CMakeLists.txt中修改:
if(MSVC)
add_definitions(-DHAVE_ACOSH -DHAVE_ATANH -DHAVE_ASINH)
else(MSVC)
add_definitions(-DHAVE_LIMITS_H -DHAVE_CONFIG_H)
endif(MSVC)
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR}
${Boost_INCLUDE_DIRS}
${OCC_INCLUDE_DIR}
${COIN3D_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
${XercesC_INCLUDE_DIRS}
)
link_directories(${OCC_LIBRARY_DIR})
set(PersonalReverseGui_LIBS
PersonalReverse
MeshGui
)
// 修改后右键编译CMakeLists.txt
PersonalReverseGui/PreCompiled.h
注意:这是必不可少的步骤,用于说明某些函数是从外部模块导入的
// Importing of App classes
#ifndef GUI_PRECOMPILED_H
#define GUI_PRECOMPILED_H
#include <FCConfig.h>
// Importing of App classes
#ifdef FC_OS_WIN32
# define PersonalReverseAppExport __declspec(dllimport)
# define PersonalReverseGuiExport __declspec(dllexport)
# define PartExport __declspec(dllimport)
# define MeshExport __declspec(dllimport)
# define MeshGuiExport __declspec(dllimport)
# define PointsExport __declspec(dllimport)
# define AppExport __declspec(dllimport)
#else // for Linux
# define PersonalReverseAppExport
# define PersonalReverseGuiExport
#endif
#ifdef _PreComp_
PersonalReverse/CMakeLists.txt
if(MSVC)
add_definitions(-DHAVE_ACOSH -DHAVE_ATANH -DHAVE_ASINH)
else(MSVC)
add_definitions(-DHAVE_LIMITS_H -DHAVE_CONFIG_H)
endif(MSVC)
if (PCL_SURFACE_FOUND AND PCL_FEATURES_FOUND)
add_definitions(-DHAVE_PCL_SURFACE)
if(EXISTS "${PCL_INCLUDE_DIRS}/pcl/surface/on_nurbs/fitting_surface_tdm.h")
add_definitions(-DHAVE_PCL_OPENNURBS)
endif()
endif ()
if (PCL_FILTERS_FOUND AND PCL_FEATURES_FOUND)
add_definitions(-DHAVE_PCL_FILTERS)
endif ()
if (PCL_SEGMENTATION_FOUND AND PCL_FEATURES_FOUND)
add_definitions(-DHAVE_PCL_SEGMENTATION)
endif ()
if (PCL_SAMPLE_CONSENSUS_FOUND)
add_definitions(-DHAVE_PCL_SAMPLE_CONSENSUS)
endif ()
include_directories(
${CMAKE_SOURCE_DIR}/src
${Boost_INCLUDE_DIRS}
${OCC_INCLUDE_DIR}
${PYTHON_INCLUDE_DIRS}
${XercesC_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIR}
${EIGEN3_INCLUDE_DIR}
${PCL_INCLUDE_DIRS}
${FLANN_INCLUDE_DIRS}
)
link_directories(${OCC_LIBRARY_DIR})
set(PersonalReverse_LIBS
Part
Mesh
Points
FreeCADApp
${PCL_COMMON_LIBRARIES}
${PCL_KDTREE_LIBRARIES}
${PCL_FEATURES_LIBRARIES}
${PCL_FILTERS_LIBRARIES}
${PCL_SEARCH_LIBRARIES}
${PCL_SURFACE_LIBRARIES}
${PCL_SEGMENTATION_LIBRARIES}
${PCL_SAMPLE_CONSENSUS_LIBRARIES}
${QT_QTCORE_LIBRARY}
)
SET(PersonalReverse_SRCS
AppPersonalReverse.cpp
PreCompiled.cpp
PreCompiled.h
)
add_library(PersonalReverse SHARED ${PersonalReverse_SRCS})
target_link_libraries(PersonalReverse ${PersonalReverse_LIBS})
SET_BIN_DIR(PersonalReverse PersonalReverse /Mod/PersonalReverse)
SET_PYTHON_PREFIX_SUFFIX(PersonalReverse)
install(TARGETS PersonalReverse DESTINATION ${CMAKE_INSTALL_LIBDIR})
PersonalReverse/PreCompiled.h
#ifndef APP_PRECOMPILED_H
#define APP_PRECOMPILED_H
#include <FCConfig.h>
// Exporting of App classes
#ifdef FC_OS_WIN32
# define PersonalReverseAppExport __declspec(dllexport)
# define PartExport __declspec(dllimport)
# define MeshExport __declspec(dllimport)
# define PointsExport __declspec(dllimport)
#else // for Linux
# define SurfaceBuilderAppExport
#endif
#ifdef _MSC_VER
# pragma warning(disable : 4181)
# pragma warning(disable : 4267)
# pragma warning(disable : 4275)
# pragma warning(disable : 4305)
# pragma warning(disable : 4522)
#endif
// pcl headers include <boost/bind.hpp> instead of <boost/bind/bind.hpp>
#ifndef BOOST_BIND_GLOBAL_PLACEHOLDERS
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#endif
#ifdef _PreComp_
2.CmdApproxCCSurface创建与加载
- 创建CmdApproxCCSurface命令
【注:这里包含了一些可能用到的模块】
【注:在activated()函数中添加了对网格对象的判断】
// Command.cpp
#include "PreCompiled.h"
#ifndef _PreComp_
#include <QApplication>
#include <QMessageBox>
#include <BRep_Builder.hxx>
#include <BRepBuilderAPI_MakePolygon.hxx>
#include <TopoDS_Compound.hxx>
#endif
#include <sstream>
#include <Mod/Part/App/TopoShape.h>
#include <Mod/Part/App/PartFeature.h>
#include <Mod/Part/App/FaceMakerCheese.h>
#include <Mod/Part/App/Geometry.h>
#include <Mod/Part/App/Tools.h>
#include <Mod/Points/App/Structured.h>
#include <Mod/Mesh/App/Mesh.h>
#include <Mod/Mesh/App/MeshFeature.h>
#include <Mod/Mesh/App/Core/Approximation.h>
#include <Mod/Mesh/App/Core/Algorithm.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObjectGroup.h>
#include <Gui/Application.h>
#include <Gui/Command.h>
#include <Gui/Control.h>
#include <Gui/MainWindow.h>
#include <Gui/FileDialog.h>
#include <Gui/Selection.h>
#include <Base/Builder3D.h>
#include <Base/CoordinateSystem.h>
#include <Base/Converter.h>
#include <Base/Tools.h>
#include "TaskApproxCCSurface.h"
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//===========================================================================
// CmdPersonalReverseTest THIS IS JUST A TEST COMMAND
//===========================================================================
DEF_STD_CMD_A(CmdApproxCCSurface)
CmdApproxCCSurface::CmdApproxCCSurface()
:Command("PersonalReverse_ApproxCCSurface")
{
sAppModule = "PersonalReverse";
sGroup = QT_TR_NOOP("PersonalReverse");
sMenuText = QT_TR_NOOP("Approximate CC surface...");
sToolTipText = QT_TR_NOOP("Approximate a B-spline surface");
sWhatsThis = "PersonalReverse_ApproxCCSurface";
sStatusTip = sToolTipText;
sPixmap = "actions/ApproxCCSurface";
}
void CmdApproxCCSurface::activated(int)
{
App::DocumentObjectT objT;
std::vector<App::DocumentObject*> obj = Gui::Selection().getObjectsOfType(App::GeoFeature::getClassTypeId());
if (obj.size() != 1 || !obj.at(0)->isDerivedFrom(Mesh::Feature::getClassTypeId())) {
QMessageBox::warning(Gui::getMainWindow(),
qApp->translate("PersonalReverse_ApproxCCSurface", "Wrong selection"),
qApp->translate("PersonalReverse_ApproxCCSurface", "Please select a mesh.")
);
return;
}
objT = obj.front();
Gui::Control().showDialog(new PersonalReverseGui::TaskApproxCCSurface(objT));
}
bool CmdApproxCCSurface::isActive(void)
{
return (hasActiveDocument() && !Gui::Control().activeDialog());
}
void CreatePersonalReverseCommands(void)
{
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.addCommand(new CmdApproxCCSurface());
}
- 加载到菜单栏和工具栏
在workbench.cpp中的菜单栏和工具栏中都添加:
*test << "PersonalReverse_ApproxCCSurface";
此时,我们创建了CmdApproxCCSurface命令。这里注意activated()
函数最后被注释的一行,后续会通过该命令调用打开TaskDialog对话框
3.TaskApproxCCSurface
- 右键项目添加项
- 选择Qt Widgets Class
- 注意将位置改成源码中的对应文件夹
- BaseClass改为QWidget
- ui_TaskApproxCCSurface.h文件生成
打开刚刚生成文件的目录,打开终端,运行以下命令:
uic TaskApproxCCSurface.ui -o ui_TaskApproxCCSurface.h
运行后,目录里多了ui_TaskApproxCCSurface.h
文件
下面将其加入CMakeLists.txt中
- 设置Dialogs_UIC_SRCS
set(PersonalReverseGui_MOC_HDRS
TaskApproxCCSurface.h
)
fc_wrap_cpp(PersonalReverseGui_MOC_SRCS ${PersonalReverseGui_MOC_HDRS})
SOURCE_GROUP("Moc" FILES ${PersonalReverseGui_MOC_SRCS})
set(Dialogs_UIC_SRCS
TaskApproxCCSurface.ui
)
if(BUILD_QT5)
qt5_wrap_ui(Dialogs_UIC_HDRS ${Dialogs_UIC_SRCS})
else()
qt4_wrap_ui(Dialogs_UIC_HDRS ${Dialogs_UIC_SRCS})
endif()
SET(Dialogs_SRCS
${Dialogs_UIC_HDRS}
${Dialogs_UIC_SRCS}
TaskApproxCCSurface.h
TaskApproxCCSurface.cpp
)
SOURCE_GROUP("Dialogs" FILES ${Dialogs_SRCS})
// SET(SurfaceBuilderGui_SRCS)下应有这两项:
${Dialogs_SRCS}
Resources/PersonalReverse.qrc
右键CMakeLists.txt编译,发现Dialogs筛选器中多了TaskApproxCCSurface相关内容
4.自动生成内容修改
TaskApproxCCSurface文件中将包含两个类:
class TaskApproxCCSurface:该类继承自Gui::TaskView::TaskDialog,用于打开TaskDialog对话框
class ApproxCCSurfaceWidget:该类用于真正响应命令,调用App中对应的函数
- 修改TaskApproxCCSurface.h/.cpp
- cpp文件最后的
#include "moc_TaskApproxCCSurface.cpp"
必不可少(我们有添加QT的宏命令,生成后会生成该文件,该文件会处理QT中的一些命令) - 类的定义均在命名空间SurfaceBuilderGui下
// TaskApproxCCSurface.h:
#ifndef PERSONALREVERSEGUI_TASKAPPROXCCSURFACE
#define PERSONALREVERSEGUI_TASKAPPROXCCSURFACE
#include <Gui/TaskView/TaskView.h>
#include <Gui/TaskView/TaskDialog.h>
#include <App/DocumentObserver.h>
namespace PersonalReverseGui {
class ApproxCCSurfaceWidget : public QWidget
{
Q_OBJECT
public:
ApproxCCSurfaceWidget(const App::DocumentObjectT&, QWidget* parent = 0);
~ApproxCCSurfaceWidget();
bool accept();
private:
class Private;
Private* d;
};
class TaskApproxCCSurface : public Gui::TaskView::TaskDialog
{
Q_OBJECT
public:
TaskApproxCCSurface(const App::DocumentObjectT&);
~TaskApproxCCSurface();
public:
void open();
bool accept();
QDialogButtonBox::StandardButtons getStandardButtons() const
{
return QDialogButtonBox::Ok | QDialogButtonBox::Cancel;
}
private:
ApproxCCSurfaceWidget* widget;
Gui::TaskView::TaskBox* taskbox;
};
}
#endif // PERSONALREVERSEGUI_TASKAPPROXCCSURFACE
// TaskApproxCCSurface.cpp
#include "PreCompiled.h"
#ifndef _PreComp_
# include <algorithm>
# include <QMessageBox>
# include <QTextStream>
#endif
#include "TaskApproxCCSurface.h"
#include "ui_TaskApproxCCSurface.h"
#include <Gui/Application.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Command.h>
#include <Gui/Document.h>
#include <Gui/Selection.h>
#include <Gui/ViewProvider.h>
#include <Gui/WaitCursor.h>
#include <Base/Interpreter.h>
#include <Base/Converter.h>
#include <Base/CoordinateSystem.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/Placement.h>
#include <Mod/Mesh/App/Core/Approximation.h>
using namespace PersonalReverseGui;
/* SurfaceBuilderGui::ApproxCCSurfaceWidget */
class ApproxCCSurfaceWidget::Private
{
public:
Ui_ApproxCCSurfaceWidget ui;
App::DocumentObjectT obj;
Private()
{
}
~Private()
{
}
};
ApproxCCSurfaceWidget::ApproxCCSurfaceWidget(const App::DocumentObjectT& obj, QWidget* parent)
:d(new Private())
{
Q_UNUSED(parent);
d->ui.setupUi(this);
d->obj = obj;
}
ApproxCCSurfaceWidget::~ApproxCCSurfaceWidget()
{
delete d;
}
bool ApproxCCSurfaceWidget::accept()
{
return true;
}
/* SurfaceBuilderGui::TaskApproxCCSurface */
TaskApproxCCSurface::TaskApproxCCSurface(const App::DocumentObjectT& obj)
{
widget = new ApproxCCSurfaceWidget(obj);
taskbox = new Gui::TaskView::TaskBox(
Gui::BitmapFactory().pixmap("actions/CompleteWorkbench"),
widget->windowTitle(), true, 0);
taskbox->groupLayout()->addWidget(widget);
Content.push_back(taskbox);
}
TaskApproxCCSurface::~TaskApproxCCSurface()
{
}
void TaskApproxCCSurface::open()
{
}
bool TaskApproxCCSurface::accept()
{
return widget->accept();
}
#include "moc_TaskApproxCCSurface.cpp"
- 修改TaskApproxCCSurface.ui
- 由于我们修改了类的定义,需要修改.ui文件,双击打开后,设置属性-ObjectName为:
PersonalReverseGui::ApproxCCSurfaceWidget
- 由于
ui_TaskApproxCCSurface.h
文件在源码文件夹下,直接编译TaskApproxCCSurface.ui
并不起作用,所以重复之前调用uic命令,生成新的ui_TaskApproxCCSurface.h
- 调用该对话框
回到我们的Command.cpp文件中,在void CmdApproxCCSurface::activated(int)
中调用我们的对话框
至此,我们完成了简易版CmdApproxCCSurface的开发,如下:
5. 脚本生成
c++脚本:GenFreeCADTaskClass
该脚本的输入参数为:NameSpace(这里是PersonalReverseGui)和DefaultClassName(这里是ApproxSingleSurface),调用该脚本后会生成以下三个文件:
TaskApproxSingleSurface.h
TaskApproxSingleSurface.cpp
TaskApproxSingleSurface.ui
你仍需要做的事情:
1.将文件拷贝到正确的目录
2.调用uic命令生成ui_TaskApproxSingleSurface.h文件
3.在CMakeLists.txt中添加该类
6.总结
完成简易版CmdApproxCCSurface的开发后,我们将初步探索App模块。App模块和Gui模块使得程序的算法实现和现实分离,那么你可能会好奇:这两块是怎么联系起来的呢?
你可能会注意到,在第1小节的外部模块加载中,我们不仅修改了Gui项目(PersonalReverseGui)下面的文件,还对App项目(PersonalReverse)下的文件进行了修改,这是因为,Gui模块会调用相应App模块中的算法,我们在Gui中导入了Mesh等外部模块,也要相应的在App中进行修改,这样才能完整的加载某一模块。
下一章节,我们将会开发进阶版CmdApproxCCSurface,该命令将会调用PersonalReverse中的命令,并进行参数的传递:从Gui传参给App模块,并接受App模块的返回值