面向对象:绘画板(MFC)
1.设计目的和内容
1.1.设计目的
学习Visual C++的MFC开发程序的步骤。 综合运用所学的类、继承和多态的知识。 进一步掌握程序的调试方法。
1.2.设计内容
正文用“五号”字体,中文用“宋体”,西文用“Times New Roman”,不加粗,单倍行距。
利用MFC的向导,创建基于单文档的应用程序;
编程实现,绘制线、圆、矩形的功能;
能够设定当前要画的图形样式(最少为以上3种)和图形颜色(最少能够设为以下4种颜色:黑、红、绿、蓝)。
当窗口重绘后,还能保持原来的图形。
选做1:能够选中某些已绘制的图形,并删除之。
选做2:能够保存已绘制的图形,下次打开时恢复原状。
2.基本功能描述
2.1.工程的新建
打开Microsoft Visual C++ 6.0
点击File —> New
在Projects中选择 MFC AppWizard (exe) ;
在 Project name中输入工程的名字Graphic,在 Location中选择工程存放的路径,填完后点击OK按钮。(如图1)
填完后点击OK按钮,弹出对话框(如图2)。
弹出来的对话框中我们可以选择生成三种不同类型的应用程序:单文档、多文档、对话框。
单文档程序如记事本,在一个应用程序中只能对一个文档进行操作,降低了编程的复杂度并减少了运行程序时所需的资源。
对某些小型应用(比如文本编辑器或小型图像编辑器)可以采用这种类型的窗口应用程序。
在这个程序中,我们就需要单文档,选择完后点击Next,设置默认,最后点击Finish,
完成工程的创建。
2.2.单文档简介
生成单文档程序后,进行编译,在运行,我们可以看到以下的结果。如图3 。
在classview中 系统默认生成了5个类:
CAboubtDlg CMainFrame CMy2App CMy2Doc CMy2View ,
为了实现重绘,我们新增了一个GGraph类,用来存储重绘时需要的图形的数据:线的类型、线的颜色、填充色、图形的类型、绘制图形的必要点···
2.3.添加函数
为了实现绘制图形,我们必须要获得一些已知的点,也就是通过Windows函数,调用其一定的函数,实现捕捉屏幕上的点(我们绘制时候的所需点)。
所以,我们要添加Windows函数。具体操作如下:
选择CMy2View
鼠标右键-->add Windows message handle
选择自己需要添加的Windows函数:
MOVE LBUTTONDOWN LBUTTONUP ···
点击 add handler
最后点击 确定
3.设计思路
MFC类库由130多个类组成,封装了两千多个API函数。
提供了许多与绘图相关的类和函数,因此想要制作绘图软件,我们想办法调用MFC提供的与绘图相关的MFC函数和类。
通过查阅资料我们发现MFC绘图原理是计算机根据两点坐标,调用相关函数自动绘制,基本绘图函数包括矩形、直线、椭圆,正圆使我们生活中使用非常广泛的图形,因此我们设想通过调用椭圆函数来实现正圆的绘制,最后我们通过将椭圆封装在矩形中,使矩形成为正方形,进而限制两点坐标关系,进一步得到正圆。
一般绘图软件均具有橡皮擦功能,对此功能我们将橡皮擦按钮设置成白色大小不一的直线形式,从而达到擦除已绘图形的目的。
该程序的重点在于图形的重绘,因为在窗口大小进行改变的时候,系统会自动调用OnDraw函数,进行图形的重新绘制,我们通过建立一个新类GGraph,用来用来存储重绘时需要的图形的数据:线的类型、线的颜色、填充色、图形的类型、绘制图形的必要点···
GGraph类各变量的声明:
GGraph();
virtual ~GGraph();
long m_type;
CPoint point1;
CPoint point2;
//不同形状 对应 不同 构造函数
GGraph(int a, CPoint b, CPoint c, COLORREF d, COLORREF e, int f, int g);
GGraph(int a, CPoint b, CPoint c, COLORREF d, COLORREF e, int f, int g, int aaa, int aaaa);
GGraph(int a, CPoint b, CPoint c, COLORREF d, COLORREF e, int f, int g, int h);
COLORREF color1;
COLORREF color2;
int w; //存线宽
int shape; //存形状
int aaa1; //存弧度
int aaa2; //存弧度
int ww; //存橡皮擦的宽度
重绘的原理:在绘制图形的时候,通过一个动态数组,用来存储当前绘制图形的必要元素:点 颜色 形状···
保存元素代码如下:
if (shape == 10)
{
GGraph *graph = new GGraph(penstyle, point_1, point_2, m_ncolor, m_fcolor, w, shape, aa1, aa2);
m_ptr.Add(graph);
}
else if (key == 3)
{
}
else if (shape == 4)
{
GGraph *graph = new GGraph(penstyle, point_1, point_2, m_ncolor, m_fcolor, w, shape, ww);
m_ptr.Add(graph);
}
else
{
GGraph *graph = new GGraph(penstyle, point_1, point_2, m_ncolor, m_fcolor, w, shape);
m_ptr.Add(graph);
}
重绘的实现:在OnDraw中重新编写绘制图形的代码就可以了。
4.软件设计
4.1.设计步骤及关键代码
4.1.1所需变量的定义与声明
CPtrArray m_ptr; //动态数组 m_ptr存储数据
CPoint point1;
CPoint point2;
CPoint point_4;
CPoint point_1; //记录第一个点
CPoint point_2; //记录第二个点
CPoint point_3;
COLORREF m_ncolor; //颜色
COLORREF m_fcolor; //填充色
BOOL m_down; //判断鼠标是否按下
CMetaFileDC m_d; //文件保存
int shape; //形状
int w; //线的宽度
int ww; //橡皮擦的大小
long penstyle; //笔的类型
int key; //用于判断是否使用橡皮擦和删除 判断作用
int aa1; //弧度
int aa2; //弧度
int flag; //记录鼠标按下的次数 在删除的时候使用
CString m_show1; //精确测量使用 起显示作用
CString m_show2; //同上
4.1.2变量初始化
point1.x = -110;
point1.y = -110;
point2.x = 10000;
point2.y = 10000;
m_down = false;
m_ncolor = RGB(0, 0, 0);
m_fcolor = RGB(0, 0, 0);
shape = 1;
w = 1;
penstyle = PS_SOLID;
key = 0;
ww = 2;
aa1 = 10;
aa2 = 10;
flag = 0;
4.1.3获取第一个点(LBUTTONDOWN中得到)
m_down = true;
SetCapture();
point_1 = point; //获取第一个点 point_1
Invalidate(0);
ReleaseCapture();
4.1.4获取第二个点(LBUTTONDOUP中得到)
point_2=point;//获取第二个点
4.1.5绘制图形
if (key == 3)
{
m_show2.Format("两点之间的距离是:%d", a);
}
CClientDC dc(this);
CPen pen;
CBrush brush;
pen.CreatePen(penstyle, w, m_ncolor);
brush.CreateSolidBrush(m_fcolor);
dc.SelectObject(&pen);
dc.SelectObject(&brush);
switch (shape)
{
case 1: //画直线
{
if (key == 3)
{
CClientDC dc1(this);
CPen pen1;
pen1.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
dc1.SelectObject(&pen1);
dc1.MoveTo(point_1.x, point_1.y);
dc1.LineTo(point_2.x, point_2.y);
}
else
{
dc.MoveTo(point_1.x, point_1.y);
dc.LineTo(point_2.x, point_2.y);
}
}
break;
case 2: //矩形
dc.Rectangle(point_1.x, point_1.y, point_2.x, point_2.y);
break;
case 3: //椭圆
{
dc.Ellipse(point_1.x, point_1.y, point_2.x, point_2.y);
break;
}
case 4: //橡皮擦
{
CClientDC dc(this);
CPen pen;
CBrush brush;
pen.CreatePen(penstyle, ww, RGB(255, 255, 255));
brush.CreateSolidBrush(RGB(255, 255, 255));
dc.SelectObject(&pen);
dc.SelectObject(&brush);
dc.MoveTo(point_1.x, point_1.y);
dc.LineTo(point_2.x, point_2.y);
}
break;
case 6: // 画圆
point_1.x = point_1.y - point_2.y + point_2.x;
dc.Ellipse(CRect(point_1, point_2));
break;
case 7: //弧线
dc.Arc(point_1.x + 10, point_1.y + 10, point_2.x - 10, point_2.y - 10, point_1.x, point_1.y, point_2.x, point_2.y);
break;
case 8: //饼
dc.Pie(CRect(point_1, point_2), point_1, point_2);
break;
case 9:
dc.Arc(CRect(point_1, point_2), point_1, point_2);
break;
case 10: //圆角矩形
dc.RoundRect(CRect(point_1.x, point_1.y, point_2.x, point_2.y), CPoint(aa1, aa2));
break;
}
4.1.6重绘
for (i = 0; i < m_ptr.GetSize(); i++)
{
int a;
CPen pen;
CBrush brush;
pen.CreatePen(((GGraph *)m_ptr.GetAt(i))->m_type, ((GGraph *)m_ptr.GetAt(i))->w, ((GGraph *)m_ptr.GetAt(i))->color1);
brush.CreateSolidBrush(((GGraph *)m_ptr.GetAt(i))->color2);
pDC->SelectObject(&pen);
pDC->SelectObject(&brush);
a = ((GGraph *)m_ptr.GetAt(i))->shape;
switch (a)
{
case 1:
pDC->MoveTo(((GGraph *)m_ptr.GetAt(i))->point1);
pDC->LineTo(((GGraph *)m_ptr.GetAt(i))->point2);
break;
case 2:
pDC->Rectangle(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2));
break;
case 3:
pDC->Ellipse(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2));
break;
case 4:
{
CPen pen1;
pen1.CreatePen(PS_SOLID, ww, RGB(255, 255, 255));
pDC->SelectObject(&pen1);
pDC->MoveTo(((GGraph *)m_ptr.GetAt(i))->point1);
pDC->LineTo(((GGraph *)m_ptr.GetAt(i))->point2);
break;
}
case 6:
pDC->Ellipse(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2));
break;
case 8:
pDC->Pie(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2), ((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2);
break;
case 9:
pDC->Arc(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2), ((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2);
break;
case 10:
pDC->RoundRect(CRect(((GGraph *)m_ptr.GetAt(i))->point1.x, ((GGraph *)m_ptr.GetAt(i))->point1.y, ((GGraph *)m_ptr.GetAt(i))->point2.x, ((GGraph *)m_ptr.GetAt(i))->point2.y), CPoint(((GGraph *)m_ptr.GetAt(i))->aaa1, ((GGraph *)m_ptr.GetAt(i))->aaa2));
break;
}
if (key == 2)
{
CPen pen;
CBrush brush;
pen.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
brush.CreateSolidBrush(RGB(255, 255, 255));
pDC->SelectObject(&pen);
pDC->SelectObject(&brush);
//pDC->Ellipse((100,100),(500,500));
pDC->Ellipse(point1.x, point1.y, point2.x, point2.y);
4.1.7图标美化
建立类向导---->选择UPDATE COMMAND UI
编写其函数:
pCmdUI->SetCheck(shape==1); //显示 图标的样式
4.1.8颜色板的调用
CColorDialog Color;//定义画框
if(Color.DoModal()==IDOK)
{
m_ncolor=Color.GetColor();
}
4.2.界面设计
4.3.关键功能的实现
该实验的关键在于重绘。重绘其实就是在屏幕大小改变的时候,系统自己调用ondraw函数,进行图形的重新绘制。
所以,要实现函数重绘的关键是,在ondraw中,重新编写绘制图形的函数代码,只不过在这里,要用到动态数组中存储的数据。
也就是说,要实现重绘的首要前提,就是要先声明一个动态数组,用于存储重绘所需的数据
其次就是,在绘制图形的时候,也要保存图形的一些关键数据,如点、颜色、形状···
最后在ondraw函数中,调用其动态数组的值,实现重绘。
①在CMy2View中增加动态数组ptr
CPtrArray m_ptr;//动态数组 m_ptr存储数据
②存储数据(简单举例)
GGraph *graph=new GGraph(penstyle,point_1,point_2,m_ncolor,m_fcolor,w,shape,ww);
m_ptr.Add(graph);
③重绘(简单举例)
CPen pen;
CBrush brush;
pen.CreatePen(PS_SOLID,1,RGB(255,255,255));
brush.CreateSolidBrush(RGB(255,255,255));
pDC->SelectObject(&pen);
pDC->SelectObject(&brush);
//pDC->Ellipse((100,100),(500,500));
pDC->Ellipse(point1.x,point1.y,point2.x,point2.y);
5.结论与心得体会
为期一个星期的面向对象课设已经基本结束,但是给了我很大的影响。
通过这次课设,使我明白C++语言这门课程光仅仅是听课是远远不够的,上机训练也不容忽视。
通过上机训练,才能够明白自己知识的不足,才能够有的放矢,更加深刻的理解C语言中的知识点。 通过实训,我找到了许多知识漏点,学到了很多以前不懂的知识,以前认为自己已经懂了的知识点也理解更加深刻了。
尤其是遇到了自己当时不知道如何编写的C++语言题目如何编写,通过自己的学习和同同学的交流后,试编和改错,最后能够顺利的编写出来,带来的也有成就感。并增加了我们对C++语言的兴趣和学好C++语言的信心。
与同学们交流的过程中,了解了程序的多种解决方法,知道了不同编写方法有不同的特点。完成程序的编写,决不意味着万事大吉。你认为万无一失的程序,实际上机运行时可能不断出现麻烦。
如编译程序检测出一大堆错误。有时程序本身不存在语法错误,也能够顺利运行,但是运行结果显然是错误的。
开发环境所提供的 编译系统无法发现这种程序逻辑错误,只能靠自己的上机经验分析判断错误所在。
团队合作是非常重要的,这次课设充分体会到了团队合作的重要性,提高了自己和他人的合作能力,感觉收获非常大,非常感谢有这个机会。
6.参考文献
PDF:Windows编程基础andMFC PDF:经典MFC绘图 PDF:深入浅出MFC
7.附录
7.1所需变量的定义与声明
CPtrArray m_ptr;//动态数组 m_ptr存储数据
CPoint point1;
CPoint point2;
CPoint point_4;
CPoint point_1;//记录第一个点
CPoint point_2;//记录第二个点
CPoint point_3;
COLORREF m_ncolor;//颜色
COLORREF m_fcolor;//填充色
BOOL m_down;//判断鼠标是否按下
CMetaFileDC m_d;//文件保存
int shape;//形状
int w;//线的宽度
int ww;//橡皮擦的大小
long penstyle;//笔的类型
int key;//用于判断是否使用橡皮擦和删除 判断作用
int aa1;//弧度
int aa2;//弧度
int flag;//记录鼠标按下的次数 在删除的时候使用
CString m_show1;//精确测量使用 起显示作用
CString m_show2;//同上
7.2变量初始化
point1.x = -110;
point1.y = -110;
point2.x = 10000;
point2.y = 10000;
m_down = false;
m_ncolor = RGB(0, 0, 0);
m_fcolor = RGB(0, 0, 0);
shape = 1;
w = 1;
penstyle = PS_SOLID;
key = 0;
ww = 2;
aa1 = 10;
aa2 = 10;
flag = 0;
7.3获取第一个点(LBUTTONDOWN中得到)
m_down=true;
SetCapture();
point_1=point;//获取第一个点 point_1
Invalidate(0);
ReleaseCapture();
7.4获取第二个点(LBUTTONDOUP中得到)
point_2=point;//获取第二个点
7.5绘制图形
if (key == 3)
{
m_show2.Format("两点之间的距离是:%d", a);
}
CClientDC dc(this);
CPen pen;
CBrush brush;
pen.CreatePen(penstyle, w, m_ncolor);
brush.CreateSolidBrush(m_fcolor);
dc.SelectObject(&pen);
dc.SelectObject(&brush);
switch (shape)
{
case 1: //画直线
{
if (key == 3)
{
CClientDC dc1(this);
CPen pen1;
pen1.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
dc1.SelectObject(&pen1);
dc1.MoveTo(point_1.x, point_1.y);
dc1.LineTo(point_2.x, point_2.y);
}
else
{
dc.MoveTo(point_1.x, point_1.y);
dc.LineTo(point_2.x, point_2.y);
}
}
break;
case 2: //矩形
dc.Rectangle(point_1.x, point_1.y, point_2.x, point_2.y);
break;
case 3: //椭圆
{
dc.Ellipse(point_1.x, point_1.y, point_2.x, point_2.y);
break;
}
case 4: //橡皮擦
{
CClientDC dc(this);
CPen pen;
CBrush brush;
pen.CreatePen(penstyle, ww, RGB(255, 255, 255));
brush.CreateSolidBrush(RGB(255, 255, 255));
dc.SelectObject(&pen);
dc.SelectObject(&brush);
dc.MoveTo(point_1.x, point_1.y);
dc.LineTo(point_2.x, point_2.y);
}
break;
case 6: // 画圆
point_1.x = point_1.y - point_2.y + point_2.x;
dc.Ellipse(CRect(point_1, point_2));
break;
case 7: //弧线
dc.Arc(point_1.x + 10, point_1.y + 10, point_2.x - 10, point_2.y - 10, point_1.x, point_1.y, point_2.x, point_2.y);
break;
case 8: //饼
dc.Pie(CRect(point_1, point_2), point_1, point_2);
break;
case 9:
dc.Arc(CRect(point_1, point_2), point_1, point_2);
break;
case 10: //圆角矩形
dc.RoundRect(CRect(point_1.x, point_1.y, point_2.x, point_2.y), CPoint(aa1, aa2));
break;
}
7.6重绘
for (i = 0; i < m_ptr.GetSize(); i++)
{
int a;
CPen pen;
CBrush brush;
pen.CreatePen(((GGraph *)m_ptr.GetAt(i))->m_type, ((GGraph *)m_ptr.GetAt(i))->w, ((GGraph *)m_ptr.GetAt(i))->color1);
brush.CreateSolidBrush(((GGraph *)m_ptr.GetAt(i))->color2);
pDC->SelectObject(&pen);
pDC->SelectObject(&brush);
a = ((GGraph *)m_ptr.GetAt(i))->shape;
switch (a)
{
case 1:
pDC->MoveTo(((GGraph *)m_ptr.GetAt(i))->point1);
pDC->LineTo(((GGraph *)m_ptr.GetAt(i))->point2);
break;
case 2:
pDC->Rectangle(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2));
break;
case 3:
pDC->Ellipse(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2));
break;
case 4:
{
CPen pen1;
pen1.CreatePen(PS_SOLID, ww, RGB(255, 255, 255));
pDC->SelectObject(&pen1);
pDC->MoveTo(((GGraph *)m_ptr.GetAt(i))->point1);
pDC->LineTo(((GGraph *)m_ptr.GetAt(i))->point2);
break;
}
case 6:
pDC->Ellipse(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2));
break;
case 8:
pDC->Pie(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2), ((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2);
break;
case 9:
pDC->Arc(CRect(((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2), ((GGraph *)m_ptr.GetAt(i))->point1, ((GGraph *)m_ptr.GetAt(i))->point2);
break;
case 10:
pDC->RoundRect(CRect(((GGraph *)m_ptr.GetAt(i))->point1.x, ((GGraph *)m_ptr.GetAt(i))->point1.y, ((GGraph *)m_ptr.GetAt(i))->point2.x, ((GGraph *)m_ptr.GetAt(i))->point2.y), CPoint(((GGraph *)m_ptr.GetAt(i))->aaa1, ((GGraph *)m_ptr.GetAt(i))->aaa2));
break;
}
if (key == 2)
{
CPen pen;
CBrush brush;
pen.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
brush.CreateSolidBrush(RGB(255, 255, 255));
pDC->SelectObject(&pen);
pDC->SelectObject(&brush);
//pDC->Ellipse((100,100),(500,500));
pDC->Ellipse(point1.x, point1.y, point2.x, point2.y);
7.7图标美化
建立类向导---->选择UPDATE COMMAND UI
编写其函数:
pCmdUI->SetCheck(shape==1); //显示 图标的样式
7.8颜色板的调用
CColorDialog Color;//定义画框
if(Color.DoModal()==IDOK)
{
m_ncolor=Color.GetColor();
}
7.9界面设计
项目源码
下载路径:
结语
代码均为原创
版权归原作者所有
「仅供小伙伴们学习参考使用」
请勿用作其余用途!
创作不易
如果您觉得写的不错的话
「点赞+在看+收藏」 ❤️