VC编程技术点滴(四)鼠标绘制任意图形

VC编程技术点滴(四)鼠标绘制任意图形

 

一、建立工程
    在Visual Studio 6.0中新建一个单文档工程,除在文档模式中选择单文档方式外,其它步骤均选择默认配置,工程名为CreateLine。
二、鼠标划线的实现思路
    本例为使用鼠标绘制线段、矩形、圆及任意曲线等图形。

   1、画线段:在菜单中选择划线命令,在窗口客户区可以单击鼠标左键连续划线,双击鼠标左键结束划线。设置一个保存鼠标 单击次数的变量,当变量值为0时,使用MoveTo函数设置起始点,变量值大于0时,使用LineTo函数实现连续划线;利用鼠标左键双击消息处理函数, 设置鼠标单击次数变量值为-1,结束划线;利用鼠标移动消息处理函数实现划线过程中的橡皮线显示。

   2、画圆:选择画圆菜单,点击鼠标左键确定圆心,释放左键并拖动鼠标以确定半径,再次点击鼠标时画圆。

   3、画矩形:与画圆类似,点击鼠标左键确定矩形左上角,释放左键并拖动鼠标已确定矩形尺寸,再次点击鼠标时画矩形。

   4、画任意曲线:选择菜单,点击鼠标左键确定曲线起始点,释放并拖动鼠标绘制任意曲线,再次点击鼠标左键结束绘制。
