基于VTK的MFC应用程序开发(1)

        提到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文件错误,解决方法已在VTKQt整合的示例章节中说明,这里不再赘述。到此为止,基于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_pImageViewerRender函数开始渲染图像,并监听外部消息实现交互功能。

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

================================================

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片