鼠标绘制简单图形

Posted Autumn の Box

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了鼠标绘制简单图形相关的知识,希望对你有一定的参考价值。

功能:

1、绘制的图形包括直线、椭圆和矩形,通过菜单对绘制的图形切换

2、在视图中使用鼠标动态的绘制图形

分析:

当窗口尺寸发生变化时,引起窗口重绘,会发送WM_PAINT消息,这时首先会擦除窗口的背景,然后再进行重绘操作,这样就把窗口中先前绘制的图形擦除掉了;可以将绘制图形的三要素(起点、终点、绘制类型)保存下来,在窗口重绘调用程序视图类窗口的OnDraw函数中再将图形根据保存的三要素重新输出;

  1、数据

    在视图中添加两个点坐标

    CPoint m_ptOrigin;  //起点坐标

    CPoint m_ptEnd;  //终点坐标

    UINT m_nDrawType;  //图形的类型,0:不绘制;1:直线;2:椭圆;3:矩形;

    BOOL m_bFlag;  //是否绘制图形

  2、绘制过程

    2.1--LBUTTONDOWN

      1)保存图形的起点

      2)m_bFlag=TRUE;  //开始绘制

    2.2--MOUSEMOVE

      if(m_bFlag){

        //擦除旧线

        //画新线        

      }

    2.3--LBUTTONUP

      m_bFlag=FALSE;  //绘制结束

实现:

创建单文档类型的MFC工程:Graphic,在资源窗口中的菜单栏中添加顶层菜单项“绘图”,并添加下拉菜单项:IDM_DOT(点)、IDM_LINE(直线)、IDM_RECTANGLE(矩形)、IDM_ELLIPSE(椭圆);在工程的视图类CGraphicView类中添加私有成员变量:UINT m_nDrawType,并在视图类的构造函数中将其初始化为0,在不同的菜单项的消息响应函数中将m_nDrawType设为不同的值以标识不同的绘制图形:点--1、直线--2、矩形--3、椭圆--4;

在视图类CGraphicView类中添加私有成员变量:CPoint m_ptOrigin,并在视图类的构造函数中将该变量初始化为0,在鼠标左键按下的消息响应函数中用以保存左键按下时的坐标;

  

  

void CGraphicView::OnLButtonDown(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    m_ptOrigin=point;
    
    CView::OnLButtonDown(nFlags, point);
}

在鼠标左键松开消息响应函数中实现绘图功能:

  

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    CClientDC dc(this);

    //创建画笔:PS_SOLID/PS_DASH/PS_DOT
    CPen pen(PS_SOLID,1,RGB(255,0,0));
    CPen* pOldPen=dc.SelectObject(&pen);

    //创建画刷,默认的画刷为白色填充,这里创建的是透明无填充的画刷
    //CBrush::FromHandle--将句柄转换为对象
    CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
    CBrush *pOldBrush=dc.SelectObject(pBrush);

    switch(m_nDrawType){
        case 1:
            //画点
            dc.SetPixel(point,RGB(255,0,0));
            break;
        case 2:
            //画线
            dc.MoveTo(m_ptOrigin);
            dc.LineTo(point);
            break;
        case 3:
            //画矩形线框,无填充
            dc.Rectangle(CRect(m_ptOrigin,point));
            break;
        case 4:
            //画椭圆线框,无填充
            dc.Ellipse(CRect(m_ptOrigin,point));
            break;
    }

    dc.SelectObject(pOldPen);
    dc.SelectObject(pOldBrush);
    
    CView::OnLButtonUp(nFlags, point);
}
 

右击工程名称增加一个新类:CGraph:

  

为CGraph类添加3个成员变量:

  UINT    m_nDrawType  绘制类型

  CPoint  m_ptOrigin   起点

  CPoint  m_ptEnd      终点

  

  

  

1、添加“设置”对话框

在工程的菜单栏资源的顶层菜单栏“绘图”下添加下拉菜单项“设置”:

  

为“设置”菜单项添加一个对应的对话框资源,在对话框中允许用户进行相关的绘图设置:

  

