【计算机图形学课程】二.MFC鼠标响应函数模拟画图软件

上一篇文章我们讲述MFC绘制图形的基本函数,包括绘制直线、绘制矩形、绘制椭圆及绘制文字,同时通过绕圆旋转和矩形平移简单的理解了图形学知识。这篇文章我将介绍鼠标响应和键盘响应,通过这些事件让学生实现一个类似画图的简单软件,同时充分发挥学生想象,自己创作东西。
        前文:
       【计算机图形学课程】一.MFC基本绘图函数使用方法

一. MFC工程创建及鼠标响应

新建一个MFC 单文档的应用程序"MousePic"。

然后,选择"View(视图)"->"建立类向导",快键键是Ctrl+W。这是MFC非常重要的一个知识点,对话框或单文档设置按钮操作、响应函数都是通过该操作实现。

在MFC ClassWizard中选择创建工程的"CMousePicView"类名,然后再"Message"中选择"WM_LBUTTONDOWN",鼠标左键按下响应操作。同时,双击它添加函数OnLButtonDown()。

鼠标常见消息响应:
        WM_LBUTTONDBCLK 双击鼠标左键
        WM_LBUTTONDOWN 按下鼠标左键
        WM_LBUTTONUP 释放鼠标左键
        WM_MOUSEMOVE 在客户区移动鼠标
        WM_RBUTTONDBCLK 双击鼠标右键
        WM_RBUTTONDOWN 按下鼠标右键
        WM_RBUTTONUP 释放鼠标右键

二. MFC实现鼠标响应操作

1.鼠标左键按下
        双击函数会定位到"MousePicView.cpp"文件,现在可以对OnLButtonDown()函数进行编辑。其中CPoint point参数记录当前鼠标左键按下的位置,nFlags表示掩码。

然后添加代码如下:

//定义一个点类型的变量,用来保存当用户点击界面时点击的位置
CPoint m_point;

//鼠标左键按下
void CMousePicView::OnLButtonDown(UINT nFlags, CPoint point)
{
	//把当前点击的点的位置赋给点m_point
	m_point = point;
	CView::OnLButtonDown(nFlags, point);
}

2.鼠标左键释放
        通过同样的方法在"类向导"中实现鼠标左键释放函数,如下图所示。

添加代码主要是鼠标释放(弹起):

//鼠标释放:记录当前坐标
void CMousePicView::OnLButtonUp(UINT nFlags, CPoint point)
{
	//绘制图形
	CDC *p = GetDC();
	p->MoveTo(m_point);    //鼠标移动到左键按下点
	p->LineTo(point);      //绘制一条直线 终点为鼠标释放点
	CView::OnLButtonUp(nFlags, point);
}

此时鼠标绘制图形如下所示,但是存在两个问题:绘制过程中不可见、绘制结果只是直线。

所以,需要借助鼠标移动函数实现,在鼠标移动过程中就进行绘制,同时引入bool类型的变量,判断鼠标按下或释放,按下的时候进行绘制操作。

3.鼠标左键移动
        通过同样的方法在"类向导"中实现鼠标左键释放函数。

完整代码如下所示:

//定义一个点类型的变量,用来保存当用户点击界面时点击的位置
CPoint m_point;

//定义布尔型变量 m_click=true表示鼠标点击 false表示鼠标释放
bool m_click;

//鼠标左键按下
void CMousePicView::OnLButtonDown(UINT nFlags, CPoint point)
{
	//把当前点击的点的位置赋给点m_point
	m_point = point;
	m_click = true;
	CView::OnLButtonDown(nFlags, point);
}

//鼠标释放:记录当前坐标
void CMousePicView::OnLButtonUp(UINT nFlags, CPoint point)
{
	//绘制图形
	/*
	CDC *p = GetDC();
	p->MoveTo(m_point);    //鼠标移动到左键按下点
	p->LineTo(point);      //绘制一条直线 终点为鼠标释放点
	*/
	m_click = false;
	CView::OnLButtonUp(nFlags, point);
}

//鼠标移动绘制图形
void CMousePicView::OnMouseMove(UINT nFlags, CPoint point)
{
	//定义画笔并选择
	CDC *p=GetDC();
	CPen pen(PS_SOLID, 4, RGB(255,0,0));
	p->SelectObject(pen);

	//鼠标按下进行绘制
	if(m_click==true) {
		p->MoveTo(m_point);
		p->LineTo(point);
		m_point = point;
	}
	CView::OnMouseMove(nFlags, point);
}

