面向对象:绘画板(MFC)

共 22257字,需浏览 45分钟

 ·

2021-08-13 20:49

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)

图1工程创建过程

填完后点击OK按钮,弹出对话框(如图2)。

图2工程创建过程

弹出来的对话框中我们可以选择生成三种不同类型的应用程序:单文档、多文档、对话框。

单文档程序如记事本,在一个应用程序中只能对一个文档进行操作,降低了编程的复杂度并减少了运行程序时所需的资源。

对某些小型应用(比如文本编辑器或小型图像编辑器)可以采用这种类型的窗口应用程序。

在这个程序中,我们就需要单文档,选择完后点击Next,设置默认,最后点击Finish,

完成工程的创建。

2.2.单文档简介

生成单文档程序后,进行编译,在运行,我们可以看到以下的结果。如图3 。

图3编译界面

在classview中 系统默认生成了5个类:

  • CAboubtDlg
  • CMainFrame
  • CMy2App
  • CMy2Doc
  • CMy2View ,

为了实现重绘,我们新增了一个GGraph类,用来存储重绘时需要的图形的数据:线的类型、线的颜色、填充色、图形的类型、绘制图形的必要点···

图4工程默认类

2.3.添加函数

为了实现绘制图形,我们必须要获得一些已知的点,也就是通过Windows函数,调用其一定的函数,实现捕捉屏幕上的点(我们绘制时候的所需点)。

所以,我们要添加Windows函数。具体操作如下:

选择CMy2View

鼠标右键-->add Windows message handle

选择自己需要添加的Windows函数:

  • MOVE
  • LBUTTONDOWN
  • LBUTTONUP
  • ···

点击 add handler

最后点击 确定

图5添加系统函数

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(000);
m_fcolor = RGB(000);
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(255255255));
        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(255255255));
    brush.CreateSolidBrush(RGB(255255255));

    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(255255255));
      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(255255255));
      brush.CreateSolidBrush(RGB(255255255));

      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

图6添加系统函数

编写其函数:

pCmdUI->SetCheck(shape==1); //显示 图标的样式

4.1.8颜色板的调用

CColorDialog Color;//定义画框
if(Color.DoModal()==IDOK)
{
 m_ncolor=Color.GetColor();
}

4.2.界面设计

图7工程界面

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(255255255));
        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(255255255));
    brush.CreateSolidBrush(RGB(255255255));

    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

图8添加系统函数

编写其函数:

pCmdUI->SetCheck(shape==1); //显示 图标的样式

7.8颜色板的调用

CColorDialog Color;//定义画框
if(Color.DoModal()==IDOK)
{
 m_ncolor=Color.GetColor();
}

7.9界面设计

图9工程界面及测试界面

项目源码

下载路径:

工程文件下载

结语

代码均为原创

版权归原作者所有

「仅供小伙伴们学习参考使用」

请勿用作其余用途!

创作不易

如果您觉得写的不错的话

「点赞+在看+收藏」 ❤️

写留言

浏览 27
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报