双击对话框资源创建相应的对话框类:

  

  

在对话框资源中添加一个静态文本框“线宽”和一个编辑框:

  

利用MFC类向导为编辑框IDC_LINE_WIDTH关联一个成员变量,用来设置线宽:

  

为“设置”菜单项添加命令响应,并选择视图类CGraphicView类对此消息进行响应:

  

  因为要在视图类中引用新建的对话框类,故需要在视图类的源文件中包含新建的对话框类的头文件:

  #include "SettingDlg.h" 

void CGraphicView::OnSetting() 
{
    // TODO: Add your command handler code here
    CSettingDlg dlg;
    dlg.DoModal();  //显示对话框
    
}

在视图类CGraphicView类中添加私有成员变量:UINT m_nLineWidth,并在视图类的构造函数中将其初始化为0,用来保存用户在对话框的线宽编辑框中设置的线宽;

  

用户在线宽编辑框输入线宽后,若单击【确定】按钮则保存这个线宽值,若单击【取消】按钮则不保存线宽值:

void CGraphicView::OnSetting() 
{
    // TODO: Add your command handler code here
    CSettingDlg dlg;
    dlg.m_nLineWidth=m_nLineWidth;  //保存上次设置的线宽值
    if(IDOK==dlg.DoModal()){
        m_nLineWidth=dlg.m_nLineWidth;  //调用新填写的线宽值
    }
    
}

在创建画笔对象时,其宽度用m_nLineWidth变量来代替:

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    CClientDC dc(this);

    CPen pen(PS_SOLID,m_nLineWidth,RGB(255,0,0));
    CPen* pOldPen=dc.SelectObject(&pen);

    CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
    CBrush *pOldBrush=dc.SelectObject(pBrush);

    switch(m_nDrawType){
        case 1:
            dc.SetPixel(point,RGB(255,0,0));
            break;
        case 2:
            dc.MoveTo(m_ptOrigin);
            dc.LineTo(point);
            break;
        case 3:
            dc.Rectangle(CRect(m_ptOrigin,point));
            break;
        case 4:
            dc.Ellipse(CRect(m_ptOrigin,point));
            break;
    }

    dc.SelectObject(pOldPen);
    dc.SelectObject(pOldBrush);
    
    CView::OnLButtonUp(nFlags, point);
}

在“设置”对话框中添加一个设置线型的组框IDC_LINE_STYLE,在组框中放置3个单选按钮,对应“实线”、“虚线”、“点线”,勾选第一个单选按钮的属性中的“组”,使其成为改组单选按钮组的组长;

  

利用MFC类向导为这组单选按钮关联一个成员变量: int m_nLineStyle,以成员变量的值来区分选中哪个单选按钮:0--实线、1--虚线、2--点线、-1--未选中;

  

在程序的视图类CGraphicView类中添加私有成员变量:int m_nLineStyle,并在视图类的构造函数中将其初始化为0:

   

void CGraphicView::OnSetting() 
{
    // TODO: Add your command handler code here
    CSettingDlg dlg;
    dlg.m_nLineWidth=m_nLineWidth;  //保存上次设置的线宽值
    dlg.m_nLineStyle=m_nLineStyle;  //保存上次设置的线型
    if(IDOK==dlg.DoModal()){
        m_nLineWidth=dlg.m_nLineWidth;  //调用新填写的线宽值
        m_nLineStyle=dlg.m_nLineStyle;  //调用新选择的线型
    }
    
}

PS_SOLID/PS_DASH/PS_DOT这些线型在WINGDI.h文件中定义时均对应有0/1/2的值关联,恰好与m_nLineStyle的取值相对应,故可以在创建画笔对象时直接将线型参数替换为m_nLineStyle

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    CClientDC dc(this);

    CPen pen(m_nLineStyle,m_nLineWidth,RGB(255,0,0));
    CPen* pOldPen=dc.SelectObject(&pen);

    CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
    CBrush *pOldBrush=dc.SelectObject(pBrush);

    switch(m_nDrawType){
        case 1:
            dc.SetPixel(point,RGB(255,0,0));
            break;
        case 2:
            dc.MoveTo(m_ptOrigin);
            dc.LineTo(point);
            break;
        case 3:
            dc.Rectangle(CRect(m_ptOrigin,point));
            break;
        case 4:
            dc.Ellipse(CRect(m_ptOrigin,point));
            break;
    }

    dc.SelectObject(pOldPen);
    dc.SelectObject(pOldBrush);
    
    CView::OnLButtonUp(nFlags, point);
}