绘制结果如下所示,相当于一个简单的画图软件。

        4.补充知识
        如果在OnMouseMove()鼠标移动函数if判断中缺少代码m_point = point,它会出现意想不到的效果,因为你需要每次绘制,鼠标移动当前点坐标point都需要赋值给下次绘制的起始坐标,供p->MoveTo(m_point)使用。

同时,你可以绘制圆形、矩形等相关形状,不仅仅限定于直线。

//鼠标移动绘制图形
void CMousePicView::OnMouseMove(UINT nFlags, CPoint point)
{
	//定义画笔并选择
	CDC *p=GetDC();
	CPen pen(PS_SOLID, 1, RGB(255,0,0));
	p->SelectObject(pen);

	//鼠标按下进行绘制
	if(m_click==true) {
		p->MoveTo(m_point);
		//p->LineTo(point);
		p->Rectangle(point.x, point.y,point.x+20, point.y+30);
		m_point = point;
	}
	CView::OnMouseMove(nFlags, point);
}

输出如下所示:

绘制中,定义了画笔Pen,正确的方法需要在绘制完成后,进行释放该画笔。核心代码如下:

	//定义画笔绘制矩形
    CPen MyPen, *OldPen;
    MyPen.CreatePen(PS_DASH, 2, RGB(0,0,255)); //虚线 粗2 蓝色
    OldPen = pDC->SelectObject(&MyPen);        //旧画笔赋值  

	//绘制图形

	//清除
    pDC->SelectObject(OldPen);
    MyPen.DeleteObject();  

三. MFC键盘响应函数

1.基础知识
        Windows对每个按键定义了与设备无关的编码,这种编码叫做虚拟码。有了这个虚拟码,Windwos程序员可以使用该虚拟码进行编程。其中键盘上部分按键的虚拟码如下图所示:

Windows按键消息常见如下:
        WM_CHAR 敲击键盘上的字符键时,产生该消息
        WM_KEYDOWN 任意键(包括字符键)被按下时都产生该消息,如果被按下的是字符键,在产生消息的同时还产生字符消息
        WM_KEYUP 任意角(包括字符键)被释放都产生该消息 
        WM_SYSKEYDOWN F10被按下或者Alt与另一个键被同时按下
        WM_SYSKEYUP F10被释放或者Alt与另一个键被同时释放

2.按键响应操作
        同样,通过类向导建立按键按下函数。

然后添加如下代码,按下任意一个键,绘制的矩形向右平移40距离。

//鼠标按键
void CMousePicView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: Add your message handler code here and/or call default

	CDC *p = GetDC();
	p->MoveTo(m_point);       //键盘按下
	m_point.x += 40;          //水平平移40
	p->Rectangle(m_point.x, m_point.y, m_point.x+20, m_point.y+30);
	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

绘制图形如下所示:

3.响应不同键盘的操作
        需要将UINT nChar转换为Char字符型,然后进行盘,WASD进行上下左右移动绘制椭圆。

//鼠标按键
void CMousePicView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: Add your message handler code here and/or call default

	CDC *p = GetDC();
	char cChar;               //当前被按下的字符
    cChar = char(nChar);      //将按下的键转换为字符

	//定义画笔
    CPen MyPen, *OldPen;
    MyPen.CreatePen(PS_DASH, 2, RGB(0,0,255));  //虚线 粗2 蓝色
    OldPen = p->SelectObject(&MyPen);           //旧画笔赋值  

    //画刷
    CBrush MyBrush, *OldBrush;
    MyBrush.CreateSolidBrush(RGB(0,255,0));
    OldBrush = p->SelectObject(&MyBrush);  

    if (cChar == ‘D‘) {
		p->MoveTo(m_point);       //D键按下
		m_point.x += 40;          //水平向右平移40
		p->Ellipse(m_point.x, m_point.y, m_point.x+20, m_point.y+40);
	}
	if (cChar == ‘A‘) {
		p->MoveTo(m_point);       //A键按下
		m_point.x -= 40;          //水平向左平移40
		p->Ellipse(m_point.x, m_point.y, m_point.x+20, m_point.y+40);
	}
	if (cChar == ‘S‘) {
		p->MoveTo(m_point);       //S键按下
		m_point.y += 50;          //竖直向下平移50
		p->Ellipse(m_point.x, m_point.y, m_point.x+20, m_point.y+40);
	}
	if (cChar == ‘W‘) {
		p->MoveTo(m_point);       //W键按下
		m_point.y -= 50;          //竖直向上平移50
		p->Ellipse(m_point.x, m_point.y, m_point.x+20, m_point.y+40);
	}

	//清除
    p->SelectObject(OldPen);
    MyPen.DeleteObject();
    p->SelectObject(OldBrush);
    MyBrush.DeleteObject(); 

	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

