TrafficAssistSys——基于VS2013_MFC

/**

*  2015-7-8

*  SD Yang

*  博客园地址:http://www.cnblogs.com/atcodemonkey/

*  新浪微博: http://weibo.com/atCodeMonkey/

*  个人主页:   http://atcodemonkey.sinaapp.com/

* */

  • 概要

实训项目8:城市道路出行路线辅助系统  (2周)

题目要求:

(一)实训目的

通过该实验,使掌握windows程序设计的基本方法。了解地理信息系统的基本原理,采用地图拓扑结构建模以及最短路径搜索算法,搜索简单地图中的路径,运用图形绘制、位图显示等技术,动态展现最短路径及车辆在最短路径上的移动,熟练使用定时器、文件读写等接口,完成路线选择和出行预览。通过处理过程对计算机软件系统工作原理的进一步理解,促进对面向对象概念的系统理解以及面向对象方法的应用。

(二)实训要求

1、基本要求

(1)载入地图的位图;//载入各种格式的图片

(2)在地图中标记节点和路线,并保存节点、路线形成的路网拓扑结构;

(3)再现路网拓扑结构;

(4)修改和保存路网拓扑结构的距离权重;

(5)选择出行的起点和终点,显示最短路径;

(6)动画演示车辆在最短路径上的行驶过程。

2、发挥部分

(1)可以载入多个地图,存储多个路网的拓扑结构;

(2)管理拓扑结构中的权重,包括距离权重和行程时间权重;

(3)在同一张地图上,同时行驶多辆车;

(4)车辆行驶的仿真过程可以调整速度;

(5)其它自行设计的功能。

(三)评分标准


项 目


主要内容


满 分


设计

报告


需求分析


需要的功能列表及操作要求


5


界面设计


组件的布局及正确应用


5


算法设计


画出流程图


5


代码规范性


符合匈牙利命名法规则


2


调试与分析


调试中出现的异常的分析


5


面向对象概念的应用


程序设计中建立模型时对面向对象方法中的封装性、继承性、多态性的应用实例说明


5


实验报告结构及规范性


符合附件中的结构和规范要求


3


总分


30


基本要求


基本要求实现


35


发挥

部分


完成第(1)项


8


完成第(2)项


8


完成第(3)项


8


完成第(4)项


5


其他


6


总分


35

PS:关于匈牙利命名法 见林锐 《高质量C语言编程指南》

http://wenku.baidu.com/link?url=k1u8LjZ_2Mb2xSQYkV5m8kiRseYBLqOp3OV8lzWvXxnmZERk-tARULdnCZkLLVlDapZKPAYNjAbZa2B3MIopJ4tos3IFyQsUyKM8vjTiREW

  • 方案确定及论证

先看一下几个主流的地图软件

①Google地图/Google地球,由于受到天朝墙的限制用的人已经不多了。

②Baidu地图 附加一个网址http://map.baidu.com/

       以下是Baidu地图的效果图

       

  由上图中可以看出,即使是国内互联网巨头的团队仍然采用结点的方法来开发导航。由微积分的思想,弯道就是很多个结点。

  对于现在的道路,大多都是双向道路,所以用无向图表示,根据迪杰斯特拉算法得到最短路径矩阵,经过变换得到最短路径,然后采用可视化界面的方式将其表示出来。

  • 程序流程图及关键代码

系统环境:Windows 8.1 pro

编译环境:Visual Studio 2013MFC

编码语言:C++

博客代码编写:Notepad++

考核得分:及格(优秀/良好/及格/不及格)

软件实习报告编写:TexLab Word2013 Visio2013 //Prezi演示文稿

算法验证软件:Matlab2015a

  • 程序UI测试及功能展示
  • 感悟与问题

计算机程序设计综合实验报告

城市道路出行路线辅助系统

2015年 07月 09日

目录

一.系统名称... 1

二.系统功能说明... 1

三.系统设计... 7

四.系统流程图... 9

五.重点算法说明... 10

1.迪杰斯特拉算法:... 10

(1).dijkstra算法简介... 11

(2).dijkstra算法基本过程... 11

(2).dijkstra算法的流程图如下所示:... 14

2.图形界面与邻接矩阵的交换:... 16

3.地图图片与拓扑结构保存的绑定:... 16

六.设计过程遇到的问题及解决办法、解决效果... 20

1.在MFC对话框中添加状态栏... 20

2.获取位图尺寸... 22

3.设置自己的图标... 23

4.MFC按钮控件的美化... 25

VC下加载多种格式图片的方法总结... 28

5.定位程序运行目录... 34

6.MFC执行流程... 34

七.对程序设计的认识... 37

八.自己编写的程序代码... 38

1.CNode类... 38

Node.h. 38

Node.cpp. 39

2.CEdge类... 40

Edge.h. 40

Edge.cpp. 41

3.CRoute类... 42

Route.h. 42

Route.cpp. 43

4.CGraph类... 45

Graph.h. 45

Graph.cpp. 46

5.CSplashThread类... 49

SplashThread.h. 49

SplashThread.cpp. 50

一.系统名称

城市道路出行路线辅助系统

(Traffic Assist System)

二.系统功能说明

城市道路出行路线辅助系统(Traffic Assist System),是计算机科学在地理信息系统(Geographic Information SystemGeoInformation systemGIS)领域的有机应用。利用计算机高效便捷的特点来处理地理信息系统中复杂的问题是好的选择。本系统采用地图拓扑结构建模,使用迪杰斯特拉(Dijkstra)最短路径搜索算法,搜索简单地图中的路径。本系统应用范围广泛,用户可自定义自己的地图,大到世界地图,小大家庭的范围,都可以通过自定义拓扑结构在实现搜索权值最小的目的。本系统运用图形绘制、位图显示、多格式文本显示等技术,动态的、直观的展现最短路径及车辆在最短路径上的移动。本系统采用最新的成熟VS2013编码,使用兼容性较好的Windows版本API接口,使得程序在兼容性和扩展性方面有着先天优势,较VC6写出的程序,本系统不仅可以Windows 8及以上的系统中运行,而且在目前PC中占有绝对优势的Windows 7系统中完美运行,使得本系统具有良好的推广性和实用价值。本系统中大量使用了定时器、文件读写等接口,实现了路线选择和出行预览等功能。完全采用了面向对象的方法进行编码,在用户体验方面也做了较好的处理。本系统通过动态的加载外部图片、数据文件来进行操作,具有可更新性和可升级性。

具体的功能说明:

l  闪屏(Splash)

首先,虽然闪屏没有成为一个软件技术规范,可是纵观一个有规模的程序,都是在程序的开始闪屏,闪屏的目的大多有以下两种:第一,为了美观性,让用户了解编码团队或者进行宣传;第二,功能性,程序本身较大,需要加载大量的资源文件,创造良好的用户体验。本系统采用的闪屏,综合以上两点考虑。

美观性考虑:本系统使用的闪屏美观且说明接受反馈所使用的博客。例如:

功能性考虑:由于本程序的大多资源是在程序运行中动态加载的,而加载时需要时间的,所以闪屏采用了多线程编程的思路,加载闪屏的同时,进程加载了资源同时获取了程序运行的路径,为后面的操作实现可能。

PS:程序每次运行都会检测到所运行的程序地址,并以此为地址的参考,确定其他文件夹的地址,从响应的文件夹获取相应的资源,例如用splash文件夹获取闪屏,获取闪屏的时候有个优先级,.gif>.jpg>.bmp,若都不存在则不显示,添加了容错机制,避免程序无法进入。

l  皮肤加载

皮肤加载使用了许多年前的Skin++付费皮肤,可以通过.ini文件保存用户的皮肤选择,目前用做UI设计的比较好的方案还是Qt+UI皮肤库。或者DirectUI(类似于AndroidUI驱动,通过加载.xml文件来实现),作为一款简单的软件,大道至简,mfc本身的系统风格完全能够满足使用需求,故本程序至提供了有限的且不可用户自定义的皮肤选择。

l  功能选区

功能选区采用TabCtrl选项卡的形式呈现,有效的利用了用户珍贵的屏幕资源。

l  创建新地图

通过菜单,导入新地图选项。

用CFileDialog对话框选择好响应的图片后

为新建的地图起名字,在程序目录下的map文件夹下会有相应的图片和保存道路拓扑结构文件。

l  加载已存在地图

通过菜单选项,打开文件。打开已经保存的地图图片文件,或者保存拓扑结构的文件,打开其中一个,另一个会绑定的加载到程序内存中。加载后可进行所有的操作。

l  最优路线选择

l  最优路线保存分享

当查询好本次最优路线后,可以通过图片的方式将其保存。编辑-复制图像-选择相应的路径。