2、添加颜色对话框

  利用MFC提供的CColorDialog类可创建一个颜色对话框:

    CColorDialog(

      COLORREF clrInit = 0,  //指定默认的颜色选择,默认为黑色

      DWORD dwFlags = 0,  //指定一组标记,用来定制颜色对话框的功能和外观

      CWnd* pParentWnd = NULL  //指向颜色对话框父窗口或拥有者窗口的指针

    );

   在程序的菜单栏资源中的顶层菜单项“绘图”中增加“颜色”下拉菜单项:

    

  选择视图类CGraphicView类对此菜单项的命令做出相应:

    

  为视图类CGraphicView类添加一个私有成员变量:COLORREF m_clr,并在视图类的构造函数中将其初始化为黑色,用于保存用户选择的颜色;

    

    m_clr=RGB(0,0,0);  

void CGraphicView::OnColor() 
{
    // TODO: Add your command handler code here
    CColorDialog dlg;
    /*CColorDialog类有一个CHOOSECOLOR结构体类型的成员变量m_cc,
        m_cc.rgbResult中就保存了用户所选择的颜色*/
    dlg.m_cc.Flags |= CC_RGBINIT;  //设置颜色对话框选中已选颜色
    dlg.m_cc.rgbResult=m_clr;  //保存上次设置的颜色
    if(IDOK==dlg.DoModal()){
        m_clr=dlg.m_cc.rgbResult;  //调用新选择的颜色
    }
    
}

  在创建画笔时,将颜色参数替换为变量m_clr   

void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    CClientDC dc(this);

    CPen pen(m_nLineStyle,m_nLineWidth,m_clr);
    CPen* pOldPen=dc.SelectObject(&pen);

    CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
    CBrush *pOldBrush=dc.SelectObject(pBrush);

    switch(m_nDrawType){
        case 1:
            dc.SetPixel(point,m_clr);
            break;
        case 2:
            dc.MoveTo(m_ptOrigin);
            dc.LineTo(point);
            break;
        case 3:
            dc.Rectangle(CRect(m_ptOrigin,point));
            break;
        case 4:
            dc.Ellipse(CRect(m_ptOrigin,point));
            break;
    }

    dc.SelectObject(pOldPen);
    dc.SelectObject(pOldBrush);
    
    CView::OnLButtonUp(nFlags, point);
}

3、添加字体对话框

  利用MFC提供的CFontDialog类创建字体对话框:

    CFontDialog(

      LPLOGFONT lplfInitial = NULL,  //指向LOGFONT结构体的指针,允许用户设置一些字体的特征

      DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS,  //设置一个或多个与选择的字体相关的标记

      CDC* pdcPrinter = NULL,  //指向打印设备上下文的指针

      CWnd* pParentWnd = NULL  //指向字体对话框父窗口的指针

      );

   在程序菜单栏资源的顶层菜单项“绘图”中添加“字体”下拉选项:

    

  为“字体”菜单项添加命令响应,选择视图类CGraphicView类对此命令做出响应,在响应函数中创建并显示字体对话框:

    

  在工程的视图类CGraphicView类中添加一个私有成员变量:CFont m_font,用以保存用户选择的字体;再在视图类中添加另一个私有成员变量:CString m_strFontName,并在视图类的构造函数中将其初始化为空,用以保存所选字体的名称;

    

    

    m_strFontName="";   