三、具体实现
   1、设置成员变量
    在工程的ClassView中右键选择CCreateLineView类,选择添加成员变量,包括记录图形类型的CString变量 m_drawtype;记录线段起始和终止点(对圆来说则是圆心坐标和圆周上的点坐标)的m_Startp和m_Endp,类型为CPoint;添加记录 鼠标左键单击次数的变量m_step(类型为int)。
   2、添加绘图菜单命令
    为统一处理鼠标绘图菜单消息,这里用到了ON_COMMAND_RANGE宏和ON_UPDATE_COMMAND_UI_RANGE宏,用于映射一组绘图菜单命令。下面是MFC手册中关于ON_COMMAND_RANGE宏的用法:

    ON_COMMAND_RANGE( id1, id2, memberFxn )

    参数: id1 一个连续范围的命令ID的起始值。 
           id2 一个连续范围的命令ID的结束值。 
           memberFxn 命令组被映射到的消息处理函数的名字。 

    说明:
    不同于ON_COMMAND把单个命令ID映射到成员函数。使用这个宏可以把一个连续范围的多个命令ID映射到单个命令处理成员函数。ID的范围从id1 开始,到id2结束,也就是说这一组命令ID要连续。一般来说,只要连续定义菜单等命令,其ID都是连续的,可以通过 View->Resourec symbloes菜单,或String Table资源来查看。如果命令ID不连续,可以直接在Resource.h中修改。
    由于ClassWizard不支持消息映射范围,所以你必须自己写入这个宏。确保你把它写在了消息映射的“//{{AFX_MSG_MAP”分界符外面。   

    基于此,可以在ResourceView的菜单资源中添加“鼠标绘图”菜单,在该菜单下添加“绘直线”、“画圆”、“画矩形”、“画任意曲线”等二级菜单,并编辑各菜单的命令ID名称,如画曲线的命令ID为ID_CURVE。

    在CreateLineView.h和CreateLineView.cpp文件中手工添加如下代码:

    // CreateLineView.h : interface of the CCreateLineView class

    ......

    class CCreateLineView : public CView
    {

       ......

     // Generated message map functions
     protected:
        //{{AFX_MSG(CCreateLineView)
        afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
        afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
        afx_msg void OnMouseMove(UINT nFlags, CPoint point);
        //}}AFX_MSG
        //以上为使用类向导添加的消息处理函数(后面还有详细说明)

        //下两行代码为手工添加,用于处理鼠标绘图菜单的统一处理
       afx_msg void OnDrawByMouse(int m_nID);
       afx_msg void OnUpdateDrawByMouse(CCmdUI *pCmdUI);

       ......

    };

    ......

    //CreateLineView.cpp :implementation of the CCreateLineView class

    ......

    //消息映射
    BEGIN_MESSAGE_MAP(CCreateLineView, CView)
          //{{AFX_MSG_MAP(CCreateLineView)
          ON_WM_LBUTTONDOWN()
          ON_WM_LBUTTONDBLCLK()
          ON_WM_MOUSEMOVE()
          //}}AFX_MSG_MAP
          // Standard printing commands
          ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
          ......   

          //以下两行代码为手工添加,用于鼠标绘图菜单的统一处理

          //设置菜单范围的第一个和最后一个菜单项
         ON_COMMAND_RANGE(ID_DRAWLINE,ID_CURVE,OnDrawByMouse)
         ON_UPDATE_COMMAND_UI_RANGE(ID_DRAWLINE,ID_CURVE,OnUpdateDrawByMouse)

    END_MESSAGE_MAP()

    ......

    //手工添加相应函数

    void CCreateLineView::OnDrawByMouse(int m_nID)
    {
       switch(m_nID)
       {
         case ID_DRAWLINE:
           m_drawtype="line";
           m_step=0;
           break;
         case ID_DRAWCIRCLE:
           m_drawtype="circle";
           m_step=0;
           break;
         case ID_DRAWRECT:
           m_drawtype="rect";
           m_step=0;
           break;
         case ID_CURVE:
           m_drawtype="curve";
           m_step=0;
           break;
         default:;
       }
    }

    void CCreateLineView::OnUpdateDrawByMouse(CCmdUI *pCmdUI)
    {
       int nFlag = 0 ;
       switch(pCmdUI->m_nID)
       {
         case ID_DRAWLINE:
           if(m_drawtype=="line")

              nFlag=1;
           break;
         case ID_DRAWCIRCLE:
           if(m_drawtype=="circle")

              nFlag=1;
           break;
         case ID_DRAWRECT:
           if(m_drawtype=="rect")

              nFlag=1;
           break;
         case ID_CURVE:
           if(m_drawtype=="curve")

              nFlag=1;
              break;
         default:;
       }

       pCmdUI->SetCheck(nFlag);
    }

    ......
    3、添加鼠标左键单击消息处理函数
    在ClassWizard中为CCreateLineView类添加鼠标左键单击消息处理函数(消息名称为WM_LBUTTONDOWN):
    void CCreateLineView::OnLButtonDown(UINT nFlags, CPoint point)
    {
       CDC* pDC = GetDC() ;//获取设备环境
       pDC->SelectStockObject(NULL_BRUSH) ;//将库存GDI对象选进设备环境 
       if(m_drawtype== "line")
       {
         if(m_step==0)//第一次单击鼠标左键确定直线起始点
         {
           m_Startp=m_Endp=point;
           m_step++;
         }
         if(m_step>0)//再次单击鼠标左键确定直线段终点,可以连续绘制折线
         {
           m_Endp=point;
           m_step++;
           DrawLine(pDC,m_Startp,m_Endp);//定义的成员函数

           m_Startp=point;  
         }
       } 
       if(m_drawtype== "circle")
       {
         if(m_step==0)
         {
           m_Startp=m_Endp=point;
           m_step=1;
         }

         else if(m_step==1)//如果没有else,两个条件语句都会执行(上一if改变了条件变量)
         {
           m_Endp=point;
           m_step=0;//等于0可以继续画圆,等于-1则一次只能画一个圆。双击鼠标左键可以停止画图。
           DrawCircle(pDC,m_Startp,m_Endp);//定义的成员函数
         }
       }

       if(m_drawtype== "rect")
       {
          if(m_step==0)
          {
            m_Startp=m_Endp=point;
            m_step=1;
          }

          else if(m_step==1)
          {
            m_Endp=point;
            m_step=0;
            DrawRect(pDC,m_Startp,m_Endp);//定义的成员函数
          }
       }

       //绘制曲线在鼠标移动函数中具体实现
       if(m_drawtype == "curve")
       {
         if(m_step==0)//第一次单击鼠标左键确定曲线起始点
         {
            m_Startp=m_Endp=point;
            m_step=1;
         }
         else if(m_step==1)//再次单击鼠标左键结束绘制曲线

            m_step=0; 
       }

       ReleaseDC(pDC) ;//释放掉不再使用的DC ; 
       CView::OnLButtonDown(nFlags, point);
    }

   4、添加鼠标左键双击消息处理函数
    在ClassWizard中为CCreateLineView类添加鼠标左键双击消息处理函数(消息名称为WM_LBUTTONDBLCLK):
    void CCreateLineView::OnLButtonDblClk(UINT nFlags, CPoint point)
    {
       m_step=-1;//表示结束划线。

       CView::OnLButtonDblClk(nFlags, point);
    }

   5、添加成员函数

    为CCreateLineView类添加DrawLine等成员函数:

 

    // CreateLineView.h

    protected:

    void DrawLine(CDC *pDC,CPoint startPoint,CPoint endPoint);
    void DrawRect(CDC *pDC,CPoint m_Startp,CPoint m_Endp);
    int ComputeRadius(CPoint m_Centerp,CPoint m_Arroundp);
    void DrawCircle(CDC *pDC,CPoint m_Centerp,CPoint m_Arroundp);

 

    // CreateLineView.cpp

    void CCreateLineView::DrawLine(CDC *pDC,CPoint startPoint, CPoint endPoint)
    {
       pDC->MoveTo(startPoint);
       pDC->LineTo(endPoint);
    }

    void CCreateLineView::DrawCircle(CDC *pDC, CPoint m_Centerp, CPoint m_Arroundp)
    {
       int radius=ComputeRadius(m_Centerp,m_Arroundp);
       CRect rc(m_Centerp.x-radius,m_Centerp.y-radius,m_Centerp.x+radius,m_Centerp.y+radius);
       pDC->Ellipse(rc);
    }
    //计算半径
    int CCreateLineView::ComputeRadius(CPoint m_Centerp, CPoint m_Arroundp)
    {
      int dx=m_Centerp.x-m_Arroundp.x;
      int dy=m_Centerp.y-m_Arroundp.y;
      return (int)sqrt(dx*dx+dy*dy);
    }

    void CCreateLineView::DrawRect(CDC *pDC, CPoint m_Startp, CPoint m_Endp)
    {
       pDC->Rectangle(m_Startp.x,m_Startp.y,m_Endp.x,m_Endp.y);
    }

   
   6、OnMouseMove函数(包括橡皮线的实现):
    在ClassWizard中为CCreateLineView类添加鼠标移动消息处理函数(消息名称为WM_MOUSEMOVE):
    //参考《Visual C++ 实践与提高》之图形图像编程篇   

    void CCreateLineView::OnMouseMove(UINT nFlags, CPoint point)
    {
       //插入设置状态栏的代码(后面有详细说明)
       CDC* pDC = GetDC() ;
       //设置绘图模式,并将先前的绘图模式加以保存
       int nDrawmode = pDC->SetROP2(R2_NOT) ;// 设置绘图像素的颜色为屏幕颜色的反色
       //将库存GDI对象选进设备环境
       pDC->SelectStockObject(NULL_BRUSH) ;

       if(m_drawtype=="line")
       {
         if(m_step >0)
         {
            CPoint prePnt, curPnt ;
            // 获得鼠标所在的前一个位置
            prePnt = m_Endp ;
            curPnt = point ;
            //绘制橡皮线
            DrawLine(pDC, m_Startp,prePnt) ;//该语句起到抹掉上一次划线的作用。当第一次点击鼠标

            //左键时,m_Startp=m_Endp,m_step由0变为1,移动鼠标时,先划了从m_Startp到m_Endp的

            //线(由于两个点的位置相同,系统并未真正划线),又划了从m_Startp到鼠标移动的当前点

            //的线,同时,m_Endp被赋值为鼠标移动的当前点;再次移动鼠标,又会先划一条从m_Startp

            //到m_Endp的线,等于重划了上次移动鼠标时所划的第二条线,由于设置线像素的颜色为屏幕

            //颜色的反色,重划线等于抹掉原来划的线;然后再划从m_Startp到鼠标移动的当前点的线,

            //依次类推,划出新橡皮线,抹掉旧线,就起到了橡皮线的作用。
            DrawLine(pDC, m_Startp,curPnt) ;
            m_Endp = point ;
         }
       }

       if(m_drawtype=="circle")
       {
         if(m_step>0)
         {
            CPoint prePnt, curPnt ;
            // 获得鼠标所在的前一个位置
            prePnt = m_Endp ;
            curPnt = point ;
            DrawCircle(pDC, m_Startp,prePnt) ;//绘制橡皮线            
            DrawCircle(pDC, m_Startp,curPnt) ;       
            m_Endp = point ;
         }
       }

       if(m_drawtype=="rect")
       {
         if(m_step>0)
         {
            CPoint prePnt, curPnt ;
            // 获得鼠标所在的前一个位置
            prePnt = m_Endp ;
            curPnt = point ;
            //绘制橡皮线
            DrawRect(pDC, m_Startp,prePnt) ;
            DrawRect(pDC, m_Startp,curPnt) ;       
            m_Endp = point ;
         }
       }

       if(m_drawtype=="curve")
       {
         if(m_step ==1)
         {
           CPoint prePnt, curPnt ;
           // 获得鼠标所在的前一个位置
           prePnt = m_Endp ;
           curPnt = point ;
           //绘制任意曲线

           //划任意曲线的方式:点击划线菜单,在窗口中点击选择起始点位置,移动鼠标就可以任意划

           //线,再次点击鼠标左键,结束当前曲线输入;选择新的曲线起始点可以继续绘制新曲线。双

           //击鼠标左键结束曲线绘制。
           DrawLine(pDC, prePnt,curPnt) ;      
           m_Endp = point ;
         }
       }
       //恢复到先前的绘图模式
       pDC->SetROP2(nDrawmode) ;
       //释放掉不再使用的DC ;
       ReleaseDC(pDC) ; 
       CView::OnMouseMove(nFlags, point);
    }
   7、状态栏显示鼠标坐标
    在框架类的状态栏设置中增加一个分割区:
    // MainFrm.cpp : implementation of the CMainFrame class
    static UINT indicators[] =
    {
       ID_SEPARATOR,           //增加一个状态分隔区
       ID_SEPARATOR,           // status line indicator
       ID_INDICATOR_CAPS,
       ID_INDICATOR_NUM,
       ID_INDICATOR_SCRL,
    };

    在视图类的鼠标移动消息处理函数中增加状态显示代码:

    // CreateLineView.cpp
    void CCreateLineView::OnMouseMove(UINT nFlags, CPoint point)
    {
       //设置状态条,显示鼠标坐标
       CStatusBar* pStatus=

            (CStatusBar*)AfxGetApp()->m_pMainWnd->GetDescendantWindow(ID_VIEW_STATUS_BAR);
       //ASSERT(pStatus) ;
       CString str ;
       str.Format("(%8d,%8d)",point.x,point.y) ;

       if(pStatus)
       {
          //pStatus->SetPaneText(0,str);未增加状态栏分割区前,只能在第1个分割区显示
          pStatus->SetPaneText(1,str);//在增加了状态栏分割区后,可在第2个分割区显示
       }

      //插入前面提到的其它绘图代码
       CView::OnMouseMove(nFlags, point);
    }