l  链接到百度地图

l  用户反馈(Feedback)

用户可以通过 帮助-打开产品主页 到笔者的微博反馈程序使用中的问题。

三.系统设计

界面设计

  • MFC风格
  • Skin++

数据库设计

暂无

算法设计

数据结构的选择


存储结构


优点


缺点


邻接矩阵


算法显得简单明了


权值无法扩展,存储时麻烦


邻接表


存储简单明了,无论一个结点有几个属性都很简单的读取和存储


算法还是要归结到邻接矩阵,此时就要加一个返回在矩阵中位置的函数

本程序采用的为邻接矩阵存储结构

结构的声明在Matrix.h中,详情如下:

typedef int T;  //将int别名于T,当费用出现乘小数时,当float待之

typedef struct graph{

T NoEdge;  //表示没有路径,本程序用# 65536 #

int Vertices;      //图中的顶点数

T** A;       //本程序采用动态生成二维数组,对不同的地图,消耗的内存量不同

}Graph;

算法原理阐述

按路径长度递增次序产生算法:

把顶点集合V分成两组:

1)   S:已求出的顶点的集合(初始时只含有源点V0)

2)   V-S=T:尚未确定的顶点集合

将T中顶点按递增的次序加入到S中,保证:

1)   从源点V0到S中其他各顶点的长度都不大于从V0到T中任何顶点的最短路径长度

2)   每个顶点对应一个距离值

S中顶点:从V0到此顶点的长度

T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度

依据:可以证明V0到T中顶点Vk的,或是从V0到Vk的直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和

反证法可证)

求最短路径步骤

算法步骤如下:

G={V,E}

  1. 初始时令 S={V0},T=V-S={其余顶点},T中顶点对应的距离值

u  若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值

u  若不存在<V0,Vi>,d(V0,Vi)为∞

  1. 从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中
  2. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值

重复上述步骤2、3,直到S中包含所有顶点,即W=Vi为止

四.系统流程图

五.重点算法说明

1.迪杰斯特拉算法:

以下是代码思路说明后有结合本程序应用:

(1).dijkstra算法简介

Dijkstra算法是由E.W.Dijkstra于1959年提出,又叫迪杰斯特拉算法,它应用了贪心算法模式,是目前公认的最好的求解最短路径的方法。算法解决的是有向图中单个源点到其他顶点的最短路径问题,其主要特点是每次迭代时选择的下一个顶点是标记点之外距离源点最近的顶点。但由于dijkstra算法主要计算从源点到其他所有点的最短路径,所以算法的效率较低。

(2).dijkstra算法基本过程

假设路网中每一个节点都有标号 是从出发点s到点t的最短路径长度;表示从s到t的最短路径中t点的前一个点。求解从出发点s到点t的最短路径算法的基本过程为:

1.      初始化。出发点设置为:

标记起源点s,记k = s,其他所有点设为未标记。

2.      检验从所有已标记的点k到其他直接连接的未标记的点j的距离,并设置:

3.      选取下一个点。从所有未标记的点中选取 最小的点i,点i被选为最短路径中的一点,并设为已标记的。

4.      找到点i的前一点。从已经标记的点集合中找到直接连接到点i的点,并标记为 。

5.      标记点i。如果所有的点已标记,则算法结束。否则,记k = i,转到2继续。

从以上算法的步骤中可以看出 :dijkstra算法的关键部分是从未标记的点中不断地找出距离源点距离最近的点,并把改点加入到标记的点集合中,同时更新未标记的点集合中其余点到起始点的最短估计距离[z1] 。

以一个带有权值的无向图为例,用dijkstra算法分析从源点A到目标点F的最短路径。

1. 用带有权值的一个矩阵w表示含有n各节点的带权无向图, 代表弧段 的权值,如果从节点 到节点 不连通,那么 ,带权值图邻接矩阵如下图所示.设置A为源点,G为目的点, 代表从节点A到有向图中其他节点 的最短路径长度。设置初始值 代表标记的节点集合。

2. 是从A点出发求出的一条最短路径上的终止节点,令 ;

3.  修改起始节点A到集合之间的最短路径的长度值,如果d(j)+w(j,k) < d(k),那么d(k) = d(j) + w(j,k);

4. 重复步骤2、3的操作N-1次,最终得到从起始节点A到其他节点的最短路径,按照递增的顺序排列路径的长度。

(2).dijkstra算法的流程图如下所示:

2.图形界面与邻接矩阵的交换:

void CGraph::UpdateGraph(CGraph *myGraph, CRoute *myRoute)

{

//将两个全局变量引入程序中

//extern CGraph* myGraph;

//extern CGraph* myRoute;

//myRoute中的结点数,就是图中的结点数

int nodeCount = (myGraph->Vertices) = (myRoute->myNode.GetCount());

//图从myRoute中获取信息初始化

myGraph->CreateGraph(myGraph, nodeCount, 65536);

//得到图中一共有多少个结点

int edgeCount = (myRoute->myEdge.GetCount());

//为图添加边

int startNodeID;

int endNodeID;

int edgeWeight;

for (int i = 0; i < edgeCount; i++)

{

startNodeID = myRoute->myEdge.GetAt(myRoute->myEdge.FindIndex(i)).GetStartNode().GetID();

endNodeID = myRoute->myEdge.GetAt(myRoute->myEdge.FindIndex(i)).GetEndNode().GetID();

edgeWeight = myRoute->myEdge.GetAt(myRoute->myEdge.FindIndex(i)).GetWeight();

CGraph::Add(myGraph, startNodeID, endNodeID, edgeWeight);

//因为是无向图片

CGraph::Add(myGraph, endNodeID, startNodeID,edgeWeight);

}

}

3.地图图片与拓扑结构保存的绑定:

void Cpage2::OnBnClickedCommandQutiwork()

{

// TODO:  在此添加控件通知处理程序代码

// 设置标志位为假,不能再在屏幕上画点

// 并且要完成保存当前路径

IsDoing = false;

CFile myFile;

/*

CString m_strFileName;

CFileDialog myFiledlg(false);

int nSelect = myFiledlg.DoModal();

if (IDOK == nSelect)

m_strFileName = myFiledlg.GetPathName();

else

return;

*/

extern CString mapFileName;

//myFile.Open(m_strFileName, CFile::modeCreate | CFile::modeReadWrite);

myFile.Open(mapFileName, CFile::modeCreate | CFile::modeReadWrite);

extern CRoute* myRoute;

int tempNodeNumber = myRoute->GetNodeNumber();

myFile.Write(&tempNodeNumber, sizeof(int));

int tempEdgeNumber = myRoute->GetEdgeNumber();

myFile.Write(&tempEdgeNumber, sizeof(int));

for (int i = 0; i < myRoute->GetNodeNumber(); i++)

{

CNode *myNode = new CNode;

int iD = myRoute->myNode.GetAt(myRoute->myNode.FindIndex(i)).GetID();

CPoint point = myRoute->myNode.GetAt(myRoute->myNode.FindIndex(i)).GetPoint();

myNode->SetID(iD);

myNode->SetPoint(point);

myFile.Write(myNode, sizeof(CNode));

}

for (int i = 0; i < myRoute->GetEdgeNumber(); i++)

{

CEdge *myEdge = new CEdge;

CNode startNode = myRoute->myEdge.GetAt(myRoute->myEdge.FindIndex(i)).GetStartNode();

CNode endNode = myRoute->myEdge.GetAt(myRoute->myEdge.FindIndex(i)).GetEndNode();

int weight = myRoute->myEdge.GetAt(myRoute->myEdge.FindIndex(i)).GetWeight();

myEdge->SetStarNode(startNode);

myEdge->SetEndNode(endNode);

myEdge->SetWeight(weight);

myFile.Write(myEdge, sizeof(CEdge));

}

myFile.Close();

}

// Cpage0 消息处理程序

void Cpage0::OnBnClickedCommandFindpath()

