简单图形绘制系统设计方案.docx
《简单图形绘制系统设计方案.docx》由会员分享,可在线阅读,更多相关《简单图形绘制系统设计方案.docx(51页珍藏版)》请在冰豆网上搜索。
简单图形绘制系统设计方案
简单图形绘制系统设计方案
第一章课题背景
引言
计算机技术的发展,使人类社会进入了信息化和自动化,计算机智能识别也随着计算机的发展得到了迅速的发展。
特别是图形图像的计算机处理技术更是有了前所未有的进步和应用。
计算机识别也逐渐的从图形图像处理的大环境下分离出来作为一门新的高科技研究领域出现。
画图涉及到的学科很多,包括数字信号处理、工程数学、信息论、运筹学等,它与计算机、自动化、生物学、视觉心理和生理学、人工智能、智能信息处理等众多领域交叉、综合集成,有广泛的应用。
该软件功能与Windows下附件中的画图软件有相似功能。
本程序是在VC6.0平台上用MFC所编。
绘制方面的功能主要包括绘制各种图元,包括直线、矩形、椭圆,用笔或画刷自由绘图。
绘图时可以选择各种颜色,各种线型和线宽。
该软件设计的设计思想是面向对象程序设计的思想。
面向对象技术是一个非常实用而强有力的软件开发方法。
它具有许多特色。
一是方法的唯一性,即方法是对软件开发过程所有阶段进行综合考虑而得到的。
二是从生存期的一个阶段到下一个阶段的高度连续性,即在一个阶段所用到的部分与在下一个阶段所使用的部分是衔接的,所使用的技术经过生存期每一阶段后不改变。
三是把面向对象分析(OOA)、面向对象设计(OOD)和面向对象程序设计(OOP)集成到生存期的相应阶段。
笔者作为一个初学者,所学知识有限,做这个软件主要是为了测试近期的学习成果,方便了解对MFC的学习掌握程度,以便于后期的学习。
该程序中基本上包含了笔者这一段时间在VC上付出的所有努力。
软件又MFC单文档类工程为主要载体,其中菜单、对话框的创建以及各种消息的响应,MFC库函数的调用等,其创建过程能看出一个初学者由浅入深的过程。
软件的预期是能成为一个电脑绘图的辅助功能存在,可是软件完成后绘图功能上依然有许多方面不能满足需求。
希望读者提出宝贵的意见和建议。
第二章设计简介及设计方案论述
2.1设计目的
培养学生对科学研究的认识,掌握基本的科研方法;培养学生分析问题、研究问题和解决问题的能力;培养学生动手能力和编程能力以及自主查阅资料的能力。
2.2设计思路
2.2.1题设要求
完成简单图形的绘制系统:
实现直线,矩形,圆的绘制,并能设置线型,线宽和颜色;实现图形的重绘和保存功能;实现在状态栏动态显示当前时间功能。
2.2.2题目分析
题目要求设计一个简单的绘图系统,程序功能要求简单,可以用一个简单的窗口菜单程序解决。
对题目要求的绘制图形和线条更改,使用菜单响应会很容易解决,至于图形的重绘和保存,状态栏的时间显示虽然使用的函数较为复杂,看似麻烦,实际上函数框架简化出来后很容易理解,响应功能的代码量也相对较少。
2.3设计原理
我创建的是一个单文档类的MFCAppWizard(exe)工程(图2-1,图2-2),在该工程里,我使用菜单(图2-3,图2-4)和对话框(图2-5)实现了题目要求的简单绘图设计。
图2-1创建MFC应用程序
图2-2创建单文档类MFC工程
图2-3双击IDR_MAINFRAME创建菜单
图2-4建立如图菜单项
图2-5建立如图所示对话框
第三章详细设计
3.1菜单的响应
3.1.1菜单的创建
首先建立一个但文档类型的MFCAppWizard(exe)工程,工程名为:
简单绘图演示(图2-1,图2-2),然后打开资源视图,找到“菜单”-“IDR_MAINFRAME”,双击“IDR_MAINFRAME”创建菜单(见图2-3图2-4)。
建立的菜单如图4所示。
3.1.2菜单的响应
菜单建好后,要为所建立的菜单添加消息响应函数,这样所建立的菜单才能被加载入程序(斜体部分为添加的消息响应函数,图3-1)。
例3-1
voidC简单绘图演示View:
:
OnLine()
{//TODO:
在此添加命令处理程序代码
}
voidC简单绘图演示View:
:
OnCircle()
{
//TODO:
在此添加命令处理程序代码
}
voidC简单绘图演示View:
:
OnRect()
{
//TODO:
在此添加命令处理程序代码
}
图3-1在类向导中响应创建的菜单项
(响应项ID:
ID_CIRCLE、ID_LINE、ID_RECT;分别对应圆、直线、矩形)
接着,为C简单绘图演示View添加私有成员变量m_nDrawStyle(图3-2是具体的添加成员变量操作),并将m_nDrawStyle初始化为0(斜体为m_nDrawStyle初始化过程)。
例3-2
C简单绘图演示View:
:
C简单绘图演示View()
:
m_nDrawStyle(0)
{
//TODO:
在此处添加构造代码
m_nDrawStyle=0;
}
图3-2为C简单绘图演示View添加UINT类型的私有成员变量m_nDrawStyle
m_nDrawStyle变量初始化后再为响应函数赋值(斜体为赋值过程)
例3-3
voidC简单绘图演示View:
:
OnLine()
{//TODO:
在此添加命令处理程序代码
m_nDrawStyle=0;
}
voidC简单绘图演示View:
:
OnCircle()
{//TODO:
在此添加命令处理程序代码
m_nDrawStyle=1;
}
voidC简单绘图演示View:
:
OnRect()
{//TODO:
在此添加命令处理程序代码
m_nDrawStyle=2;
}
赋值完成后,即要开始让程序绘图了,谈到绘图自然就想到了绘图的必要工具——画笔和画刷,当然,当我们调用绘图函数的时候其实系统已经提供了一个默认的画笔很画刷。
如果要自定义更改画笔和画刷的颜色,类型就需要另外调用申明了。
现在先使用默认工具响应图像的绘制,自定义画笔画刷后面会讲到。
对于直线、矩形和圆,在绘图是都可以由2个点来确定其图形。
当鼠标左键按下时得到一个点,当鼠标左键松开时又会得到一个点。
也就是说,在鼠标左键按下时将当前点保存为绘图原点,当鼠标左键松开时,就可以绘图了。
因此就需要为视类(C简单绘图演示View)分别捕获鼠标左键按下和左键松开这两个消息。
另外,当鼠标左键按下时,需要将鼠标当前按下点保存起来,因此为C简单绘图演示View类再增加一个CPoint类型的私有成员变量:
m_ptOrigin。
并在C简单绘图演示View类构造函数中,将该变量的值设置为0,即将原点设置为(0,0)。
然后,在鼠标左键按下消息响应函数中,保存当前点,代码如例3-4(斜体)。
例3-4
voidC简单绘图演示View:
:
OnLButtonDown(UINTnFlags,CPointpoint)
{
//TODO:
在此添加消息处理程序代码和/或调用默认值
m_ptOrigin=point;
CView:
:
OnLButtonDown(nFlags,point);
}
在鼠标左键松开消息响应函数中实现绘图功能,具体代码如例3-5(斜体)。
例3-5
voidC简单绘图演示View:
:
OnLButtonUp(UINTnFlags,CPointpoint)
{
//TODO:
在此添加消息处理程序代码和/或调用默认值
CClientDCdc(this);
switch(m_nDrawStyle)
{
case1:
dc.Ellipse(CRect(m_ptOrigin,point));
break;
case2:
dc.Rectangle(CRect(m_ptOrigin,point));
break;
case0:
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
break;
}
CView:
:
OnLButtonUp(nFlags,point);
}
好了,绘图响应函数完成了,试运行看看程序是否能完成绘图操作。
为了进行绘图,首先需要有DC对象,例3-5所示代码中首先定义了一个CClientDC类型的变量:
dc。
然后将已经保存在变量m_nDrawStyle的用户选择调用出来,选择switch/case语句来分别完成绘图,完成用户的不同选择。
程序运行结果(图3-3):
图3-3简单绘图程序运行显示
从运行结果图中可以发现该程序绘制的图形都是黑色的,而且图形中绘制的圆也不是圆,而是一个椭圆,当一个图形在另一个图形上绘制的时候会将之前的图形覆盖掉。
为了有效的解决程序中发现的问题,我们一个一个分析:
1、图形显示为黑色,是因为系统默认的画笔和画刷都是黑色的,要想程序输出不用的颜色,这就需要为程序重新申明一支画笔和画刷;
2、绘制的圆是椭圆的原因是因为我们调用的是椭圆的绘制函数,在几何上,圆是椭圆的特殊形态,当椭圆的长轴和短轴相等时,椭圆就成了一个圆,换句话说就是当椭圆的外接矩形是一个正方形时,这是的椭圆就是圆,而椭圆的绘制函数就是通过外接矩形的左上角坐标和右下角坐标来绘制的,所以在获取椭圆的参数时,只要将左上角和右下角两个坐标的x,y的差取等即可;
3、图形会被覆盖是因为程序默认的画刷类型是一个白画刷,若是想看到图形里面的容,只需要将画刷类型改为透明即可。
程序更改如例3-6(斜体,粗体部分为修改后的容)
例3-6
voidC简单绘图演示View:
:
OnLButtonUp(UINTnFlags,CPointpoint)
{
//TODO:
在此添加消息处理程序代码和/或调用默认值
CClientDCdc(this);
CPenpen(PS_SOLID,1,RGB(255,0,0));
dc.SelectObject(&pen);
CBrush*pBrush=CBrush:
:
FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
dc.SelectObject(pBrush);
switch(m_nDrawStyle)
{
case1:
dc.Ellipse(m_ptOrigin.x,m_ptOrigin.y,point.x,m_ptOrigin.y+point.x-m_ptOrigin.x);
break;
case2:
dc.Rectangle(CRect(m_ptOrigin,point));
break;
case0:
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
break;
}
CView:
:
OnLButtonUp(nFlags,point);
}
运行结果(图3-4):
图3-4修改后程序运行结果图
从图中可以看到之前的问题已经被解决了。
3.2设置对话框
基本绘图功能已经实现,现在给“简单绘图演示”程序加上其他功能——线宽、线型和颜色按钮设置。
(对话框建立如第二章图2-5)
对话框建好后,为此对话框建立一个响应的对话框类。
图3-5为对话框建立相应的对话框类
响应对话框,创建一个响应消息(如例3-7斜体部分)。
例3-7
voidC简单绘图演示View:
:
OnSetting()
{
//TODO:
在此添加命令处理程序代码
CSettingDlgdlg;
dlg.m_nLineWidth=m_nLineWidth;
dlg.m_nLineStyle=m_nLineStyle;
if(IDOK==dlg.DoModal())
{
m_nLineWidth=dlg.m_nLineWidth;
m_nLineStyle=dlg.m_nLineStyle;
}
}
响应消息是应该注意添加头文件#include“SettingDlg.h”。
其中m_nLineWidth和m_nLineStyle变量的申明见图3-6。
申明后在C简单绘图演示View和SettingDlgView中初始化为0。
图3-6为编辑框和单选框添加变量
得到线宽和线型的变量后,在例3-7中已经保存赋值,接着就要使用了,线宽线型都是对画笔的变量,自然使用在画笔函数中,先前定义的画笔CPenpen(PS_SOLID,1,RGB(255,0,0));其中三个参数分别对应线型线宽和颜色,将线型和线宽参数用变量m_nLineStyle和m_nLineWidth代替即可实现对画笔线型和线宽的更改,更改如下:
例3-8
CPenpen(m_nLineStyle,m_nLineWidth,RGB(255,0,0));
更改后运行结果如图3-7.
图3-7线型线宽实现后的运行结果
3.3颜色对话框
在先前对话框建立的时候,可以看到一个“颜色选择”按钮,该按钮是对画笔颜色进行自定义的控件。
MFC为我们提供了一个类:
CColorDialog,可以很方便地创建颜色对话框。
在SettingDlgView响应按钮“颜色选择”:
例3-9
voidCSettingDlg:
:
OnClickedColor()
{
//TODO:
在此添加控件通知处理程序代码
CColorDialogdlg;
dlg.m_cc.Flags|=CC_RGBINIT|CC_FULLOPEN;
dlg.m_cc.rgbResult=m_clr;
if(IDOK==dlg.DoModal())
{
m_clr=dlg.m_cc.rgbResult;
}
}
其中m_clr是一个COLORREF类型的变量,该变量在SettingDlg.h中申明,是一个public成员。
然后在C简单绘图演示View响应OnSetting消息中添加如下粗体代码:
例3-10
voidC简单绘图演示View:
:
OnSetting()
{
//TODO:
在此添加命令处理程序代码
CSettingDlgdlg;
dlg.m_nLineWidth=m_nLineWidth;
dlg.m_nLineStyle=m_nLineStyle;
dlg.m_clr=clr;
if(IDOK==dlg.DoModal())
{
m_nLineWidth=dlg.m_nLineWidth;
m_nLineStyle=dlg.m_nLineStyle;
clr=dlg.m_clr;
}
}
其中clr是在C简单绘图演示View.h中定义的COLORREF类型的private成员变量。
做完这些后,再次更改画笔参数中的颜色参数,更改如下:
CPenpen(m_nLineStyle,m_nLineWidth,clr);
响应后运行结果如图3-8:
图3-8颜色对话框改变画笔颜色
3.4状态栏时间设置
通过第三章的前三节,我们已经创建了一个可以绘制圆、矩形和直线的简单绘图程序,该程序已经能够实现对,下面我们来扩展下程序对系统时间的响应。
首先增加一个名为时钟的字符串资源,创建方式:
双击StringTable文件夹下的StringTable项,打开C简单绘图演示程序的字符串资源,在其中添加一个“ID:
IDS_TIMER;Caption:
时钟”的字符串资源。
然后在CMainFrame类源文件的前部定义:
例3-11
staticUINTindicators[]=
{
ID_SEPARATOR,//状态行指示器
IDS_TIMER,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
为了在状态栏上显示时间的静态文本,在程序的CMainFrame类的OnCreate函数的后部,但要在其return语句前添加例3-12所示代码。
例3-12
CTimet=CTime:
:
GetCurrentTime();
CStringstr=t.Format("%H:
%M:
%S");
CClientDCdc(this);
CSizesz=dc.GetTextExtent(str);
intindex=0;
index=m_wndStatusBar.CommandToIndex(IDS_TIMER);
m_wndStatusBar.SetPaneInfo(index,IDS_TIMER,SBPS_NORMAL,sz.cx);
m_wndStatusBar.SetPaneText(index,str);
SetTimer(1,1000,NULL);
其中SetTimer()是一个定时器,起功能是1000毫秒触发一次定时器消息,加粗部分功能是获取输出文本显示是占据的宽度,然后直接用这个宽度去修改窗格的宽度。
此时我们已经获得了当前的系统时间,但是这个时间是静止的,我们要想它实时的显示系统当前的时间,就必须让这个时间“动起来”。
我们已经设置了一个定时器,没一秒发送一次WM_TIMER定时器消息,那么我们就可以在该定时器消息响应函数中再次获得系统当前时间,并设置状态栏窗格的文本。
加入代码如例3-13。
例3-13
voidCMainFrame:
:
OnTimer(UINT_PTRnIDEvent)
{
//TODO:
在此添加消息处理程序代码和/或调用默认值
CTimet=CTime:
:
GetCurrentTime();
CStringstr=t.Format("%H:
%M:
%S");
CClientDCdc(this);
CSizesz=dc.GetTextExtent(str);
m_wndStatusBar.SetPaneInfo(1,IDS_TIMER,SBPS_NORMAL,sz.cx);
m_wndStatusBar.SetPaneText(1,str);
CFrameWndEx:
:
OnTimer(nIDEvent);
}
此时运行程序可以看到状态栏中的时间已经“动起来”了。
3.5图形的保存和重绘
程序运行后似乎没什么问题了,可是当我们放大或缩小窗口时,我们在窗口中绘制的图形会神奇的消失掉。
这是因为当窗口尺寸发生变化时,引起窗口重绘,会发送WM_PAINT消息,这是首先会擦除窗口的背景,然后再进行重绘操作,这样就把窗口中先前绘制的图形擦除掉了。
如果希望所绘制的图形始终在窗口中呈现出来,就需要将这些图形保存起来,然后当窗口尺寸发生变化引起窗口重绘时,将这些图形再次在窗口中输出。
当窗口重回是总是会调用程序视类的OnDraw函数,因此我们可以在该函数中完成图形的输出。
而保存图形的方式多种多样,对于本程序来说,只需要绘制时的起点、终点和绘制的类型(圆、矩形或直线)就可以了。
此时我们要新建一个类:
CGraph。
新增类类型为:
GenericClass。
为新增类添加成员变量:
例3-14
public:
CGraph(UINTm_nDrawType,CPointm_ptOrigin,CPointm_ptEnd,COLORREFm_clr1,UINTm_LineWidth,intm_LineStyle);
virtual~CGraph();
UINTm_nDrawType;
CPointm_ptOrigin;
CPointm_ptEnd;
COLORREFm_clr1;
intm_LineStyle;
UINTm_LineWidth;
在GraphView中初始化变量:
例3-15
CGraph:
:
CGraph(UINTm_nDrawType,CPointm_ptOrigin,CPointm_ptEnd,COLORREFm_clr1,UINTm_LineWidth,intm_LineStyle)
{
this->m_nDrawType=m_nDrawType;
this->m_ptOrigin=m_ptOrigin;
this->m_ptEnd=m_ptEnd;
this->m_clr1=m_clr1;
this->m_LineWidth=m_LineWidth;
this->m_LineStyle=m_LineStyle;
}
在C简单绘图演示View视类OnDraw中加入如下代码:
例3-16
voidC简单绘图演示View:
:
OnDraw(CDC*/*pDC*/)
{
C简单绘图演示Doc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
if(!
pDoc)
return;
//TODO:
在此处为本机数据添加绘制代码
CClientDCdc(this);
CBrush*pBrush=CBrush:
:
FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
dc.SelectObject(pBrush);
for(inti=0;i{
CPenpen(((CGraph*)m_ptrArray.GetAt(i))->m_LineStyle,((CGraph*)m_ptrArray.GetAt(i))->m_LineWidth,
((CGraph*)m_ptrArray.GetAt(i))->m_clr1);
dc.SelectObject(&pen);
switch(((CGraph*)m_ptrArray.GetAt(i))->m_nDrawType)
{
case1:
dc.Ellipse(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin.x,((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin.y,
((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd.x,((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin.y+((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd.x-((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin.x);
break;
case2:
dc.Rectangle(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin,
((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));
break;
case0:
dc.MoveTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin);
dc.LineTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd);
break;
}
}
}
可以看到,代码中调用了一个CGraph*指针,其定义如下:
例3-17