转载于:https://www.cnblogs.com/luoshupeng/archive/2011/08/19/2146174.html

可以作为课程设计的程序,实现简单的绘图,移动图形的功能。 使用vc框架实现。 GHpaint程序的几个重点 程序的基本功能: 程序提供绘图、删除已绘图形、移动已绘图形。 可以选择绘图颜色、形状、线粗。 重点问题: 1、 程序设计基于面向对象。 程序中所有图形都从基类CShapebase派生,大部分工作(例如:删除、移动等)都在这个基类中完成。此类中的两个纯需函数: //纯需函数DrawShape,每个基类都必须实现的函数。 //指定不同图形绘制方法。 virtual void DrawShape(CDC* pdc) = 0; //橡皮筋效果的算法函数 virtual void ZoomShape(CDC *pdc) = 0; 这两个函数需要在派生类中重载。这两个函数中主要实现不同图形绘制方法。 也就是说,不同图形的不同点只有绘制方法不同,其他所有属性和行为都可以统一处理。 这里的继承体现了面向对象的设计,论文中可以体现这一点。 2、 动态模板库对象CPtList的使用,动态模板库基础知识。 CPtList是动态模板库的一个实例化类。功能是以链表形式保存指针,至于指针类型不做要求,需要程序员自己设计。 本程序CPtList对象中保存所有图形对象指针,由于所有图形都从CShapebase派生,所以所有图形指针都可以转化为基类CShapebase指针后保存在CPtList中。以后再遍历列表,调用图形对象函数时出现了多态调用。这里体现面向对象程序的多态,论文中可以说明。 至于CPtList的使用,可以在网上查查,可以看看程序代码,比较简单。 多态:是一种函数调用形式。出现在类继承情况下。详细的多态定义到网上查查。 程序中的多态体现在CShapebase类的虚函数,在使用CShapebase指针调用这两个虚函数,实际调用的使子类的重载函数体。这里实际上是不知道函数怎样工作,但是知道函数功能。 例如:使用CShapebase指针调用DrawShape函数,因为不知道子类是什么图形,所以不知道DrawShape怎样画这个图,但是直到DrawShape函数会把这个图画好,这就达到了要求。 3、 Windows窗口绘图基本知识。 3.1 Windows GUI绘图基本知识。使用MFC类库之后,每个窗口都有一个CDC指针量。这个变量提供图形绘制。CDC类说明查查网络,内容比较多。CDC及提供图形绘制算法,也提供各种绘制模式(单色、异或色等),提供画笔和画刷的功能。 3.2 窗口绘图基本思想: 图形绘制在窗口中,windows不会帮助程序员让图形“长在”窗口上,当窗口被遮挡,最小化后,图形绘消失。程序需要在合适的时机把图形重新画在窗口上。所以窗口上画的内容需要程序员自己思考怎样保存。在本程序中所有图形保存在CPtList对象中。 3.2 Windows为窗口提供了一个重绘消息:OnPaint,在winxp系统中,这个消息会在窗口被遮挡部分从新出现、窗口从最小化恢复时调用。在win7种只有窗口从屏幕外移动进屏幕时调用。具体情况可以查查网络,这里是我的发现,并不是主要内容。 在OnPaint消息中重新绘制所有图形是一个保证图形不消失的好办法。 4、 Windows颜色控制基本知识。 4.1 windows使用RGB三原色(红绿蓝)提供颜色控制,本程序使用24位颜色,在内存中占用4字节,所以用int型表示,但是int型的最高位字节不使用,这样每一种颜色就有一个字节表示,每种颜色级别从0-255。系统提供RGB宏帮助定义颜色,例如:RGB(255,0,0)是红色,RGB(0,0,0)黑色,RGB(255,255,255)白色。 4.2 windows绘图提供多种颜色混合模式,本程序中使用异或模式实现图形的“橡皮筋”效果和移动效果,使用纯色模式定位图形
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值