{

// TODO:  在此添加控件通知处理程序代码

UpdateWindow();

if(pp.IsEmpty())

{ }

else

{

pp.RemoveAll();

}

extern CGraph* myGraph;

extern CRoute* myRoute;

myGraph->UpdateGraph(myGraph, myRoute);//将图形界面的信息与邻接矩阵联系

UpdateData(true);

int nodeCount = myRoute->GetNodeNumber();

int startNodeID = m_nStartNodeID;

int endNodeID = m_nEndNodeID;

int *d = new int[nodeCount];

int *path = new int[nodeCount];

myGraph->Dijkstra(*myGraph, startNodeID, d, path);//BadInpute

int v = endNodeID;

extern CList<int, int&>pp;

if (-1 == path[v] )

{

MessageBox(_T("No Way"));

}

else

{

while (path[v] != -1)

{

pp.AddHead(v);

v = path[v];

}

pp.AddHead(startNodeID);

pp.AddTail(endNodeID);

for (int i = 0; i < pp.GetCount() - 1; i++)

{

CDC *pDC = GetParent()->GetParent()->GetDC();

CBrush *oldBrush, myBrush;

myBrush.CreateSolidBrush(RGB(100, 200, 100));

CPen *oldPen, myPen;

//myPen.CreatePen(PS_SOLID, 1, RGB(200, 0, 0));//wingdi.h

//myPen.CreatePen(PS_SOLID, 5, RGB(200, 0, 0));//wingdi.h

COLORREF myPathColor = m_oColorPath.GetColor();//获得颜色

myPen.CreatePen(PS_SOLID, 5, myPathColor);//wingdi.h

oldBrush = pDC->SelectObject(&myBrush);

oldPen = pDC->SelectObject(&myPen);

pDC->SetBkColor(RGB(200, 200, 200));

pDC->SetTextColor(RGB(100, 100, 100));

pDC->MoveTo(myRoute->myNode.GetAt(myRoute->myNode.FindIndex(pp.GetAt(pp.FindIndex(i)))).GetPoint());

pDC->LineTo(myRoute->myNode.GetAt(myRoute->myNode.FindIndex(pp.GetAt(pp.FindIndex(i + 1)))).GetPoint());

//CString strText("Ok");

//pDC->TextOut(point.x, point.y, strText);

pDC->SelectObject(oldBrush);

pDC->SelectObject(oldPen);

myBrush.DeleteObject();

myPen.DeleteObject();

}

}

delete d;

delete path;

}

六.设计过程遇到的问题及解决办法、解决效果

1.在MFC对话框中添加状态栏

如果我们想实现在MFC对话框中添加状态栏显示,如何例如分状态栏为两列,第一列显示鼠标的当前位置,第二列显示当前的时间,(如上图)。

1. 首先,打开在资源视图的String Table并添加两个ID:ID_INDICATOR_NISH 和ID_INDICATOR_TIME,如下图

2. 在该对话框的头文件中添加一个CStatusBar类对象

  1. CStatusBar m_bar;

3. 打开该对话框的cpp文件,并在最顶端添加以下代码:

  1. static UINT BASED_CODE indicators[]=
  2. {
  3. ID_INDICATOR_NISH,
  4. ID_INDICATOR_TIME
  5. };

4. 接下来创建状态栏,在OnInitDialog()函数如下代码:

  1. <pre name="code" class="cpp"><span style="font-size:16px;">m_bar.Create(this);//创建状态栏
  2. m_bar.SerIndicators(indicators, sizeof(indicators)/sizeof(UINT)); //设置状态栏数目
  3. CRect rect;
  4. GetClientRect(&rect);
  5. //设置各栏长度
  6. m_bar.SetPaneInfo(0, ID_INDICATOR_NISH, SBPS_NORMAL, rect.Width()-100); m_bar.SetPaneInfo(1, ID_INDICATOR_TIME, SBPS_STRETCH, 0); //在ping屏幕上绘制状态栏</span>
  7. RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, ID_INDICATOR_TIME);
  8. 5. 至此,对话框上的状态栏创建已经成功了。若是用户喜欢,可以单独设置状态栏背景颜色,</span>
  9. 可以在OnInitDialog()函数中添加代码:
  10. m_bar.GetStatusBarCtrl().SetBKColor(RGB(180,180,180));
  11. 6. 添加时钟显示。首先在OnInitDialog()中添加
  12. SetTimer(100,1000,NULL);
  13. 之后添加WM_TIMER的相应函数:
  14. void CDlgStatusBarDlg::OnTimer(UINT nIDEvent)
  15. {
  16. if(nIDEvent==100)
  17. {
  18. CTime t1;
  19. t1=CTime::GetCurrentTime();
  20. m_bar.SetPaneText(1,t1.Format("%H:%M:%S"));
  21. }
  22. CDialog::OnTimer(nIDEvent);
  23. }
  24. 7. 添加XY坐标显示。重写函数OnMouseMove():
  25. void CDlgStatusBarDlg::OnMouseMove(UINT nFlags, CPoint point)
  26. {
  27. CString s;
  28. s.Format("X=%d Y=%d",point.x,point.y);
  29. m_bar.SetPaneText(0,s);
  30. CDialog::OnMouseMove(nFlags, point);
  31. }
  32. 至此,编译运行程序,就可以看到预期的效果了。

2.获取位图尺寸

从 CBitmap类对象中获取位图尺寸我们可用GetBitmap()函数 。

// 变量bitmap是一个CBitmap类对象

BITMAP bm;

bitmap.GetBitmap( &bm );

bmWidth = bm.bmWidth;

bmHeight = bm.bmHeight;

如果你有一个 HBITMAP句柄,你可以将它附加到一个CBitmap类对象上,再用上述方法获取尺寸。

// 变量hBmp是一个HBITMAP句柄

BITMAP bm;

::GetObject( hBmp, sizeof( bm ), &bm );

bmWidth = bm.bmWidth;

bmHeight = bm.bmHeight;

从BMP位图文件中获取位图尺寸可用下述方法。

CFile file;

// sBMPFileName是BMP位图文件名

if( !file.Open( sBMPFileName, CFile::modeRead) )

return ;

BITMAPFILEHEADER bmfHeader;

// 读文件头

if (file.Read((LPSTR)&bmfHeader, sizeof(bmfHeader))

!= sizeof(bmfHeader))

return ;

// 确定文件类型标记‘BM‘

if (bmfHeader.bfType != ((WORD) (‘M‘ << 8) | ‘B‘))

return ;

BITMAPINFOHEADER bmiHeader;

if (file.Read((LPSTR)&bmiHeader, sizeof(bmiHeader))

!= sizeof(bmiHeader))

return ;

int bmWidth = bmiHeader.biWidth;

int bmHeight = bmiHeader.biHeight;

3.设置自己的图标

默认的VC6.0下的MFC图标和VC2005中的MFC图标分别如下:

工具/原料 VC/VS

方法/步骤

  1. 方法一:找一张ICO图标,替换programname/res/programname.ico文件,就可以啦,这时候你运行后得到的图标可能还是原来MFC的默认图标,这时候你只要把工程目录下的Debug和Release文件删除掉,重新编译生成就能得到更换图标后的程序。或者直接RebuildAll,再运行,怎么样很简单吧。
  2. 当然,你如果你在你的工程里操作也没有问题,在工作区的resorce页中的Icon中把ID为IDR_MAIN的图标资源删除,再导入自己制作的图标资源,把资源ID改为IDR_MAIN就OK啦。
  3. 方法二:上面这个的工作十分简单,但是如果要在程序中写代码改变我们应用程序的图标,比如有时候我们要在程序中动态改变程序的标题栏、任务栏图标或是托盘的图标,那就要花点功夫了。其实也不是很难,主要用几个函数搞定,如果是MFC主要就是 LoadIcon和 SetIcon。
  4. 比如我们在OnInitDialog或是OnCreate中加入:

AfxGetApp()->LoadIcon(IDI_ICON1);

SetIcon(ico,true);

SetIcon(ico,false);

  1. 另外你可能要问,MFC程序默认是怎么加载它的图标的,其实很简单,在主对话框的构造函数中有这样一句:

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAIN);

  1. 这就是加载默认图标的,而在OnInitDialog初始化函数中有这两行代码:

SetIcon(m_hIcon, TRUE); // Set big icon

SetIcon(m_hIcon, FALSE); // Set small icon

  1. 这两行代码就将应用程序的图标设置好了。到这里聪明的你应该能触类旁通了吧,其实在MFC中更换或设置图标的方式有很多种,像QQ这样动态更换图标也不是一件太难的事。

4.MFC按钮控件的美化

一、准备工作

按钮控件的集中基本状态:

Normal:按钮一开始就显示的样子

Over:鼠标指针移动到按钮上显示的样子

Down:按下按钮时显示的样子

Focus:按钮按下后松开的样子,例如:标准按钮按下之后松开会显示一个虚线框

Disable:按钮被设置为无效的时候的显示的样子

二、实现过程

1、新建一个类CXPButton,该类继承于CButton

说明:自绘控件说白了就是画图,故而在需要添加的成员变量中可以看到与画图相关的各种数据类型,一般来说成员变量都会在构造函数中初始化,在类的析构函数中销毁。

按钮的实现原理:

(1)在控件初始化的时候为控件添加Owner_Draw的属性。因为在MFC中想要激活控件的自绘功能,要求在该控件的属性中必须包含属性值BS_OWNERDRAW,这一步的具体实现方法可以通过类向导为CXPButton类添加PreSubclassWindow()函数,在该函数中完成属性值的设置。当激活控件的自绘功能之后,每次控件的状态改变之后都会运行函数DrawItem(),这个函数的功能就在于绘制控件在各种状态下的外观。

(2)添加WM_MOUSELEAVE消息函数,当鼠标指针离开按钮时,触发该消息函数,我们在函数中添加代码,通知DrawItem函数鼠标指针已经离开,让按钮重绘。

(3)添加WM_MOUSEHOVER消息函数,当鼠标指针位于按钮之上的时候,触发该消息函数,我们在函数中添加代码,通知DrawItem函数鼠标指针在按钮上,让按钮重绘。

(4)添加DrawItem函数。在该函数中根据按钮的当前状态绘制按钮的外观。可以说绘制按钮的大部分功能都是在这个函数中实现的。在该函数中有一个很关键的指针:LPDRAWITEMSTRUCT

说明:事实上WM_MOUSELEAVE和WM_MOUSEHOVER两个Windows消息是通过WM_MOUSEMOVE消息触发的,而WM_MOUSEMOVE是标准的Windows消息,因此我们可以通过类向导来为CXPButton类添加WM_MOUSEMOVE消息函数。

代码如下:

void CXPButton::OnMouseMove(UINT nFlags, CPoint point)

{

// TODO: Add your message handler code here and/or call default

if (!m_bTracking)

{

TRACKMOUSEEVENT tme;

tme.cbSize = sizeof(tme);

tme.hwndTrack = m_hWnd;

tme.dwFlags = TME_LEAVE | TME_HOVER;

tme.dwHoverTime = 1;

m_bTracking = _TrackMouseEvent(&tme);

}

CButton::OnMouseMove(nFlags, point);

}

        这段代码很重要,在其它自绘控件中,如果想触发WM_MOUSELEAVE和WM_MOUSEHOVER消息,也使用类似的方法去实现。

2、接着添加WM_MOUSELEAVE和WM_MOUSEHOVER消息消息函数。在CXPButton类的声明中(即在XPButton.h文件中)找到afx_msg void OnMouseMove(UINT nFlags, CPoint point);的函数声明,紧接其下输入:

afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);

afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);

然后在XPButton.cpp文件中找到ON_WM_MOUSEMOVE(),紧接其后输入

ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)

ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)

再就是函数的实现了代码如下:

LRESULT CXPButton::OnMouseLeave(WPARAM wParam, LPARAM lParam)

{

m_bOver = FALSE;

m_bTracking = FALSE;

InvalidateRect(NULL, FALSE);

return 0;

}

LRESULT CXPButton::OnMouseHover(WPARAM wParam, LPARAM lParam)

{

m_bOver = TRUE;

InvalidateRect(NULL);

return 0;

}

难点一:

DRAWITEMSTRUCT结构体为我们提供了哪些信息呢?

