基于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文件的时候,可以根据需要添加相应的模块。

[plain]  view plain  copy
  1. #----------------------------------------------------------------------------------  
  2.   
  3. cmake_minimum_required( VERSION 2.8 )  
  4.   
  5. project( vtkSDI )  
  6.    
  7. #----------------------------------------------------------------------------------  
  8.   
  9. # 查找并包含VTK工具包  
  10.   
  11. find_package( VTK )  
  12.   
  13. if (VTK_FOUND)  
  14.   
  15.   include (${VTK_USE_FILE})  
  16.   
  17. else (VTK_FOUND)  
  18.   
  19.   message (FATAL_ERROR "Cannot build without VTK. Please set VTK_DIR")  
  20.   
  21. endif( VTK_FOUND )  
  22.   
  23. #----------------------------------------------------------------------------------  
  24.   
  25. # 这里添加本工程的文件  
  26.   
  27. # 主要分为两部分:  
  28.   
  29. # 一是新建的单文档程序中的非工程文件  
  30.   
  31. # 二是用户后续添加的类文件  
  32.   
  33. SET( PROJECT_SRCS  
  34.   
  35.     MainFrm.h  
  36.   
  37.     MainFrm.cpp  
  38.   
  39.     stdafx.h  
  40.   
  41.     stdafx.cpp  
  42.   
  43.     vtkSDI.h  
  44.   
  45.     vtkSDI.cpp  
  46.   
  47.     vtkSDIDoc.h  
  48.   
  49.     vtkSDIDoc.cpp  
  50.   
  51.     vtkSDIView.h  
  52.   
  53.     vtkSDIView.cpp  
  54.   
  55.     targetver.h  
  56.   
  57.     Resource.h  
  58.   
  59.     vtkSDI.rc  
  60.   
  61.     res/vtkSDI.rc2  
  62.   
  63.     res/vtkSDI.ico  
  64.   
  65.     res/vtkSDIDoc.ico  
  66.   
  67.     res/Toolbar.bmp  
  68.   
  69.     res/Toolbar256.bmp  
  70.   
  71.     )   
  72. #----------------------------------------------------------------------------------  
  73.   
  74. # 设置工程包含的vtk模块,这里根据需要加载对应的模块  
  75.   
  76. include("${VTK_DIR}/GUISupport/MFC/VTKMFCSettings.cmake")  
  77.   
  78. set( VTK_LIBS ${vtk_libraries}   
  79.   
  80.               vtkMFC  
  81.   
  82.               vtkIO  
  83.   
  84.               vtkRendering   
  85.   
  86.               vtkGraphics   
  87.   
  88.               vtkHybrid   
  89.   
  90.               vtkFiltering   
  91.   
  92.               vtkCommon   
  93.   
  94.               vtkImaging   
  95.    )  
  96. #----------------------------------------------------------------------------------  
  97.   
  98. ADD_EXECUTABLE( vtkSDI  WIN32  ${PROJECT_SRCS} )  
  99.   
  100. TARGET_LINK_LIBRARIES ( vtkSDI  ${VTK_LIBS} )  
  101. <span style="font-size:14px;"> </span>  

        需要注意的是在倒数第二行中需要加入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,代码如下:

[cpp]  view plain  copy
  1. void CvtkSDIDoc::OnFileOpen()  
  2.   
  3. {  
  4.   
  5.        // TODO: Add your command handler code here  
  6.   
  7.        CString FilePathName;  
  8.   
  9.    
  10.   
  11.        CFileDialog dlg( TRUE, NULL, NULL,  
  12.   
  13.                        OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,  
  14.   
  15.                        (LPCTSTR)_TEXT("JPG Files (*.jpg)|*.jpg|All Files (*.*)|*.*||"),  
  16.   
  17.                        NULL );  
  18.   
  19.    
  20.   
  21.        if(dlg.DoModal()==IDOK)  
  22.   
  23.        {  
  24.   
  25.               FilePathName=dlg.GetPathName();  
  26.   
  27.   
  28.               vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();  
  29.   
  30.               reader->SetFileName(FilePathName.GetBuffer(0));  
  31.   
  32.               reader->Update();  
  33.   
  34.               m_pImageData = reader->GetOutput();  
  35.   
  36.        }  
  37.   
  38.        else  
  39.   
  40.        {  
  41.   
  42.               return;  
  43.   
  44.        }  
  45.   
  46. }  

        下面在CvtkSDIView类中实现图像的显示功能。这里我们使用VTK中的类vtkImageViewer2。该类提供了强大的图像显示功能,可以接受vtkImageData输入,通过设置交互对象vtkRenderWindowInteractor实现用户交互,如窗宽窗位调节,图像放缩等。这里首先在CvtkSDIView头文件中定义vtkImageViewer2对象和窗口交互对象vtkRenderWindowInteractor。

       vtkSmartPointer<vtkImageViewer2>                 m_pImageViewer;

       vtkSmartPointer<vtkRenderWindowInteractor>m_pRenderWindowInteractor;

        然后重载CvtkSDIView类的OnCreate()函数,在该函数中对定义的对象进行初始化,将MFC窗口句柄设置vtkImageViewer2的父窗口ID,以及设置vtkImageViewer2对象与vtkRenderWindowInteractor对象关联:

