提到MFC一般都不陌生,大部分在校学生使用最多的应该就是它了。但是相对于Qt平台来说,MFC的机制比较复杂。当使用MFC平台进行VTK程序开发时,许多人可能没有头绪。这里结合一个实例讲一下如何在MFC平台下进行VTK程序开发。
这里在MFC下实现一个基于VTK的单文档图像显示程序。通过这个程序,主要演示两个方面:一是怎样使用CMake将MFC程序与VTK结合;二是怎样在MFC程序中调用VTK类实现具体的功能。这个也是基于MFC和VTK进行软件开发的基础,搭好这个框架后,以后的工作就是垒砖了,相信通过前面的学习,垒砖已经小菜一碟了。
在开始代码之前,这里提一下,相信许多人在网上查询这方面的资料的时候,有许多资料会讲到通过MFC中的开发环境(如Visual Studio 2008)设置VTK的包含路径和lib库路径等。但是这样配置对于一般用户来说比较复杂,每次开发新的工程时候,都要查找和设置这些库,既费时费力,又不利于程序的移植(例如换到另外一台机器,如果VTK编译路径不一致的话就会找不到库)。因此这里还是推荐大家使用CMake来管理程序。仅仅几行脚本代码就可以实现VTK库的配置,而且便于程序移植。
现在开始一步步实现VTK和MFC的程序开发。首先建立一个单文档的MFC工程,工程名字为vtkSDI。这里与基于Qt的VTK程序开发稍微有点不同。先建立工程的目的是我们需要把由MFC自动生成的类加入到CMakeLists.txt中,方便管理。MFC单文档工程的建立比较简单,过程不再演示。工程文件如下所示,主要的类为CvtkSDIApp,CMainFrame,CvtkSDIDoc,CvtkSDIView和CAboutDlg五个。由于我们使用CMake来配置和生成工程,因此将工程目录下的工程文件删除,主要是.ncb,.sln,.vcproj,.user文件。
图1 MFC单文档工程的建立时所包含的文件
下面编写CMakeLists.txt文件,将工程文件写入到CMakeLists.txt中并连接VTK动态库。VTK根据功能不同划分了多个不同的模块,每个模块都是一个库。因此在编写CMakeLists.txt文件的时候,可以根据需要添加相应的模块。
#----------------------------------------------------------------------------------
cmake_minimum_required( VERSION 2.8 )
project( vtkSDI )
#----------------------------------------------------------------------------------
# 查找并包含VTK工具包
find_package( VTK )
if (VTK_FOUND)
include (${VTK_USE_FILE})
else (VTK_FOUND)
message (FATAL_ERROR "Cannot build without VTK. Please set VTK_DIR")
endif( VTK_FOUND )
#----------------------------------------------------------------------------------
# 这里添加本工程的文件
# 主要分为两部分:
# 一是新建的单文档程序中的非工程文件
# 二是用户后续添加的类文件
SET( PROJECT_SRCS
MainFrm.h
MainFrm.cpp
stdafx.h
stdafx.cpp
vtkSDI.h
vtkSDI.cpp
vtkSDIDoc.h
vtkSDIDoc.cpp
vtkSDIView.h
vtkSDIView.cpp
targetver.h
Resource.h
vtkSDI.rc
res/vtkSDI.rc2
res/vtkSDI.ico
res/vtkSDIDoc.ico
res/Toolbar.bmp
res/Toolbar256.bmp
)
#----------------------------------------------------------------------------------
# 设置工程包含的vtk模块,这里根据需要加载对应的模块
include("${VTK_DIR}/GUISupport/MFC/VTKMFCSettings.cmake")
set( VTK_LIBS ${vtk_libraries}
vtkMFC
vtkIO
vtkRendering
vtkGraphics
vtkHybrid
vtkFiltering
vtkCommon
vtkImaging
)
#----------------------------------------------------------------------------------
ADD_EXECUTABLE( vtkSDI WIN32 ${PROJECT_SRCS} )
TARGET_LINK_LIBRARIES ( vtkSDI ${VTK_LIBS} )
需要注意的是在倒数第二行中需要加入WIN32,否则生成的工程在编译时会出现错误。第二个需要注意的是,由于工程是基于MFC的,需要连接vtkMFC库。完成CMakeLists.txt文件后,利用CMake设置相应的代码路径(存放工程源代码)和工程路径(存在生成的工程和文件),配置、生成当前工程,得到工程文件vtkSDI.sln。
打开vtkSDI.sln并编译,编译后如无语法和链接错误,则可得到一个可运行的空工程。注意运行时可能会提示找不到dll文件错误,解决方法已在VTK与Qt整合的示例章节中说明,这里不再赘述。到此为止,基于MFC单文档工程的VTK编程框架已经搭建,接下来需要做的在该框架下调用VTK实现相应的功能。下面以图像的读取和显示为例进行说明。
首先为vtkSDI工程添加一个菜单项,通过该菜单项加载图像。打开工程的资源视图Resource View,双击Menu中IDR_MAINFRAME菜单,右键单击FILE菜单下的Open项,为该项添加一个事件Add Event Handler,弹出如下窗口:
图2 添加MFC事件响应向导
这里我们将该菜单的函数响应到CvtkSDIDoc文档类中,消息类型选择为COMMAND,点击Add and Edit按钮,完成消息的响应。该函数的作用是当用户选择该菜单时,弹出图像文件选择对话框,并将用户选择的图像文件加载。为此,我们需要为该类定义一个图像数据对象接收读入的图像数据。在CvtkSDIDoc头文件中添加如下代码,定义一个图像数据的智能指针:
vtkSmartPointer<vtkImageData> m_pImageData;
注意不要忘了添加头文件。在CvtkSDIDoc构造函数中对m_pImageData进行初始化,代码如下:
m_pImageData =vtkSmartPointer<vtkImageData>::New();
m_pImageData =NULL;
然后在菜单响应函数OnFileOpen中,添加读取图像的代码。这里以jpg格式的图像为例进行说。VTK中读取jpg图像的类为vtkJPEGReader,代码如下:
void CvtkSDIDoc::OnFileOpen()
{
// TODO: Add your command handler code here
CString FilePathName;
CFileDialog dlg( TRUE, NULL, NULL,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
(LPCTSTR)_TEXT("JPG Files (*.jpg)|*.jpg|All Files (*.*)|*.*||"),
NULL );
if(dlg.DoModal()==IDOK)
{
FilePathName=dlg.GetPathName();
vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
reader->SetFileName(FilePathName.GetBuffer(0));
reader->Update();
m_pImageData = reader->GetOutput();
}
else
{
return;
}
}
下面在CvtkSDIView类中实现图像的显示功能。这里我们使用VTK中的类vtkImageViewer2。该类提供了强大的图像显示功能,可以接受vtkImageData输入,通过设置交互对象vtkRenderWindowInteractor实现用户交互,如窗宽窗位调节,图像放缩等。这里首先在CvtkSDIView头文件中定义vtkImageViewer2对象和窗口交互对象vtkRenderWindowInteractor。
vtkSmartPointer<vtkImageViewer2> m_pImageViewer;
vtkSmartPointer<vtkRenderWindowInteractor>m_pRenderWindowInteractor;
然后重载CvtkSDIView类的OnCreate()函数,在该函数中对定义的对象进行初始化,将MFC窗口句柄设置vtkImageViewer2的父窗口ID,以及设置vtkImageViewer2对象与vtkRenderWindowInteractor对象关联:
int CvtkSDIView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
m_pImageViewer = vtkSmartPointer<vtkImageViewer2>::New();
m_pImageViewer->SetParentId(GetSafeHwnd());
m_pRenderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
m_pImageViewer->SetupInteractor(m_pRenderWindowInteractor);
m_pRenderWindowInteractor->Start();
return 0;
}
为了使图像能够居中显示,需要每次在窗口大小改变时,设置m_pImageViewer的大小。这里重载CvtkSDIView类的函数OnSize(),当每次响应该函数时,将当前窗口的客户区域大小设置为m_pImageViewer的大小。
void CvtkSDIView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
CRect rect;
GetClientRect(rect);
m_pImageViewer->SetSize(rect.Width(), rect.Height());
}
最后在OnDraw函数中实现图像渲染。在该函数中,将文档类中读入的图像数据设置vtkImageViewer2的输入,然后调用m_pImageViewer的Render函数开始渲染图像,并监听外部消息实现交互功能。
void CvtkSDIView::OnDraw(CDC* /*pDC*/)
{
CvtkSDIDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
m_pImageViewer->SetInput(pDoc->m_pImageData);
m_pImageViewer->Render();
}
编译链接后即可执行程序,运行结果如下所示。另外还可以测试交互功能,如按住鼠标右键拖动鼠标或者滑动滚轮,实现图像的放缩功能;按住鼠标左键,拖动鼠标即可实现图像窗宽窗位的调节。
图3 程序运行结果
从程序来看没有编译错误,而且正确显示图像。OK,关闭程序,这时意外出现了,在VS输出窗口中出现许多内存溢出,如下图所示:
图4 MFC&VTK程序内存溢出
这样的内存溢出是因为VTK的DLL先于MFC的DLL加载。因此在CMakeLists.txt中加入下面代码对VTK的DLL进行延迟加载,代码如下:
VTK_MFC_ADD_DELAYLOAD_FLAGS(CMAKE_EXE_LINKER_FLAGS
vtkMFC.dll
vtkIO.dll
vtkRendering.dll
vtkGraphics.dll
vtkHybrid.dll
vtkFiltering.dll
vtkCommon.dll
vtkImaging.dll
)
添加如上代码后,重新编译工程(rebuild),运行然后关闭工程,观察VS输出窗口中的输出信息,没有内存溢出错误,正常退出。
==========欢迎转载,转载时请保留该声明信息==========
版权归@东灵工作室所有,更多信息请访问东灵工作室
教程系列导航:http://blog.csdn.net/www_doling_net/article/details/8763686
================================================
暂无评论内容