绘制如下图所示:

4.按键光标选择

//鼠标按键
void CMousePicView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	//光标操作
    char cChar;               //当前被按下的字符
    HCURSOR hCursor = 0;      //显示光标句柄
    HCURSOR hPrevCursor = 0;  //以前的光标句柄
    cChar = char(nChar);      //将按下的键转换为字符
    if (cChar == ‘A‘){
        //加载箭头光标
        hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
    }
    if (cChar == ‘B‘){
        //加载箭头光标
        hCursor = AfxGetApp()->LoadStandardCursor(IDC_IBEAM);
    }
    if (cChar == ‘C‘){
        //加载箭头光标
        hCursor = AfxGetApp()->LoadStandardCursor(IDC_WAIT);
    }
    if (cChar == ‘X‘){
        hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
        hPrevCursor = SetCursor(hCursor);
        if (hPrevCursor)
            DestroyCursor(hPrevCursor);
    }
    else{
        if (hCursor){
            hPrevCursor = SetCursor(hCursor);
            if (hPrevCursor)
                DestroyCursor(hPrevCursor);
        }
    }

	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

四. MFC鼠标绘制-学生作业展示

最后展示学生做的成果,虽然代码非常简单,原理也很简单,但是学生做得真的挺好的,原来编程还可以这样上啊,一方面提升学生的学习兴趣,另一方面增加他们的编程能力。

  

  

  

 