[cpp]  view plain  copy
  1. int CvtkSDIView::OnCreate(LPCREATESTRUCT lpCreateStruct)  
  2.   
  3. {  
  4.   
  5.     if (CView::OnCreate(lpCreateStruct) == -1)  
  6.   
  7.            return -1;   
  8.   
  9.     // TODO:  Add your specialized creation code here  
  10.   
  11.     m_pImageViewer = vtkSmartPointer<vtkImageViewer2>::New();  
  12.   
  13.     m_pImageViewer->SetParentId(GetSafeHwnd());  
  14.   
  15.    
  16.     m_pRenderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();  
  17.   
  18.     m_pImageViewer->SetupInteractor(m_pRenderWindowInteractor);  
  19.   
  20.     m_pRenderWindowInteractor->Start();  
  21.   
  22.     return 0;  
  23.   
  24. }  

        为了使图像能够居中显示,需要每次在窗口大小改变时,设置m_pImageViewer的大小。这里重载CvtkSDIView类的函数OnSize(),当每次响应该函数时,将当前窗口的客户区域大小设置为m_pImageViewer的大小。

[cpp]  view plain  copy
  1. void CvtkSDIView::OnSize(UINT nType, int cx, int cy)  
  2.   
  3. {  
  4.   
  5.     CView::OnSize(nType, cx, cy);  
  6.   
  7.    
  8.     // TODO: Add your message handler code here  
  9.   
  10.     CRect rect;  
  11.   
  12.     GetClientRect(rect);  
  13.   
  14.     m_pImageViewer->SetSize(rect.Width(), rect.Height());  
  15.   
  16. }  

        最后在OnDraw函数中实现图像渲染。在该函数中,将文档类中读入的图像数据设置vtkImageViewer2的输入,然后调用m_pImageViewerRender函数开始渲染图像,并监听外部消息实现交互功能。

[cpp]  view plain  copy
  1. void CvtkSDIView::OnDraw(CDC* /*pDC*/)  
  2.   
  3. {  
  4.   
  5.     CvtkSDIDoc* pDoc = GetDocument();  
  6.   
  7.     ASSERT_VALID(pDoc);  
  8.   
  9.     if (!pDoc)  
  10.   
  11.            return;  
  12.   
  13.     // TODO: add draw code for native data here  
  14.   
  15.     m_pImageViewer->SetInput(pDoc->m_pImageData);  
  16.   
  17.     m_pImageViewer->Render();  
  18.   
  19. }  

        编译链接后即可执行程序,运行结果如下所示。另外还可以测试交互功能,如按住鼠标右键拖动鼠标或者滑动滚轮,实现图像的放缩功能;按住鼠标左键,拖动鼠标即可实现图像窗宽窗位的调节。

 

图3 程序运行结果

        从程序来看没有编译错误,而且正确显示图像。OK,关闭程序,这时意外出现了,在VS输出窗口中出现许多内存溢出,如下图所示:

 

图4 MFC&VTK程序内存溢出

        这样的内存溢出是因为VTK的DLL先于MFC的DLL加载。因此在CMakeLists.txt中加入下面代码对VTK的DLL进行延迟加载,代码如下:

[plain]  view plain  copy
  1. VTK_MFC_ADD_DELAYLOAD_FLAGS(CMAKE_EXE_LINKER_FLAGS  
  2.   
  3.     vtkMFC.dll  
  4.   
  5.     vtkIO.dll  
  6.   
  7.     vtkRendering.dll  
  8.   
  9.     vtkGraphics.dll  
  10.   
  11.     vtkHybrid.dll  
  12.   
  13.     vtkFiltering.dll  
  14.   
  15.     vtkCommon.dll  
  16.   
  17.     vtkImaging.dll  
  18.   
  19.     )  

 

        添加如上代码后,重新编译工程(rebuild),运行然后关闭工程,观察VS输出窗口中的输出信息,没有内存溢出错误,正常退出。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值