void CGraphicView::OnFont() 
{
    // TODO: Add your command handler code here
    CFontDialog dlg;
    //CFontDialog类有一个CHOOSEFONT结构体类型的数据成员:m_cf
    //m_cf.lpLogFont--指向逻辑字体的指针
    //m_cf.lpLogFont->lfFaceName--存放字体的名称
    if(IDOK==dlg.DoModal()){
        //设置新字体前先判断是否已有指定的字体,如果有就先释放该字体资源
        if(m_font.m_hObject){  
            m_font.DeleteObject();
        }
        m_font.CreateFontIndirect(dlg.m_cf.lpLogFont);  //调用指定的逻辑字体
        m_strFontName=dlg.m_cf.lpLogFont->lfFaceName;  //保存所选字体的名称
        Invalidate(TRUE);  //窗口重绘,以在视图类的OnDraw函数中显示所选字体
    }

}
void CGraphicView::OnDraw(CDC* pDC)
{
    CGraphicDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    // TODO: add draw code for native data here
    CFont *pOldFont=pDC->SelectObject(&m_font);
    pDC->TextOut(0,0,m_strFontName);
    pDC->SelectObject(pOldFont);
}

4、示例对话框

  在程序的“设置”对话框中添加一个组框,当用户选择线宽和线型后能直接在组框中显示示例;

    

  当线宽的编辑框中的文本改变时,会向其父窗口(对话框)发送EN_CHANGE消息;当单选按钮被点击时,也会向其父窗口(对话框)发送BN_CLICKED消息;对话框的CSettingDlg类,需要捕获这两个通知消息,以反映出用户所做的改变;利用MFC类向导为CSettingDlg类添加编辑框(IDC_LINE_WIDTH)的EN_CHANGE消息的响应函数,和三个单选按钮的BN_CLICKED消息响应函数:

  

  

  不可能在编辑框及单选按钮的这4个消息响应函数中都绘制示例线条,可以在这4个响应函数中调用Invalidate函数进行窗口重绘,均会发送WM_PAINT消息,只需在该WM_PAINT消息的响应函数中绘制示例线条即可;为对话框类CSettingDlg类添加WM_PAINT消息的响应函数;

     

void CSettingDlg::OnChangeLineWidth() 
{
    // TODO: If this is a RICHEDIT control, the control will not
    // send this notification unless you override the CDialog::OnInitDialog()
    // function and call CRichEditCtrl().SetEventMask()
    // with the ENM_CHANGE flag ORed into the mask.
    
    // TODO: Add your control notification handler code here
    Invalidate(TRUE);
    
}
void CSettingDlg::OnRadio1() 
{
    // TODO: Add your control notification handler code here
    Invalidate(TRUE);
    
}

void CSettingDlg::OnRadio2() 
{
    // TODO: Add your control notification handler code here
    Invalidate(TRUE);
    
}

void CSettingDlg::OnRadio3() 
{
    // TODO: Add your control notification handler code here
    Invalidate(TRUE);
    
}
void CSettingDlg::OnPaint() 
{
    CPaintDC dc(this); // device context for painting
    
    // TODO: Add your message handler code here
    UpdateData();  //当对话框中的控件值发生改变时要先更新一下
    CPen pen(m_nLineStyle,m_nLineWidth,RGB(0,0,0));
    dc.SelectObject(&pen);

    CRect rect;
    GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);  //获取“示例”组框的矩形区域大小
    ScreenToClient(&rect);  //由屏幕坐标转化为客户坐标

    dc.MoveTo(rect.left+20,rect.top+rect.Height()/2);
    dc.LineTo(rect.right-20,rect.top+rect.Height()/2);
    
    // Do not call CDialog::OnPaint() for painting messages
}

 

以上是关于鼠标绘制简单图形的主要内容,如果未能解决你的问题,请参考以下文章

使用鼠标和图形缩放

怎样用h5canvas鼠标绘制图形

OpenCV-Python实战(番外篇)——OpenCV中利用鼠标事件动态绘制图形

processing基本图形绘制

使用canvas画布利用js通过鼠标实现矩形的绘制(任意方向的绘制图形)

绘制非常大的图形上下文的最佳实践