在上一篇博客中我们已经绘制了MFC界面,在这篇博客中我们将添加响应代码,为MFC框架添加一个最基本的功能:打开一个文件夹。
一、添加相关头文件
这里头文件主要包含三类:opencv头文件、批量读取文件相关的头文件、CvvImage。这里需要强调CvvImage这个头文件,这个是用来关联OpenCv和picture控件,并且这个头文件是隶属于OpenCv1.x的,在2.x版本中已经将这个类移除,因此需要手动下载这两个文件(CvvImage.h和CvvImage.cpp),下载地址:[CvvImage](http://download.csdn.net/detail/u013088062/9395566)。下载后将这两个文件放在工程目录下:
![](https://box.kancloud.cn/2016-02-25_56ce6387678e6.png)
然后在VS的解决方案资源管理器窗口中,右击该工程,在快捷菜单中选择“添加->现有项”:
![](https://box.kancloud.cn/2016-02-25_56ce63877e9ea.png)
将这两个文件添加到当前工程中:
![](https://box.kancloud.cn/2016-02-25_56ce63878e06a.png)
添加完成后,可以在代码中添加响应的头文件了,建议将include命令添加在GenderRecognitionMFCDlg.h头文件中:
~~~
#include <iostream>
#include <sstream>
#include <fstream>
#include <opencv2\opencv.hpp>
#include "CvvImage.h"
using namespace std;
using namespace cv;
~~~
这里有两点需要强调:
(1)#include语句包含两种形式,“ #include<> ”和“ #include“” ”。这两种格式的区别在于优先搜索路径的不同,“ #include<> ”默认优先按照系统路径进行搜索,“ #include“” ”默认优先搜索当前的工程目录。
(2)include语句的位置。大型工程中的include语句要注意避免一个重复包含的问题,即要保证每条include语句只执行一次,否则就会出现重定义类型的错误。C++提供两种机制来确保include语句执行的唯一性,一是通过“ifndef”宏来包围include代码块,二是通过“#pragma once”宏来实现,这里默认使用第二种,因此所有的include语句应该位于“#pragma once”语句之后。
二、添加控件响应函数
由于读取显示图片的操作是通过“图像文件夹”按钮来控制的,因此需要为这个按钮添加响应的事件响应函数,方法非常简单,在资源视图窗口中双击对应控件即可,VS将自动添加响应函数:
![](https://box.kancloud.cn/2016-02-25_56ce63879eefc.png)
三、打开文件夹,读取图片路径
接下来为OnBnClickedButtonImagefile()函数添加批量读取图片的代码。我们这里选用SHBrowseForFolder方法。关于这个方法我之前曾写过一篇博客来专门介绍,具体参见[一种批量读取文件的方法——SHBrowseForFolder](http://blog.csdn.net/u013088062/article/details/39137809)。注意一点,在SHBrowseForFolder方法中需要用到dir目录相关的操作函数(如opendir等),这算是Linux的移植版,因此需要借用dirent.h头文件,不过我们已经在之前下载的资源中提供了这个文件,只需依据之前CvvImage文件的配置方法,添加到当前工程中即可:
![](https://box.kancloud.cn/2016-02-25_56ce6387abc62.png)
接下来,向CGenderRecognitionMFCDlg类中添加若干成员变量,用以记录读取的文件夹以及文件的路径及属性,具体需要添加以下四个成员变量,分别是CString m_Path(图像文件路径)、char* m_ImageDir(文件结构)、DIR *m_pDir(目录结构)、struct dirent *m_pEnt(目录结构),这里以m_pEnt为例,介绍VS中添加类成员变量的方法。在类视图中,右击CGenderRecognitionMFCDlg类,在快捷菜单中选择“添加->添加变量”:
![](https://box.kancloud.cn/2016-02-25_56ce6387bd832.png)
在弹出的向导窗口中,指定变量的属性,然后单击“完成”按钮:
![](https://box.kancloud.cn/2016-02-25_56ce6387ce009.png)
同理,添加其他三个成员变量(注意变量类型):
![](https://box.kancloud.cn/2016-02-25_56ce6387ded85.png)
变量添加完成后,即可向OnBnClickedButtonImagefile()函数中添加相应代码:
~~~
/**********初始化变量**********/
CString str; //存储图像路径
BROWSEINFO bi; //用来存储用户选中的目录信息
TCHAR name[MAX_PATH]; //存储路径
ZeroMemory(&bi,sizeof(BROWSEINFO)); //清空目录对应的内存
bi.hwndOwner = GetSafeHwnd(); //得到窗口句柄
bi.pszDisplayName = name;
/**********设置对话框并读取目录信息**********/
BIF_BROWSEINCLUDEFILES;
bi.lpszTitle = _T("Select folder"); //对话框标题
bi.ulFlags = 0x80; //设置对话框形式
LPITEMIDLIST idl = SHBrowseForFolder(&bi); //返回所选中文件夹的ID
SHGetPathFromIDList(idl,str.GetBuffer(MAX_PATH)); //将文件信息格式化存储到对应缓冲区中
str.ReleaseBuffer(); //与GerBuffer配合使用,清空内存
m_Path=str; //将路径存储在m_path中
if(str.GetAt(str.GetLength()-1)!='\\')
m_Path += "\\";
UpdateData(FALSE);
IMalloc * imalloc = 0;
if (SUCCEEDED(SHGetMalloc(&imalloc)))
{
imalloc->Free (idl);
imalloc->Release();
}
/**********获取该路径下的第一个文件**********/
m_ImageDir = (LPSTR)(LPCTSTR)m_Path;
m_pDir = opendir(m_ImageDir);
for (int i = 0; i < 1; i ++) //过滤目录 .. 和 .
{
m_pEnt = readdir(m_pDir);
}
~~~
有关SHBrowseForFolder的方法介绍,除了之前给出的那篇博客之外,这里再推荐两篇更为详细的博客:[文件夹浏览(SHBrowseForFolder)](http://www.cnblogs.com/Clingingboy/archive/2011/04/16/2018284.html)以及[使用SHBrowseForFolder函数打开文件目录对话框](http://www.cppblog.com/franksunny/archive/2010/12/30/137754.html)。
四、简单调试
完成上面那段代码之后,进行一下简单调试。首先按下F7对工程进行编译,在编译过程中注意将调试器版本设置为X64(64位)。编译通过后,设置断点,按下F5进行调试运行。此时单击“图像文件夹”按钮,将弹出打开文件夹对话框:
![](https://box.kancloud.cn/2016-02-25_56ce6387eda2e.png)
选择一个文件夹,然后查看各个变量的情况:
![](https://box.kancloud.cn/2016-02-25_56ce638808adb.png)
可见,m_Path保存了当前选择的文件夹路径。在下一篇博文中我们将通过这个变量来完成对应文件夹目录下图像文件的遍历和显示。