typedef struct tagDRAWITEMSTRUCT { UINT CtlType;         //控件类型 UINT CtlID;          //控件ID UINT itemID;          //菜单项、列表框或者组合框中某一项的索引值 UINT itemAction;      //控件行为 UINT itemState;      //控件状态 HWND hwndItem;      //父窗口句柄或菜单句柄 HDC hDC;             //控件对于的绘图设备句柄 RECT rcItem;          //控件占据的矩形区域 ULONG_PTRitemData;      //列表框或者组合框中某一项的值 } DRAWITEMSTRUCT;

实际上自绘按钮本身的函数结构都是差不多的,它们显示效果的区别主要取决于代码编写者对GDI作图函数的运用与掌握程度。可以研究一下CXPButton类中DrawItem函数的数据结构,其实只要修改一下其中GDI绘图函数的部分代码,马上又能做出另一个自绘按钮控件了。

VC下加载多种格式图片的方法总结IPicture, CxImage, CImage(AtlImage), CPictureEx

尽管VC有提供相应的API和类来操作bmp位图、图标和(增强)元文件,但却不支持jpg、gif和png等格式的图片,而这几种格式却是常常要用到的。这里我给大家介绍两种办法来操作这些格式的图片。

1.用API OleLoadPicture来加载JPG、GIF格式的图片(注:不支持PNG格式,另外GIF只能加载第一帧,且不支持透明)

OleLoadPicture 函数实际上创建了一个IPicture类型的COM接口对象,然后我们可以通过这个COM接口来操作图片(实际上你也可以用API OleCreatePictureIndirect来加载图片,不过相比而言OleLoadPicture函数简化了基于流的IPicture对象的创建),下面是示例代码:(注:由于只是用来示例,代码中省去了出错情况的处理)

  1. #include <olectl.h></pre>
  2. /**如下代码段实现的功能是从指定的路径中读取图片,并显示出来*/
  3. void DisplayImage(HDC hDC, LPCTSTR szImagePath) {
  4. HANDLE hFile=CreateFile(szImagePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); //从指定的路径szImagePath中读取文件句柄DWORD dwFileSize=GetFileSize(hFile, NULL);
  5. //获得图片文件的大小,用来分配全局内存
  6. HGLOBAL hImageMemory=GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
  7. void *pImageMemory=GlobalLock(hImageMemory);
  8. DWORD dwReadedSize;
  9. ReadFile(hFile, pImageMemory, dwFileSize, &dwReadedSize, NULL);
  10. GlobalUnlock(hImageMemory);
  11. CloseHandle(hFile);
  12. IStream *pIStream;
  13. IPicture *pIPicture;
  14. CreateStreamOnHGlobal(hImageMemory, false, &pIStream);
  15. //用全局内存初使化IStream接口指针
  16. OleLoadPicture(pIStream, 0, false, IID_IPicture, (LPVOID*)&(pIPicture));//用OleLoadPicture获得IPicture接口指针
  17. //得到IPicture COM接口对象后,你就可以进行获得图片信息、显示图片等操作
  18. OLE_XSIZE_HIMETRIC hmWidth;
  19. OLE_YSIZE_HIMETRIC hmHeight;
  20. pIPicture->get_Width(&hmWidth); //用接口方法获得图片的宽和高
  21. pIPicture->get_Height(&hmHeight);
  22. pIPicture->Render(hDC,0,0,100,100,0,hmHeight,hmWidth,-hmHeight,NULL); //在指定的DC上绘出图片
  23. GlobalFree(hImageMemory); //释放全局内存
  24. pIStream->Release(); //释放pIStream
  25. pIPicture->Release(); //释放pIPicture
  26. }

2.利用第三方的开发库来操作图片

这里我向大家推荐一个库CxImage。 CxImage里面包含了许多的类,可以用来加载、保存、显示和变换图片,而且支持许多的图片格式,包括BMP、 JPEG、 GIF、 PNG、 TIFF、 MNG、 ICO、 PCX、 TGA、 WMF、 WBMP、 JBG、 J2K等。另外CxImage也支持Alpha通道,动画帧等许多功能,而且它还是开源免费的。CxImage的当前的版本是v6.00, 介绍和下载可以访问:http://www.codeproject.com/KB/graphics/cximage.aspx。CxImage的用法十分简单,示例如下(省去出错处理):

  1. void DisplayImage(HDC hDC, CString fileName)
  2. {
  3. CString fileExt; //图片的扩展名
  4. int len = fileName.GetLength();
  5. for(int i=len-1; i>=0; i--) //得到图片的扩展名
  6. {
  7. if(fileName[ i ] == ‘.‘)
  8. {
  9. fileExt=fileName.Mid(i+1);
  10. break;
  11. }
  12. }
  13. fileExt.MakeLower(); //将扩展名转为小写
  14. if(fileExt != _T(""))
  15. {
  16. //创建CxImage对象,其中静态方法CxImage::GetTypeIdFromName用来根据扩展名获得图片格式的ID代表
  17. CxImage image(fileName,CxImage::GetTypeIdFromName(fileExt));
  18. if(image.IsValid())
  19. {
  20. image.Draw(hDC);
  21. image.Destroy();
  22. }
  23. }
  24. }

3 提供一中更简单的方法

VC MFC 提供的 API LoadBitmap / LoadImage 类 CBitmap 等都只能操作 BMP 位图,图标。对于其他常用的 JPG / JPEG / GIF / PNG 格式,它无能为力。VC 下怎样才能加载各种非 BMP 格式的图片呢? 下面介绍一种最简单的办法。用 CImage 类的 Load 函数加载图片,之后用 Detach 取得 HBITMAP 句柄。取得图片的HBITMAP 句柄后就可以像操作 BMP 图片一样处理 JPG / JPEG / GIF / PNG 格式的图片了。具体代码如下:

  1. #include <atlimage.h>
  2. CImage img;
  3. HRESULT ret = img.Load(filename ); // filename 是要加载的文件名(包含路径)
  4. HBITMAP bitmap = img.Detach();
  5. //像操作 BMP 图片一样处理图片

但这些网上的方法还是有些问题,比如gif不能动态的显示

下面说一下详细步骤吧:

1.下载 PictureEx.h和PictureEx.cpp两个文件

把这两个文件放在工程的文件夹里面,然后在将这两个文件添加到工程里面去,这样你的工程里就多了一个类了:CPictureEx

2.将你要加载的GIF图片添加到项目文件夹里,这里我命名为:"inter.gif"

3.在试图类的头文件里添加:

  1. #include "PictureEx.h"

定义一个对象:

  1. CPictureEx m_GifPic;

4.在视图类的OnCreate中创建 CPictureEx 对象并加载图片:

  1. <pre class="cpp" name="code">m_GifPic.Create(NULL,WS_CHILD | WS_VISIBLE |SS_ENHMETAFILE,CRect(50,50,100,100),this,1234);
  2. m_GifPic.Load(_T("inter.gif"));
  3. m_GifPic.ShowWindow(SW_HIDE);//SW_SHOW
  4. </pre>

注意:这一步骤不要在OnDraw里面实现,否则会出现错误,我一开始时一直有问题就是这个原因,还有load必须在movewindow(下一步的函数)之前,否则不会显示图片,还有就是load也可以放到ondraw里面去,但是那么做的话速度明显不行了。

5.在ondraw里改变窗口位置并显示图片

  1. m_GifPic.Create(NULL,WS_CHILD | WS_VISIBLE |SS_ENHMETAFILE,CRect(50,50,100,100),this,1234);
  2. m_GifPic.Load(_T("inter.gif"));
  3. m_GifPic.ShowWindow(SW_HIDE);//SW_SHOW
  4. CRect rc =CRect(100,400,150,450);
  5. m_GifPic.MoveWindow(&rc,true);
  6. m_GifPic.Draw();
  7. m_GifPic.ShowWindow(SW_SHOW);

这里还有一个基于对话框加载GIF图片的例子,添加了图片链接和鼠标变换的功能。

下面是详细的编程过程:

1. 新建项目:在VC6中用MFC新建一个基于对话框的GifDemo应用程序,接受所有缺省选项即可;

2.在项目中插入文件:把PictureEx.h,PictureEx.cpp文件copy 到项目文件夹下,Project->Add to Project->Files中选上PictureEx.h,PictureEx.cpp, Insert;

3.加入图片控件:从对话框控件中把Picture Control(图片控件)拖入主对话框中,修改其属性:ID:IDC_GIF,TYPE:Rectangle,其余接受缺省选项。再在ClassWiard中为IDF_GIF加入CSatic控制变量m_GifPic, 注意看一下,GifDemoDlg.h中是否加上了#include "PictureEx.h"(由ClassWiard加入)。然后将CSatic m_GifPic;更改成CPictureEx m_GifPic;

4.加载动画文件:先将要加载的动画文件放到 res 资源文件夹下,再将其Import进项目中,由于MFC只支持256BMP文件的图片,因此,我们要新建一个图片类型:"GIF",我在这里将我网站的宣传图片roaring.gif放进去 (希望大家多支持),并将其ID修改成:IDR_GIFROARING。

______________________________________________________________

import(导入)gif动画的详细过程:
在resourceview窗口中,单击鼠标右键,在出现的环境菜单中选择“import...”命令,会出现“import resource”选择文件对话框,文件类型选择“所有文件(*.*)”,open as 选项为"auto",再选择动画文件所在目录,选上要载入的动画文件 roaring.gif,再单击 import,由于gif动画类型不是vc默认的文件类型,这时会出现"custom resource type"对话框,键入“"gif"”,再单击ok,然后再修改其id。
______________________________________________________________

5.在程序的适当位置添入加载代码:
这里,我们在CGifDemoDlg::OnInitDialog()函数中加入如下代码:

  1. // TODO: Add extra initialization here
  2. if (m_GifPic.Load(MAKEINTRESOURCE(IDR_GIFROARING),_T("Gif")))
  3. m_GifPic.Draw();

如果仅仅把动画载入,到这就可以了,运行一下,应该看看您的的成果了。

下面附带说说如何将这幅动画制作成超链接,以后,咱们也可以宣传自已的公司、网站或产品了。

6.利用ClassWiard加入一个LButtonDown鼠标左键消息处理函数CGifDemoDlg::OnLButtonDown(UINT
nFlags, CPoint point),  添入如下代码:

  1. void CGifDemoDlg::OnLButtonDown(UINT nFlags, CPoint point)
  2. {
  3. // TODO: Add your message handler code here and/or call default
  4. CRect rect;
  5. m_GifPic.GetWindowRect(&rect);
  6. ScreenToClient(&rect);
  7. if (rect.PtInRect(point))
  8. ShellExecute(AfxGetMainWnd()->m_hWnd,_T("open"),
  9. _T("http://roaringwind.best.163.com"),_T(""),NULL,0);
  10. CDialog::OnLButtonDown(nFlags, point);
  11. }

我在这儿将我主页的地址放上了,运行,点击动画图片就能进入我的站点的了。当然要是能象所有的超链接一样,能将鼠标变成手形,就更好了。

7.改变鼠标形状:将一个鼠标文件放在res文件夹中,IMPORT,ID:IDC_CURSOR1,利用ClassWiard加入一个WM_SETCURSOR消息处理函数CGifDemoDlg::OnSetCursor(CWnd*
pWnd, UINT nHitTest, UINT message),  添入如下代码:

  1. BOOL CGifDemoDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
  2. {
  3. // TODO: Add your message handler code here and/or call default
  4. CRect rect;
  5. m_GifPic.GetWindowRect(&rect);
  6. ScreenToClient(&rect);
  7. CPoint point;
  8. GetCursorPos(&point);
  9. ScreenToClient(&point);
  10. if (rect.PtInRect(point) && m_hCursor)
  11. {
  12. SetCursor(m_hCursor);
  13. return TRUE;
  14. };
  15. return CDialog::OnSetCursor(pWnd, nHitTest, message);
  16. }

5.定位程序运行目录

CString    sPath;

GetModuleFileName(NULL,
sPath.GetBufferSetLength(MAX_PATH + 1), MAX_PATH);

sPath.ReleaseBuffer();

int    nPos;

nPos
= sPath.ReverseFind(‘\\‘);

sPath
= sPath.Left(nPos);

myExplorer.Navigate(_T("C:\\Loading.gif"),
NULL, NULL, NULL, NULL);

6.MFC执行流程

int CWinThread::Run()

{

ASSERT_VALID(this);

//
for tracking the idle time state

BOOL
bIdle = TRUE;

LONG
lIdleCount = 0;

//
acquire and dispatch messages until a WM_QUIT message is received.

for
(;;)

{

//
phase1: check to see if we can do idle work

while
(bIdle &&

!::PeekMessage(&m_msgCur, NULL, NULL,
NULL, PM_NOREMOVE))

{

//
call OnIdle while in bIdle state

if
(!OnIdle(lIdleCount++))

bIdle
= FALSE; // assume "no idle" state

}

//
phase2: pump messages while available

do

{

//
pump message, but quit on WM_QUIT

if
(!PumpMessage())

return
ExitInstance();

//
reset "no idle" state after pumping "normal" message

if
(IsIdleMessage(&m_msgCur))

{

bIdle
= TRUE;

lIdleCount
= 0;

}

}
while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

}

ASSERT(FALSE);  // not reachable

}

关于ExitInstance()函数的调用问题,需要了解一下MFC的执行流程。

我以建立一个名为Test的MFC工程(对于楼主所提的问题,是单文档还是多文档无所谓)为例,一切均采用默认设置,描述一下MFC程序何时调用ExitInstance()函数的。

1.你会看到CTestApp这个类,它继承自CWinApp,在CTestApp的构造函数下面,你会看到这样一条语句:CTextApp theApp;这是一个全局对象,一般书中都称它为应用程序对象,因为它是CWinApp的子类,CWinApp类中封装了一些与应用程序有关的方法。

2.全局对象的构造函数最先被执行,执行完毕后,才是主函数开始执行。执行CTestApp的构造函数,什么工作也不做,然后执行它的基类CWinApp的构造函数,为应用程序做一些准备工作,具体的细节看以查看MFC的源代码文件夹中的APPCORE.cpp文件中CWinApp的构造函数的实现。

3.执行到_tWinMain(见MFC的源代码文件夹中的APPMODUL.CPP)函数,它是MFC封装的主函数。_tWinMain中调用AfxWinMain函数。

4.AfxWinMain函数中。

4.1 调用AfxWinInit函数,主要工作有将_tWinMain传过来的四个参数赋值给CWinApp的四个相应的成员变量等,接着调用初始化线程的函数。

4.2 执行pApp->InitApplication(),pApp在CWinApp的构造函数中被赋值为this,也就是指向当前正在执行的CTestApp类的指针。但CTestApp类没有改写InitApplication(),所以调用它的基类CWinApp的InitApplication()。

4.3 执行pThread->InitInstance(),pThread在在CWinApp的构造函数中被赋值为this,于是调用CTestApp类的InitInstance()函数。

4.4 执行nReturnCode = pThread->Run()语句,进入消息循环。CTestApp没有改写Run函数,执行CWinApp::Run(),CWinApp::Run()中调用CWinThread::Run()。CWinThread::Run()的源代码2楼的shanwei355已经给出了源代码。

其中: if (!PumpMessage())

return ExitInstance();

就是应用程序调用ExitInstance()的地方,此时它是被this调用的(如是其他的对象或类调用不能之单写一个函数名,要有其他信息.例如CWinApp::ExitInstance())。因此如果你在CTestApp中没有改写ExitInstance()函数,它会执行其默认实现。ExitInstance()会在应用程序发出WM_QUIT消息后,退出应用前被执行。2楼给出MFC源代码中的注释:// pump message, but quit on WM_QUIT一句说的就是它被调用的时机。WM_QUIT消息会在你单击程序窗口的关闭按钮,或在程序中主动调PostQuitMessage()函数时发出。

七.对程序设计的认识

环境与法律

编程语言就像法律,同样类型的法律在不同国家规则不同,适用的范围也不同,相同的部分可能实际操作上有的比较简化,有的比较繁琐。就好像C++与java 虽然都支持类的继承,但java只允许一个类仅有一个基类而c++可以有多个,就好像中国的法律规定一夫一妻制。同时java不支持对指针操作,也就好像中国的法律不准公民随便使用枪支,虽然带来很多不便,但使整体社会环境更安全了;美国的公民允许使用,但要掌握并合理使用枪支,不是一件容易的事,弄不好会很危险。这些规则虽然不同,但人们的生活方式没有本质的差别,因为我们归根到底都是人,我们的生活习性,是由我们生活的环境决定的,同样在x86下编译执行的语言最终得到的东西都会遵循相同的x86规则。

效率与模式

规则大致相同,但有的社会效率更高,有的更低,这不是法律可以根本解决的事。就好像算法的优略。
同样有的社会更加稳定,有的比较混乱,这也不是法律可以根本解决的事,因为法律是最一般的规则,解决这个问题既需要进一步健全法律,同时也可以引入一种更合理的活动模式。比如大街上的“超市”,进一步统一了客户与商家的接口(收费柜台),在同样的零售业规则下,超市比一般的商店更加具有优势。这就是设计模式的价值。

人与法律

1、一般人遵守法律。

2、好人遵守法律,并时时刻刻检查自己是否遵守了各项法律。

3、高人遵守法律,并时时刻刻检查自己是否遵守了各项法律,并懂得养成更好的生活习惯(设计模式)。

4、大师遵守法律,并时时刻刻检查自己是否遵守了各项法律,并懂得养成更好的生活习惯(设计模式),并不断尝试提高自己的生活效率(使用更好的算法)。

5、圣人遵守法律,并时时刻刻检查自己是否遵守了各项法律,并懂得养成更好的生活习惯(设计模式),并不断尝试提高自己的生活效率(使用更好的算法),并开始研究改变我们的生活环境,或是制定更加完善的法律。

善假于物,又不仅仅为物。

针对项目的不同,选择适合的工具和有发展性的算法,会让工作事半功倍。

八.自己编写的程序代码

类的建立

1.CNode类

Node.h

#pragma
once

class
CNode

{

private:

int iD;//编号

//CString nodeName;

CPoint nodePosition;

//CString nodeVista;//保存结点街景图片绝对地址

CSize nodeSize;

public:

CNode();

~CNode();

void SetID(int iD);

int GetID();

void SetPoint(CPoint nodePosition);

CPoint GetPoint();

void SetName(CString nodeName);

CString GetName();

void SetSize(CSize nodeSize);

CSize GetSize();

void ShowNode(CDC *pDC);

//CNode *Next;

};

Node.cpp

//CNode的实现文件

#include
"stdafx.h"

#include
"Node.h"

CNode::CNode()

{

SetSize(20);//设置默认Size为20,半径就为10

SetPoint((0, 0));//设置默认位置为(0, 0)

//Next = NULL;

}

CNode::~CNode()

{

}

int
CNode::GetID()

{

return iD;

}

void
CNode::SetID(int iD)

{

this->iD = iD;

}

CString
CNode::GetName()

{

//return nodeName;

return _T("");

}

void
CNode::SetName(CString nodeName)

{

//this->nodeName = nodeName;

}

CPoint
CNode::GetPoint()

{

return this->nodePosition;

}

void
CNode::SetPoint(CPoint nodePosition)

{

this->nodePosition = nodePosition;

}

CSize
CNode::GetSize()

{

return nodeSize;

}

void
CNode::SetSize(CSize nodeSize)

{

this->nodeSize = nodeSize;

}

void
CNode::ShowNode(CDC *pDC)

{

/*

pDC->Ellipse(nodePosition.x - nodeSize.cx
/ 2, nodePosition.y - nodeSize.cy / 2,

nodePosition.x + nodeSize.cx / 2,
nodePosition.y + nodeSize.cy / 2);

*/

pDC->Ellipse(nodePosition.x - 10 / 2,
nodePosition.y - 10 / 2,

nodePosition.x + 10 / 2, nodePosition.y
+ 10 / 2);

CString strText("");

strText.Format(_T("%d"),
this->GetID());

pDC->TextOut(this->GetPoint().x,
this->GetPoint().y, strText);

}

2.CEdge类

Edge.h

#pragma
once

#include
"Node.h"

class
CEdge

{

private:

CNode StartNode;

CNode EndNode;

int Weight;//权重

public:

CEdge();

~CEdge();

void SetStarNode(CNode StartNode);

CNode GetStartNode();

void SetEndNode(CNode EndNode);

CNode GetEndNode();

void ShowEdge(CDC *pDC);

void SetWeight(int Weight);

int GetWeight();

//CEdge *Next;

};

Edge.cpp

#include
"stdafx.h"

#include
"Edge.h"

CEdge::CEdge()

{

}

CEdge::~CEdge()

{

}

void
CEdge::ShowEdge(CDC *pDC)

{

pDC->MoveTo(StartNode.GetPoint());

pDC->LineTo(EndNode.GetPoint());

}

CNode
CEdge::GetStartNode()

{

return StartNode;

}

void
CEdge::SetStarNode(CNode StartNode)

{

this->StartNode = StartNode;

}

CNode
CEdge::GetEndNode()

{

return EndNode;

}

void
CEdge::SetEndNode(CNode EndNode)

{

this->EndNode = EndNode;

}

int
CEdge::GetWeight()

{

return this->Weight;

}

void
CEdge::SetWeight(int Weight)

{

this->Weight = Weight;

}

3.CRoute类

Route.h

#include
"Node.h"

#include
"Edge.h"

#pragma
once

class
CRoute

{

private:

//这里之后用可变长泛型数组来表示

int nNodeNumber;

bool nodeCompleted;//标志图中的点已经全部画完了。

//------------------------------------------------------------------------------------

int nEdgeNumber;

bool edgeCompleted_;//

public:

CList<CNode, CNode&> myNode;

CList<CEdge, CEdge&> myEdge;

CRoute();

~CRoute();

int Open();//打开一条新路线

int Save();//保存一条新路线

int GetNodeNumber();

void SetNodeNumber(int nNodeNumber);

int GetEdgeNumber();

void SetEdgeNumber(int nEdgeNumber);

int ShowRoute(CDC *pDC);

int EmptyRoute();

int AddNode(CNode *myNode);

bool DeleteNode();

int AddEdge(CEdge *myEdge);

bool DeleteEdge();

bool SaveRoute(CString mapPicturePath,
CString mapRoutePath);

//执行保存操作,要把本来随意打开的图片,放到程序部署的的特定目录下,和记录的拓扑结构放在同一文件夹

//图片保持原来的格式和尺寸

//拓扑结构存成txt或者其他

};

Route.cpp

#include
"stdafx.h"

#include
"Route.h"

CRoute::CRoute()//构造函数,多用于初始化

{

nNodeNumber = 0;

SetNodeNumber(0);//初始化结点数为0

nodeCompleted = false;

SetEdgeNumber(0);

edgeCompleted_ = false;

}

CRoute::~CRoute()//析构函数,释放系统资源

{

EmptyRoute();

}

int
CRoute::AddNode(CNode *myNode)

{

this->myNode.AddTail(*myNode);

this->myNode.GetTail().SetID(nNodeNumber);

nNodeNumber++;

return nNodeNumber;

}

int
CRoute::AddEdge(CEdge *myEdge)

{

nEdgeNumber++;

this->myEdge.AddTail(*myEdge);

return nNodeNumber;

}

int
CRoute::EmptyRoute()

{

myNode.RemoveAll();

myEdge.RemoveAll();

this->SetNodeNumber(0);

return 0;

}

int
CRoute::ShowRoute(CDC *pDC)

{

for (int i = 0; i < myNode.GetCount();
i++)

{

myNode.GetAt(myNode.FindIndex(i)).ShowNode(pDC);

}

for (int i = 0; i < myEdge.GetCount();
i++)

{

myEdge.GetAt(myEdge.FindIndex(i)).ShowEdge(pDC);

}

return 0;

}

int
CRoute::GetNodeNumber()

{

return nNodeNumber;

}

int
CRoute::GetEdgeNumber()

{

return nEdgeNumber;

}

int
CRoute::Save()

{

return 0;

}

int
CRoute::Open()

{

return 0;

}

void
CRoute::SetNodeNumber(int nNodeNumber)

{

this->nNodeNumber = nNodeNumber;

}

void
CRoute::SetEdgeNumber(int nEdgeNumber)

{

this->nEdgeNumber = nEdgeNumber;

}

bool
CRoute::SaveRoute(CString mapPicturePath, CString mapRoutePath)

{

//

//在MFC下可以用CopyFile()函数,定义如下:

//BOOL CopyFile(

//LPCTSTR lpExistingFileName, //原文件地址,包括文件名

//LPCTSTR lpNewFileName, ////目的文件地址,包括文件名

//BOOL bFailIfExists //如果目的文件存在的操作

//);

//PathFileExists(mapRoutePath);//看是否存在

if (PathFileExists(mapRoutePath))//如果文件存在,同意更新个替换文件

{

if (AfxMessageBox(_T("是否要更新地图?")) == MB_CANCELTRYCONTINUE)

{

return false;

}

}

else//如果不存在,则新建写入

{

}

return true;

}

4.CGraph类

Graph.h

#pragma
once

#include
"Route.h"

class
CGraph

{

private:

public:

CGraph();

~CGraph();

//待功能完善后,改善编码的封装性

//2015-06-30

int NoEdge;  //表示没有路径时的值,65536;

//在构造函数中初始化

//距离可以用代为来扩大范围

//此相应的代表权值,不局限于距离

int Vertices; //图中的结点数,可直接从myRoute中读出

int ** A;             //指向存储邻接矩阵的二维数组指针

//此处用结点的主关键字iD来表示二维变量

//Matrix

void SetVertices(int Vertices);//设置结点数,为后面动态生成邻接矩阵做准备

int GetVertices();

int GetNoEdge();

//建立邻接矩阵

void CreateGraph(CGraph* g, int n, int
noedge);

//边的插入

bool Add(CGraph* g, int u, int v, int w);

//边的删除

bool Delete(CGraph* g, int u, int v);

//边的搜索

bool Exist(CGraph g, int u, int v);

//Dijkstra

void Dijkstra(CGraph g, int v, int d[], int
path[]);

int Choose(int d[], int n, bool* s, int
MaxNumber);

//将邻接矩阵更新,将路径里的信息转换为邻接矩阵

void UpdateGraph(CGraph *myGraph, CRoute
*myRoute);

};

Graph.cpp

#include
"stdafx.h"

#include
"Graph.h"

CGraph
*myGraph = new CGraph;

CGraph::CGraph()

{

NoEdge = 65536;

}

CGraph::~CGraph()

{

}

int
CGraph::GetNoEdge()

{

return this->NoEdge;

}

void
CGraph::SetVertices(int Vertices)

{

this->Vertices = Vertices;

}

int
CGraph::GetVertices()

{

return this->Vertices;

}

//建立邻接矩阵

void
CGraph::CreateGraph(CGraph* g, int n, int noedge)//其实也相当于初始化

{

//建立一个空的邻接矩阵,各顶点到本身为 0 ,到其他顶点为无穷即不通

int i, j;

g->NoEdge = noedge;

g->Vertices = n;

g->A = new int*[n];

for (i = 0; i < n; i++)

{

g->A[i] = new int[n];

for (j = 0; j < n; j++)

{

g->A[i][j] = noedge;

}

g->A[i][i] = 0;

}

}

//边的插入

bool
CGraph::Add(CGraph* g, int u, int v, int w)

{

int n = g->Vertices;

if (u < 0 || v<0 || u > n - 1 || v
> n - 1 || u == v/* || g->A[u][v] != g->NoEdge*/)

{

AfxMessageBox(_T("BadInput"));

return false;

}

g->A[u][v] = w;

return true;

}

//边的删除

bool
CGraph::Delete(CGraph* g, int u, int v)

{

int n = g->Vertices;

if (u < 0 || v<0 || u>n - 1 ||
v>n - 1 || u == v || g->A[u][v] == g->NoEdge)

{

AfxMessageBox(_T("BadInput"));

return false;

}

g->A[u][v] = g->NoEdge;

return true;

}

//边的搜索

bool
CGraph::Exist(CGraph g, int u, int v)

{

int n = g.Vertices;

if (u<0 || v<0 || u>n - 1 || v>n
- 1 || g.A[u][v] == g.NoEdge)

return false;

return true;

}

void
CGraph::Dijkstra(CGraph g, int v, int d[], int path[])

{

int i, u, w, n = g.Vertices;

//测试

//CString temp;

//temp.Format(_T("%d"), n);

//AfxMessageBox(temp);

bool* s = new bool[n];

if (v<0 || v>n - 1)

{

AfxMessageBox(_T("BadInput"));

return;

}

for (i = 0; i < n; i++)

{

s[i] = false;

d[i] = g.A[v][i];

if (i != v&&d[i] < g.NoEdge)

path[i] = v;

else

path[i] = -1;

}

s[v] = true;

d[v] = 0;

for (i = 1; i < n - 1; i++)

{

u = Choose(d, n, s, g.NoEdge);

s[u] = true;

for (w = 0; w < n; w++)

{

if (!s[w] && d[u] +
g.A[u][w] < d[w])

{

d[w] = d[u] + g.A[u][w];

path[w] = u;

}

}

}

}

int
CGraph::Choose(int d[], int n, bool* s, int MaxNumber)

{

int i, minpos;

int min;

min = MaxNumber;

minpos = -1;

for (i = 0; i < n; i++)

{

if (d[i] <= min&&!s[i])

{

min = d[i];

minpos = i;

}

}

return minpos;

}

void
CGraph::UpdateGraph(CGraph *myGraph, CRoute *myRoute)

{

//将两个全局变量引入程序中

//extern CGraph* myGraph;

//extern CGraph* myRoute;

//myRoute中的结点数,就是图中的结点数

int nodeCount = (myGraph->Vertices) =
(myRoute->myNode.GetCount());

//图从myRoute中获取信息初始化

myGraph->CreateGraph(myGraph, nodeCount,
65536);

//得到图中一共有多少个结点

int edgeCount =
(myRoute->myEdge.GetCount());

//为图添加边

int startNodeID;

int endNodeID;

int edgeWeight;

for (int i = 0; i < edgeCount; i++)

{

startNodeID = myRoute->myEdge.GetAt(myRoute->myEdge.FindIndex(i)).GetStartNode().GetID();

endNodeID =
myRoute->myEdge.GetAt(myRoute->myEdge.FindIndex(i)).GetEndNode().GetID();

edgeWeight =
myRoute->myEdge.GetAt(myRoute->myEdge.FindIndex(i)).GetWeight();

CGraph::Add(myGraph, startNodeID,
endNodeID, edgeWeight);

//因为是无向图片

CGraph::Add(myGraph, endNodeID,
startNodeID,edgeWeight);

}

}

5.CSplashThread类

SplashThread.h

#pragma once

#include"SplashDlg.h"

//
CSplashThread

class CSplashThread : public CWinThread

{

DECLARE_DYNCREATE(CSplashThread)

protected:

CSplashThread();           // 动态创建所使用的受保护的构造函数

virtual
~CSplashThread();

//

CSplashDlg*  m_pSplashDlg; 
//声明一个对话框指针

public:

virtual BOOL
InitInstance();

virtual int
ExitInstance();

virtual void
OnFinalRelease();

protected:

DECLARE_MESSAGE_MAP()

DECLARE_DISPATCH_MAP()

DECLARE_INTERFACE_MAP()

public:

void
HideSplash();

};

SplashThread.cpp

//
SplashThread.cpp : 实现文件

//

#include "stdafx.h"

#include "TrafficAssistSystem.h"

#include "SplashThread.h"

//
CSplashThread

IMPLEMENT_DYNCREATE(CSplashThread,
CWinThread)

CSplashThread::CSplashThread()

{

EnableAutomation();

}

CSplashThread::~CSplashThread()

{

}

void
CSplashThread::OnFinalRelease()

{

// 释放了对自动化对象的最后一个引用后,将调用

// OnFinalRelease。  基类将自动

// 删除该对象。  在调用该基类之前,请添加您的

// 对象所需的附加清理代码。

CWinThread::OnFinalRelease();

}

BOOL
CSplashThread::InitInstance()

{

// TODO:    在此执行任意逐线程初始化

::AttachThreadInput(m_nThreadID,
AfxGetApp()->m_nThreadID, TRUE);

//:通常系统内的每个线程都有自己的输入队列。本函数允许线程和进程共享输入队列。连接了线程后,输入焦点、窗口激活、鼠标捕获、键盘状态以及输入队列状态都会进入共享状态 . (这个函数可以不用)

m_pSplashDlg = new
CSplashDlg;

//m_pSplashDlg->SetEnable(true);

m_pSplashDlg->Create(IDD_SPLASHDLG);

m_pSplashDlg->ShowWindow(SW_SHOW);

return
TRUE;

}

int
CSplashThread::ExitInstance()

{

// TODO:    在此执行任意逐线程清理

m_pSplashDlg->DestroyWindow();

delete
m_pSplashDlg;

return
CWinThread::ExitInstance();

}

BEGIN_MESSAGE_MAP(CSplashThread,
CWinThread)

END_MESSAGE_MAP()

BEGIN_DISPATCH_MAP(CSplashThread,
CWinThread)

END_DISPATCH_MAP()

// 注意: 我们添加 IID_ISplashThread 支持

//  以支持来自 VBA 的类型安全绑定。  此
IID 必须同附加到 .IDL 文件中的

//  调度接口的 GUID 匹配。

//
{4D5251E8-A0B7-44E9-853A-3430D4ABFC6E}

static const IID
IID_ISplashThread =

{
0x4D5251E8, 0xA0B7, 0x44E9, { 0x85, 0x3A, 0x34, 0x30, 0xD4, 0xAB, 0xFC, 0x6E }
};

BEGIN_INTERFACE_MAP(CSplashThread,
CWinThread)

INTERFACE_PART(CSplashThread,
IID_ISplashThread, Dispatch)

END_INTERFACE_MAP()

//
CSplashThread 消息处理程序

void
CSplashThread::HideSplash()

{

m_pSplashDlg->SendMessage(WM_CLOSE);

}

时间: 2024-08-29 09:16:38

TrafficAssistSys——基于VS2013_MFC的相关文章

基于jquery开发的UI框架整理分析

根据调查得知,现在市场中的UI框架差不多40个左右,不知大家都习惯性的用哪个框架,现在市场中有几款UI框架稍微的成熟一些,也是大家比较喜欢的一种UI框架,那应该是jQuery,有部分UI框架都是根据jQuery研发出来的产品,现在也很常见了. 国产jQuery UI框架 (jUI) DWZ DWZ富客户端框架(jQuery RIA framework), 是中国人自己开发的基于jQuery实现的Ajax RIA开源框架.设计目标是简单实用,快速开发,降低ajax开发成本. jQuery 部件布局

基于位置信息的聚类算法介绍及模型选择

百度百科 聚类:将物理或抽象对象的集合分成由类似的对象组成的多个类的过程被称为聚类.由聚类所生成的簇是一组数据对象的集合,这些对象与同一个簇中的对象彼此相似,与其他簇中的对象相异."物以类聚,人以群分",在自然科学和社会科学中,存在着大量的分类问题.聚类分析又称群分析,它是研究(样品或指标)分类问题的一种统计分析方法.聚类分析起源于分类学,但是聚类不等于分类.聚类与分类的不同在于,聚类所要求划分的类是未知的. 分类和聚类算法一直以来都是数据挖掘,机器学习领域的热门课题,因此产生了众多的

基于Spark MLlib平台的协同过滤算法---电影推荐系统

基于Spark MLlib平台的协同过滤算法---电影推荐系统 又好一阵子没有写文章了,阿弥陀佛...最近项目中要做理财推荐,所以,回过头来回顾一下协同过滤算法在推荐系统中的应用. 说到推荐系统,大家可能立马会想到协同过滤算法.本文基于Spark MLlib平台实现一个向用户推荐电影的简单应用.其中,主要包括三部分内容: 协同过滤算法概述 基于模型的协同过滤应用---电影推荐 实时推荐架构分析     一.协同过滤算法概述 本人对算法的研究,目前还不是很深入,这里简单的介绍下其工作原理. 通常,

Appium移动自动化测试之—基于java的iOS环境搭建

本文仅供参考,同时感谢帮助我搭建环境的同事 操作系统的名称:Mac OS X操作系统的版本:10.12.6 接下来我们开始踏上搭建Appium+java+ios之路,本文只说个大概,毕竟本机已经装过了,我就不在折腾了,耗费好几天时间才搞定. 一:安装Appium 1.作者系统安装的Appium版本为:1.6.4,安装方法:打开终端输入:npm install –g [email protected],检查是否安装成功:终端输入appium -v,如果显示版本号说明安装成功. 2.图形界面客户端安

9个基于Java的搜索引擎

1.Java 全文搜索引擎框架 Lucene 毫无疑问,Lucene是目前最受欢迎的Java全文搜索框架,准确地说,它是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎.Lucene为开发人员提供了相当完整的工具包,可以非常方便地实现强大的全文检索功能.下面有几款搜索引擎框架也是基于Lucene实现的. 官方网站:http://lucene.apache.org/ 2.开源Java搜索引擎Nutch Nutch 是一个开源Java实现的搜索引擎.它提供了我们运行自己的搜索

基于Linux的智能家居的设计(2)

1  系统整体设计方案 智能家居系统的是一个实时查询家庭的温湿度.照明控制.自己主动控制的设定.集家庭娱乐.智能安防为一体,大量数据快处理.可靠的系统,因此在硬件和软件上都有非常大的要求,因此在这里进行了多方面的考虑有下面两个实现方案: 方案一:利用STM32单片机作为手持终端的控制器,使用按键和12864液晶屏作为人机交互的接口.利用51单片机作为房子内部的电灯.空调.门禁等家电的控制器,利用串口实现STM32单片机和51单片机作为传输数据的通道.这个能够实现.可是.机械按键和12864在智能

最简单的基于FFmpeg的AVDevice例子(读取摄像头)【转】

转自:http://blog.csdn.net/leixiaohua1020/article/details/39702113 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] libavdevice使用 注意事项 代码 结果 下载 =====================================================最简单的基于FFmpeg的AVDevice例子文章列表: 最简单的基于FFmpeg的AVDevice例子(读取摄像头) 最简单的基于FFm

[转] 基于C#的波形显示控件的实现

转自 基于C#的波形显示控件的实现[附完整源码下载] 编者记: 09年暑假正好在学院实验室呆了一段时间,做了个完整的上位机软件(具体实现:根据下位机的指令,实现通过串口来操纵下位机进行实验,并将采集的数据进行处理和保存,并以图形的方式显示),整个项目边学C# WinForm边设计,这个波形显示控件就是项目中的一部分,也花了自己绝大多数时间.此外,顺便将该波形显示控件当作自己毕业设计的内容,下文实际上是节选自自己的本科毕业论文,希望对大家能有所帮助.代码以及文章有疏漏.错误.不妥之处在所难免,欢迎

跨过Nginx上基于uWSGI部署Django项目的坑

先说说他们的关系,Nginx和uWSGI都是Web服务器,Nginx负责静态内容,uWSGI负责Python这样的动态内容,二者配合共同提供Web服务以实现提高效率和负载均衡等目的.uWSGI实现了多个协议,如WSGI,HTTP协议,还有它自己的uwsgi协议,想了解更多关于uWSGI和uwsgi协议内容可以查阅这里.这样和fastcgi类似,请求和响应的流程如下: Request > Nginx > uWSGI > Django > uWSGI > Nginx > R