C++课设简单画图程序.docx
《C++课设简单画图程序.docx》由会员分享,可在线阅读,更多相关《C++课设简单画图程序.docx(22页珍藏版)》请在冰豆网上搜索。
C++课设简单画图程序
简单画图程序
1.基本功能描述
本次课程设计的任务是利用计算机和VC开发环境编写一个简单画图程序,该程序的设计指标(即主要功能)有:
①用鼠标拖动绘制圆、椭圆、矩形,线等基本图形;②能控制画笔的线宽和颜色;③能对图形进行颜色填充;④在鼠标移动的过程中能实时显示当前绘制的图形。
除了以上几点功能,我有另外添加了工具栏、铅笔、橡皮等功能来使程序更方便地让用户使用,简化了人机交互的过程。
2.设计思路
首先是界面的问题,既然课设的题目是简单画图,那在建立工程的时候就要选择文档结构而不是以前做的基于对话框。
可选择单文档结构或多文档结构,但想到画图界面一般只有一个,从简洁的角度考虑,选择单文档结构。
而且用户界面在设计的时候要尽可能简单美观,一目了然,对相应功能有图标提示,使用户方便使用。
然后便是画图功能的具体实现。
分析课设要求,可以发现功能一要求的的椭圆、矩形、直线可以分别通过Ellipse();、Rectangle();、MoveTo();、LineTo();这四个函数来实现。
功能二控制画笔线宽和颜色可以给二者分别关联参数,通过改变线宽参数值来控制线宽,通过调用通用对话框改变颜色参数值来控制颜色。
功能三对图形进行颜色填充虽以前未接触过,但查阅资料后发现可以调用ExtFloodFill();并合理设定参数值来实现。
功能四的实时显示功能可以通过调用MouseMove();函数来实现。
这些功能需要建立菜单资源来表示各个功能选项,并建立相应的消息响应函数来进行实现。
另外,画图程序主要是通过鼠标来进行操作,所以要对鼠标消息的响应及处理函数多下功夫来思考和编写。
至于额外添加的功能,工具栏通过添加工具栏资源和关联相应菜单中画图功能ID实现;铅笔、橡皮的功能则是利用MouseMove();函数,在其中做了一些改动来实现。
2.1.程序流程图:
图1程序流程图
3.软件设计
3.1.设计步骤
1)打开VS2010,选择新建项目,选中MFC应用程序,标题为CWN_Graphic;
2)选择单文档结构,其他为默认值,点击完成。
3.2.界面设计
1)选择资源视图,鼠标右键,添加,资源;
2)在资源视图中打开添加的菜单资源,将各个功能的名称添加进去并合理安排,并设置好相应的ID,如图2:
图2编辑菜单及相应ID
3.2.2.添加工具栏
1)在资源视图下右键选择添加资源,ToolBar;
2)打开新建的工具栏,在上面添加各个小按钮,按钮图像自行画出;
3)将每个按钮的ID与菜单项一一对应起来,如图3:
图3工具栏样式及ID
3.3.关键功能实现
1)打开类向导;
2)为各个菜单项添加消息函数,如图4:
图4添加消息函数
相关ID、关联变量及响应函数如表1:
表1相关功能ID及所关联变量
名称
ID
关联变量
响应函数
点
IDM_DOT
m_nDrawType=1
OnDot()
直线
IDM_LINE
m_nDrawType=2
OnLine()
矩形
IDM_RECTANGLE
m_nDrawType=3
OnRectangle()
椭圆
IDM_ELLIPSE
m_nDrawType=4
OnEllipse()
画笔
IDM_PEN
m_Pen=TRUE
OnPen()
填充
IDM_FILL
m_Fill=TRUE
OnFill()
橡皮(小)
IDM_ERASER1
-
OnEraser1()
橡皮(中)
IDM_ERASER2
-
OnEraser2()
橡皮(大)
IDM_ERASER3
-
OnEraser3()
线宽+线型
IDM_LINE_WIDTH
-
OnLineWidth()
颜色
IDM_Color
-
OnColor()
3)按照所需在CWN_GraphicView.h中添加变量并在CWN_GraphicView.cpp中对变量初始化:
UINTm_nDrawType=0;//绘画类型初始为0
CPointm_ptOldOrigin=0;//坐标点初始为(0,0)
CPointm_ptNewOrigin=0;
UINTm_nLineWidth=0;//线宽初始为0
UINTm_EraserWidth=1;//橡皮初始大小为1
INTm_nLineStyle=0;//线宽初始为0
BOOLm_Pen=FALSE;//画笔功能未被选中
BOOLm_Eraser=FALSE;//橡皮功能未被选中
BOOLm_LButtonDown=FALSE;//鼠标左键未按下
BOOLm_Fill=FALSE;//填充功能未被选中
COLORREFm_clr=RGB(0,0,0);//颜色初始值为黑色
4)在各响应函数中添加相应代码,例如:
/**********左键按下************/
/*该函数实现了再鼠标左键按下时确定按下点的坐标、将左键被按下置为1和判断是否填充选项被选中,若选中则进行填充,未选中则无操作*/
voidCCWN_GraphicView:
:
OnLButtonDown(UINTnFlags,CPointpoint)
{
m_ptNewOrigin=point;//将当前点坐标传给变量
m_ptOldOrigin=point;
m_LButtonDown=TRUE;//左键按下赋值1
if(m_Fill==TRUE)//填充功能
{
CClientDCdc(this);
HBRUSHhBrush=CreateSolidBrush(m_clr);//选择画刷
dc.SelectObject(hBrush);
dc.ExtFloodFill(m_ptNewOrigin.x,m_ptNewOrigin.y,RGB(0,0,0),0);
//该函数将使用当前刷子填充显示表面区域
}
CView:
:
OnLButtonDown(nFlags,point);
}
/***********左键松开************/
/*该函数通过在鼠标左键松开时,将松开点坐标传给PC,并由之前选择的绘画类型来判断要进行的绘画操作*/
voidCCWN_GraphicView:
:
OnLButtonUp(UINTnFlags,CPointpoint)
{
m_LButtonDown=FALSE;//左键松开赋值0
CClientDCdc(this);
CPenpen(m_nLineStyle,m_nLineWidth,m_clr);//创建画笔画刷
dc.SelectObject(&pen);//选择画笔
CBrush*pBrush=CBrush:
:
FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
dc.SelectObject(pBrush);//选择画刷
m_ptNewOrigin=point;//左键松开的坐标点送给m_ptNewOrigin
switch(m_nDrawType)
{
case1:
/*******画点*******/
dc.SetPixel(point,m_clr);
break;
case2:
/*******画线*********/
dc.MoveTo(m_ptOldOrigin);
dc.LineTo(m_ptNewOrigin);
break;
case3:
/**********画矩形**********/
dc.Rectangle(CRect(m_ptOldOrigin,m_ptNewOrigin));
break;
case4:
/*********画椭圆**********/
dc.Ellipse(CRect(m_ptOldOrigin,m_ptNewOrigin));
break;
}
CView:
:
OnLButtonUp(nFlags,point);
}
/*************线型+线宽设置*********/
/*该函数在线型线宽功能被选择时,会生成一个线型线宽设置对话框(之前在资源中添加和编辑的对话框资源),并将用户操作后的线型线宽值传给PC*/
voidCCWN_GraphicView:
:
OnLineWidth()
{
//TODO:
在此添加命令处理程序代码
CSettingDlgline_dlg;
line_dlg.m_nLineWidth=m_nLineWidth;//将先前设置的线宽回传给对话框
line_dlg.m_nLineStyle=m_nLineStyle;//将先前设置的线型回传给对话框
if(IDOK==line_dlg.DoModal())
{
m_nLineWidth=line_dlg.m_nLineWidth;
m_nLineStyle=line_dlg.m_nLineStyle;
}
}
/*************动态显示函数******************/
/*该函数单独写出,配合OnMouseMove();函数可实现动态显示的功能*/
voidCCWN_GraphicView:
:
Draw()
{
CClientDCdc(this);
CPenpen(m_nLineStyle,m_nLineWidth,m_clr);
CPen*oldPen=dc.SelectObject(&pen);
dc.SelectStockObject(NULL_BRUSH);
intdrawmode=dc.SetROP2(R2_NOTXORPEN);
/*R2_NOTXORPEN模式的作用是:
先把画笔的颜色取反,再与屏幕颜色进行“或”操作,从而得到像素最终显示的颜色。
*/
switch(m_nDrawType)
{
case2:
dc.MoveTo(m_ptOldOrigin);
dc.LineTo(m_ptNewOrigin);
break;
case3:
dc.Rectangle(CRect(m_ptOldOrigin,m_ptNewOrigin));
break;
case4:
dc.Ellipse(CRect(m_ptOldOrigin,m_ptNewOrigin));
break;
}
dc.SetROP2(drawmode);
dc.SelectObject(oldPen);
}
/*************MOUSEMOVE***************/
/*此函数与前面的动态显示函数(Draw();)进行配合便可实现在鼠标移动时的实时显示功能,思路是在绘图时,先用屏幕背景色覆盖掉鼠标移动前的画面,然后用选定的画笔颜色重新绘制下一次鼠标移动的画面*/
voidCCWN_GraphicView:
:
OnMouseMove(UINTnFlags,CPointpoint)
{
//TODO:
在此添加消息处理程序代码和/或调用默认值
CClientDCdc(this);
CPenpen(m_nLineStyle,m_nLineWidth,m_clr);
CPen*oldPen=dc.SelectObject(&pen);
if(m_LButtonDown!
=FALSE)
{
Draw();
m_ptNewOrigin=point;
Draw();
if(m_Pen==TRUE)//铅笔功能
{
m_nDrawType=0;//画图类型改为初始值,以免多重画图
dc.MoveTo(m_ptOldOrigin);
dc.LineTo(m_ptNewOrigin);
//修改线段起点
m_ptOldOrigin=m_ptNewOrigin;
m_ptNewOrigin=point;
}
if(m_Eraser==TRUE)//橡皮功能
{
m_nDrawType=0;
CPenpen(m_nLineStyle,m_EraserWidth,RGB(255,255,255));
dc.SelectObject(&pen);
dc.MoveTo(m_ptOldOrigin);
dc.LineTo(m_ptNewOrigin);
//修改线段起点
m_ptOldOrigin=m_ptNewOrigin;
m_ptNewOrigin=point;
}
}
dc.SelectObject(oldPen);
CView:
:
OnMouseMove(nFlags,point);
}
/************选择铅笔**************/
/*该函数在选择铅笔功能后,将铅笔功能置为真,其他有干扰的功能置为假*/
voidCCWN_GraphicView:
:
OnPen()
{
//TODO:
在此添加命令处理程序代码
m_Pen=TRUE;//画笔被选中
m_Fill=FALSE;//取消填充功能
m_Eraser=FALSE;//取消橡皮功能
}
4.结论与心得体会
本次课程设计虽说是“简单”画图程序的设计,但真要是自己做起来,感觉一点也不简单。
以前做的课程实验及课后习题等,几乎都是在老师讲解过相关课本知识和示范操作后稍作一定的功能修改,但因为毕竟是学过了,也看过老师的演示,所以就算有不会的地方也能在观看教学PPT或课堂笔记及查看教材资料后自行解决。
但这次的简单画图程序的设计,不仅综合了以前对MFC编程的全部所学知识,而且还有未学过的几个函数的详细应用,这就需要在仔细查阅相关书籍、网络资料等来对这几个函数的用法进行了解。
虽说起来简单,但实际操作中却漏洞百出,代码设计一改再改才最后实现了预定功能。
另外,在这次的课程设计程序编写过程中,我对MFC编程的过程流程在脑中也愈发清晰,对每个函数应如何声明,实现,使用的过程愈发明了。
过程一波三折,有语法问题,函数使用的问题,兼容性问题,头文件或变量无法识别,逻辑错误等等,每一种错误都花费了许多精力去找寻问题所在。
虽然辛苦,但觉得收获良多。
在编写程序的过程中,首先是一头雾水,总是在出错,而自己有不明白为什么会出错;慢慢坚持编写和查资料等找错,就一点点好了起来,对整个流程需要做的事也越来越明白;等到后期,就基本不会再出什么错了,就只是对程序的优化处理。
可能程序的编写就是这样,开头最难,等写下去,后面倒是只是代码的添加,却不怎么难了。
所以,在编写程序之前,一定要自己把程序流程好好想一想,弄明白每个流程的关键所在,流程搞得越清楚,在后续编写的过程中遇到的错误就会越少,越顺利。
除此之外,遇到搞不明白的问题的时候,要多和同学进行讨论。
本次编写过程中,我就经常和选题相同的另一位同学进行相关的讨论,尤其是对MouseMove函数的使用以及填充功能的实现,我们进行了大量的讨论,在讨论后我们再进行实际验证,并不断修改代码,最终才使相关功能得以实现。
5.思考题
1)说明直线、椭圆、矩形绘制使用的函数,及其参数含义?
答:
直线、椭圆及矩形的绘制是在CCWN_GraphicView:
:
OnLButtonUp();函数中实现,此函数在左键弹起时,将松开点坐标传给PC,并由之前选择的绘画类型(m_nDrawType的值,为2则直线,3则是矩形,4则是椭圆)来判断要进行的绘画操作。
若为直线,则调用dc.MoveTo(m_ptOldOrigin);dc.LineTo(m_ptNewOrigin);这两个函数,前者将窗口坐标移到指定位置,后者执行连线操作;若为矩形,则调用dc.Rectangle(CRect(m_ptOldOrigin,m_ptNewOrigin));函数,通过两点坐标画出矩形;若为椭圆,则调用dc.Ellipse(CRect(m_ptOldOrigin,m_ptNewOrigin));函数,由两点坐标确定一个矩形,再由矩形来内接画出一个椭圆。
2)如何控制菜单项的状态,使用的消息类型?
答:
菜单项的状态是通过各个消息响应函数来确定的,我规定了几个变量m_Pen,m_Eraser等,在选中其中一种功能后,在相应函数将其值置为TRUE,其他值置为FALSE以免产生干扰。
而其他的如线型线宽因顾不干扰则只是设置了代表各自实际值的变量而未设置是否被选中的变量。
3)如何设置菜单和工具栏按钮的快捷键操作?
答:
快捷键的功能通过在资源视图下设置加速键来实现。
在资源视图下打开加速键列表,在列表末尾空白处点击出现一个新的加速键选项,选择需要设置加速键的菜单ID值,如IDM_LINE,在修饰符下拉菜单中选择想要进行的快捷操作如Ctrl,在Key下输入想要组合的键值如L,类型选择VIRTKEY后便可完成快捷键的设置。
6.附录
6.1.调试报告
本次课设程序编写及调试过程中,遇到的问题主要由以下几种:
1)语法错误:
例如在写“;”时的中英文格式不对、少写了“}”等导致的语法错误,修改方法即为将错误的语法改为正确的。
2)无法识别预编译头文件:
遇到这样的问题后,在项目中打开属性,C/C++,预编译头,选择不使用预编译头文件即可。
3)无法识别**字符:
这类问题的解决方法是:
在项目中打开属性,配置属性,常规,字符集,选择使用多字节字符集即可解决。
4)函数使用方式有错误:
出现这一类错误主要是对函数的使用不熟悉,对其中参数的作用不了解所致;解决方法只能是去多方查阅资料,深入了解其使用方法及参数含义,并在可能的情况下去找一些范例来进行对比,从而掌握其正确使用方法。
5)其他:
本次编写过程中还出现过编译链接都能过去,但运行时却会出现一个对话框的warning然后程序不能运行的错误。
有过两次,第一次未搞明白其具体含义,在上网查询解决方法后,通过项目属性,代码生成,运行库下选择多线程DLL(/MD)后解决;第二次在多方查找资料后明白了是因为在添加工具栏按钮时,按钮位图大小不一(有两种,一种大小17*17,另一种大小15*16)所致,将按钮位图大小改为相同的后,报错消失。
6.2.测试结果
初始界面,图5:
图5程序运行初始界面
线型线宽设置对话框,图6:
图6线型线宽设置对话框
操作示例,图7:
图7操作示例
6.3.关键源代码
//GraphicView.cpp:
CGraphicView类的实现
//
#include"stdafx.h"
//SHARED_HANDLERS可以在实现预览、缩略图和搜索筛选器句柄的
//ATL项目中进行定义,并允许与该项目共享文档代码。
#ifndefSHARED_HANDLERS
#include"Graphic.h"
#endif
#include"GraphicDoc.h"
#include"GraphicView.h"
#include"SettingDlg.h"
#ifdef_DEBUG
#definenewDEBUG_NEW
#endif
//CGraphicView
IMPLEMENT_DYNCREATE(CGraphicView,CView)
BEGIN_MESSAGE_MAP(CGraphicView,CView)
//标准打印命令
ON_COMMAND(ID_FILE_PRINT,&CView:
:
OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT,&CView:
:
OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,&CGraphicView:
:
OnFilePrintPreview)
ON_WM_CONTEXTMENU()
ON_WM_RBUTTONUP()
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_COMMAND(IDM_DOT,&CGraphicView:
:
OnDot)
ON_COMMAND(IDM_LINE,&CGraphicView:
:
OnLine)
ON_COMMAND(IDM_RECTANGLE,&CGraphicView:
:
OnRectangle)
ON_COMMAND(IDM_ELLIPSE,&CGraphicView:
:
OnEllipse)
ON_COMMAND(IDM_LINE_WIDTH,&CGraphicView:
:
OnLineWidth)
ON_COMMAND(IDM_PEN,&CGraphicView:
:
OnPen)
ON_WM_MOUSEMOVE()
ON_COMMAND(IDM_Color,&CGraphicView:
:
OnColor)
ON_COMMAND(IDM_FILL,&CGraphicView:
:
OnFill)
ON_COMMAND(IDM_ERASER1,&CGraphicView:
:
OnEraser1)
ON_COMMAND(IDM_ERASER2,&CGraphicView:
:
OnEraser2)
ON_COMMAND(IDM_ERASER3,&CGraphicView:
:
OnEraser3)
END_MESSAGE_MAP()
//CGraphicView构造/析构
CGraphicView:
:
CGraphicView()
{
//TODO:
在此处添加构造代码
/**************变量初始化*************/
m_nDrawType=0;//绘画类型初始为0
m_ptOldOrigin=0;//坐标点初始值为0
m_ptNewOrigin=0;
m_EraserWidth=1;//橡皮初始大小为1
m_nLineWidth=0;//线宽初始值0
m_nLineStyle=0;//线型初始为0
m_clr=RGB(0,0,0);//颜色初始值为黑
m_Pen=FALSE;//画笔初始未选中
m_Eraser=FALSE;//橡皮初始未选中
m_LButtonDown=FALSE;//鼠标左键初始值为0
m_Fill=FALSE;//填充按钮初始未选中
}
CGraphicView:
:
~CGraphicView()
{
}
BOOLCGraphicView:
:
PreCreateWindow(CREATESTRUCT&cs)
{
//TODO:
在此处通过修改
//CREATESTRUCTcs来修改窗口类或样式
returnCView:
:
PreCreat