还是那句话,非常佩服学生的创造力及想象力吧!而且编程课原来可以这么进行,提升学生的编程能力的同时也培养了学生的兴趣。希望文章对你有所帮助~
        (By:Eastmount 2016-11-20 半夜2点半 http://blog.csdn.net/eastmount/ )

时间: 2024-12-24 21:52:31

【计算机图形学课程】二.MFC鼠标响应函数模拟画图软件的相关文章

计算机图形学(二)——微表面模型

计算机图形学中基于物理建模的渲染技术之所以能给人极佳的视觉体验,是因为利用这些渲染技术能够很真实的反映出每种物体独有的"质感".我们能通过人眼观察来感受物体表面"质感"的原因,也是因为物体表面反射周围环境的特性不同而造成的,因此对物体表面的物理建模对于其表面本身的质感表现至关重要.对物体表面的建模,最简单的是镜面模型.利用镜面模型渲染出的物体具有十分光滑的感觉.然而现实生活中很多物体表面一般是粗糙的,因此为了对上述的一般表面进行物理建模并将该物理模型应用到实际的渲染

计算机图形学(二)输出图元_10_多边形填充区_1_多边形分类和识别

多边形填充区 一个多边形(polygon)在数学上定义为由三个或者更多称为顶点的坐标位置描述的平面图形,这些顶点由称为多边性的边(edge或者side)顺序连接.进一步来看,几何上要求多边形的边除了端点之外没有其他公共点.因此,根据定义,一个多边形在其单一平面上必须有其所有的顶点且边之间无交叉.多边形的例子有三角形.矩形.八边形和十六边形等.有时,任一有封闭折线边界的平面图形暗指一个多边形,而若其没有交叉边则称为标准多边形(( standard polygon)或简单多边形( simple po

计算机图形学(二)输出图元_20_章节总结(上)

输出图元章节总结 发了好久第2章终于结束了,本章中讨论的输出图元为使用直线.曲线.填充区域.单元阵列样式和文本构造图形提供了基本的工具.我们通过在笛卡儿世界坐标系统中给出几何描述来指定图元. 用于沿线段路径绘制像素的三种方法是DDA算法.Bresenham算法和中点算法,Bresenham算法和中点算法是等同的并且是最有效的.沿线段路径的像素的颜色位存储按照递增地计算内存地址的方式而有效完成.任何线段生成算法都可以通过分割线段并将分割的线段分布到可用处理器上来获得并行的实现. 圆和椭圆采用中点算

计算机图形学(二)输出图元_6_OpenGL曲线函数_1_圆生成算法

OpenGL曲线函数 生成圆和椭圆等基本曲线的函数并未作为图元功能包含在OpenGL核心库中.但该库包含了显示Bezier样条的功能,该曲线是由一组离散点定义的多项式.OpenGL实用库(GLU)中包含有球面和柱面等三维曲面函数以及生成B样条的函数,它是包含简化Bezier曲线的样条曲线的总集.我们可以使用有理B样条显示圆.椭圆和其他二维曲线.此外,OpenGL实用工具包(GLUT)中还有可以用来显示某些三维曲面(如球面.锥面和其他形体)的函数.然而,所有这些函数比本章中介绍的基本图元应用得更多

计算机图形学(二)输出图元_10_多边形填充区_8_OpenGL顶点数组

OpenGL顶点数组 尽管前面给出的例子中只包含少量的坐标位置,但描述包含若干个对象的场景一般会复杂得多.我们先考虑描述一个简单的很基本的对象:图3.58中的单位立方体,为简化后面的讨论而使用了整数坐标.定义顶点坐标的直接方法是用一个双下标数组,例如: GLint points [8][3] = {{0,0,0},{0,1,0},{1,0,0},{1,1,0},{0,0,1},{0,1,1},{1,0,1},{1,1,1}}; 也可以先定义一个三维顶点位置的数据类型,然后给出作为单下标数组元素的

计算机图形学(二)输出图元_10_多边形填充区_7_OpenGL多边形填充区函数(上)

OpenGL多边形填充区函数(上) 描述填充多边形的OpenGL过程与描述点和折线类似,但有一个例外.函数glVertex用来输入多边形的一个顶点坐标,而完整的多边形用从glBegin到glEnd之间的一组顶点来描述.但有另外一个函数可以用来显示具有完全不同格式的矩形. 默认时多边形内部显示为单色,由当前颜色设定确定其颜色.作为选项(下面的内容中叙述),可以用图案填充多边形且显示多边形的边作为内部填充的边界.函数glBegin中指定多边形填充区的变量可使用6个不同的符号常量.这6个基本常量可用来

计算机图形学(二)输出图元_2_ OpenGL画线函数

 OpenGL画线函数 图形软件包一般都提供一个描述一条或多条直线段的函数,其中每一直线段由两个端点坐标位置定义.在OpenGL中,和选择一个点位置一样,我们使用glVertex函数选择单个端点的坐标位置.我们使用一对glBegin/g1End来引入一串端点位置.有三个OpenGL符号常量可用于指定如何把这一串端点位置连接成一组直线段.默认情况下,每一符号常量显示白色实线. 使用图元线常量GL_LINES可连接每一对相邻端点而得到一组直线段.通常,这会导致一组未连接的线段,除非重复某些坐标位置.

计算机图形学(二)输出图元_19_显示窗口重定形函数

OpenGL显示窗口重定形函数 在介绍性的OpenGL程序中,我们讨论了建立初始显示窗口的函数.但是在生成图形后,常需要用鼠标将显示窗口拖到屏幕的另一位置或改变其形状.改变显示窗口的尺寸可能改变其纵横比并引起对象形状的改变. 为了允许对显示窗口尺寸的改变做出反应,GLUT库提供下面的函数: glutReshapeFunc(winReshapeFcn); 该函数可和其他GLUT函数一起放在程序的主过程中,它在显示窗口尺寸输入后立即激活.该GLUT函数的变量是接受新窗口宽度和高度的过程名.我们可以接

计算机图形学(二)输出图元_10_多边形填充区_3_内-外测试

内-外测试 各种图形处理经常需要鉴别对象的内部区域.识别简单对象如凸多边形.圆或椭圆的内部通常是一件很容易的事情.但有时我们必须处理较复杂的对象.例如,我们可能描述一个图3.46所示的有相交边的复杂填充区.在该形状中,xy平面上哪一部分为对象边界的"内部".哪一部分为"外部"并不总是一目了然的.奇偶规则和非零环绕规则是识别平面图形内部区域的两种常用方法. 奇偶规则(odd-even rule)也称奇偶性规则(odd-parity rule)或偶奇规则(even-od