zoukankan      html  css  js  c++  java
  • 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);
        }

  • 相关阅读:
    C#的一些基本问题
    Mac ssh连接远程服务器,并实现文件的上传和下载
    Redis 持久化
    Redis 数据类型
    @dynamic 与 @synthesize 关键词详解
    Redis介绍及安装
    crontab的用法
    修改文件权限
    Linux目录结构
    一些命令
  • 原文地址:https://www.cnblogs.com/luoshupeng/p/2146174.html
Copyright © 2011-2022 走看看