VS2010/MFC编程入门之五十(图形图像:GDI对象之画笔CPen)
上一节中鸡啄米讲了CDC类及其屏幕绘图函数,本节的主要内容是GDI对象之画笔CPen。
GDI对象
在MFC中,CGdiObject类是GDI对象的基类,通过查阅MSDN我们可以看到,CGdiObject类有六个直接的派生类,GDI对象主要也是这六个,分别是:CBitmap、CBrush、CFont、CPalette、CPen和CRgn。
在这六个GDI对象中,最常用的莫过于画笔和画刷了,即CPen类和CBrush类。本文就主要讲解画笔的使用。
画笔的应用实例
鸡啄米在这里直接通过一个波形图的实例,来详细讲解画笔的使用方法。
首先介绍此实例要实现的功能:在对话框上有一个Picture控件,将此控件的背景填充为黑色;启动一个定时器,每次定时器到时,所有波形数据都前移一个单位,并获取一个80以内的随机数作为波形的最后一个数据,然后以绿色画笔在绘图控件上绘制波形。这样就实现了波形的绘制及动态变化。
下面是具体实施步骤:
1、创建一个基于对话框的MFC工程,名字设为“Example50”。
2、在自动生成的对话框模板IDD_EXAMPLE50_DIALOG中,删除“TODO: Place dialog controls here.”静态文本框,添加一个Picture控件,ID设为IDC_WAVE_DRAW。
3、为Picture控件IDC_WAVE_DRAW添加CStatic变量,名称设为m_picDraw。
4、在文件Example50Dlg.h文件中CExample50Dlg类声明的上面添加宏定义:
- #define POINT_COUNT 100
此符号常量的意义是波形的点数,这里用define将其定义为符号常量是为了方便以后可能的修改,假如我们以后想将点数改为200,则只改此宏定义就可以了:#define POINT_COUNT 200,而如果没有使用符号常量,在程序中直接使用了100,那么就需要将所有使用100的位置找出来,并替换为200,这样不仅麻烦也很容易出错,所以最好是将其定义为符号常量。
5、在CExample50Dlg.h文件中为CExample50Dlg类添加成员数组:
- int m_nzValues[POINT_COUNT];
此数组用于存放波形数据。
6、在CExample50Dlg类的构造函数中为数组m_nzValues的元素赋初值:
- CExample50Dlg::CExample50Dlg(CWnd* pParent /*=NULL*/)
- : CDialogEx(CExample50Dlg::IDD, pParent)
- {
- m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
- // 将数组m_nzValues的元素都初始化为0
- memset(m_nzValues, 0, sizeof(int) * POINT_COUNT);
- }
7、在CExample50Dlg对话框的初始化成员函数CExample50Dlg::OnInitDialog()中,构造随机数生成器,并启动定时器。CExample50Dlg::OnInitDialog()修改如下:
- BOOL CExample50Dlg::OnInitDialog()
- {
- CDialogEx::OnInitDialog();
- // Add "About..." menu item to system menu.
- // IDM_ABOUTBOX must be in the system command range.
- ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
- ASSERT(IDM_ABOUTBOX < 0xF000);
- CMenu* pSysMenu = GetSystemMenu(FALSE);
- if (pSysMenu != NULL)
- {
- BOOL bNameValid;
- CString strAboutMenu;
- bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
- ASSERT(bNameValid);
- if (!strAboutMenu.IsEmpty())
- {
- pSysMenu->AppendMenu(MF_SEPARATOR);
- pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
- }
- }
- // Set the icon for this dialog. The framework does this automatically
- // when the application's main window is not a dialog
- SetIcon(m_hIcon, TRUE); // Set big icon
- SetIcon(m_hIcon, FALSE); // Set small icon
- // TODO: Add extra initialization here
- // 以时间为种子来构造随机数生成器
- srand((unsigned)time(NULL));
- // 启动定时器,ID为1,定时时间为200ms
- SetTimer(1, 200, NULL);
- return TRUE; // return TRUE unless you set the focus to a control
- }
8、为CExample50Dlg类添加波形绘制的成员函数CExample50Dlg::DrawWave(CDC *pDC, CRect &rectPicture),参数分别为设备上下文指针和绘图的矩形区域。
- void CExample50Dlg::DrawWave(CDC *pDC, CRect &rectPicture)
- {
- float fDeltaX; // x轴相邻两个绘图点的坐标距离
- float fDeltaY; // y轴每个逻辑单位对应的坐标值
- int nX; // 在连线时用于存储绘图点的横坐标
- int nY; // 在连线时用于存储绘图点的纵坐标
- CPen newPen; // 用于创建新画笔
- CPen *pOldPen; // 用于存放旧画笔
- CBrush newBrush; // 用于创建新画刷
- CBrush *pOldBrush; // 用于存放旧画刷
- // 计算fDeltaX和fDeltaY
- fDeltaX = (float)rectPicture.Width() / (POINT_COUNT - 1);
- fDeltaY = (float)rectPicture.Height() / 80;
- // 创建黑色新画刷
- newBrush.CreateSolidBrush(RGB(0,0,0));
- // 选择新画刷,并将旧画刷的指针保存到pOldBrush
- pOldBrush = pDC->SelectObject(&newBrush);
- // 以黑色画刷为绘图控件填充黑色,形成黑色背景
- pDC->Rectangle(rectPicture);
- // 恢复旧画刷
- pDC->SelectObject(pOldBrush);
- // 删除新画刷
- newBrush.DeleteObject();
- // 创建实心画笔,粗度为1,颜色为绿色
- newPen.CreatePen(PS_SOLID, 1, RGB(0,255,0));
- // 选择新画笔,并将旧画笔的指针保存到pOldPen
- pOldPen = pDC->SelectObject(&newPen);
- // 将当前点移动到绘图控件窗口的左下角,以此为波形的起始点
- pDC->MoveTo(rectPicture.left, rectPicture.bottom);
- // 计算m_nzValues数组中每个点对应的坐标位置,并依次连接,最终形成曲线
- for (int i=0; i<POINT_COUNT; i++)
- {
- nX = rectPicture.left + (int)(i * fDeltaX);
- nY = rectPicture.bottom - (int)(m_nzValues[i] * fDeltaY);
- pDC->LineTo(nX, nY);
- }
- // 恢复旧画笔
- pDC->SelectObject(pOldPen);
- // 删除新画笔
- newPen.DeleteObject();
- }
9、有了定时器和绘图成员函数,我们就可以在WM_TIMER消息的响应函数中添加对波形数据的定时处理和对波形的定时绘制了。定时器及WM_TIMER消息处理函数的添加方法如果忘记了,可以再到VS2010/MFC编程入门之四十四(MFC常用类:定时器Timer)温习下。
WM_TIMER消息的处理函数修改如下:
- void CExample50Dlg::OnTimer(UINT_PTR nIDEvent)
- {
- // TODO: Add your message handler code here and/or call default
- CRect rectPicture;
- // 将数组中的所有元素前移一个单位,第一个元素丢弃
- for (int i=0; i<POINT_COUNT-1; i++)
- {
- m_nzValues[i] = m_nzValues[i+1];
- }
- // 为最后一个元素赋一个80以内的随机数值(整型)
- m_nzValues[POINT_COUNT-1] = rand() % 80;
- // 获取绘图控件的客户区坐标
- // (客户区坐标以窗口的左上角为原点,这区别于以屏幕左上角为原点的屏幕坐标)
- m_picDraw.GetClientRect(&rectPicture);
- // 绘制波形图
- DrawWave(m_picDraw.GetDC(), rectPicture);
- CDialogEx::OnTimer(nIDEvent);
- }
10、在对话框销毁时,定时器应关闭。所以为CExample50Dlg类添加WM_DESTROY消息的处理函数,并修改如下:
- void CExample50Dlg::OnDestroy()
- {
- CDialogEx::OnDestroy();
- // TODO: Add your message handler code here
- // 关闭定时器
- KillTimer(1);
- }
11、一切准备就绪,编译运行。最终的效果如下图:
关于画笔,鸡啄米就讲到这里了,下一节将为大家简单讲讲画刷的使用。谢谢大家的关注!
VS2010/MFC编程入门之五十(图形图像:GDI对象之画笔CPen)的更多相关文章
- VS2010/MFC编程入门之五十二(Ribbon界面开发:创建Ribbon样式的应用程序框架)
上一节中鸡啄米讲了GDI对象之画刷CBrush,至此图形图像的入门知识就讲完了.从本节开始鸡啄米将为大家带来Ribbon界面开发的有关内容.本文先来说说如何创建Ribbon样式的应用程序框架. Rib ...
- VS2010/MFC编程入门之五十四(Ribbon界面开发:使用更多控件并为控件添加消息处理函数)
上一节中鸡啄米讲了为Ribbon Bar添加控件的方法.本节教程鸡啄米将继续完善前面的实例,讲解一些稍复杂的控件的添加方法,及如何为它们添加消息处理函数. 一.为Ribbon Bar添加更多Ribbo ...
- VS2010/MFC编程入门之五(MFC消息映射机制概述)
VS2010/MFC编程入门之五(MFC消息映射机制概述)-软件开发-鸡啄米 http://www.jizhuomi.com/software/147.html 上一讲鸡啄米为大家简单分析了MFC应用 ...
- VS2010/MFC编程入门之三十八(状态栏的使用详解)
上一节中鸡啄米讲了工具栏的创建.停靠与使用,本节来讲解状态栏的知识. 状态栏简介 状态栏相信大家在很多窗口中都能见到,它总是用来显示各种状态.状态栏实际上也是一个窗口,一般分为几个窗格,每个窗格分别用 ...
- VS2010/MFC编程入门之三十九(文档、视图和框架:概述)
前面几节讲了菜单.工具栏和状态栏的使用,鸡啄米本节开始将为大家讲解文档.视图和框架的知识. 文档.视图和框架简介 在VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)创建的单文档工 ...
- VS2010/MFC编程入门之三十六(工具栏:工具栏资源及CToolBar类)
上一节中鸡啄米讲了菜单及CMenu类的使用,这一节讲与菜单有密切联系的工具栏. 工具栏简介 工具栏一般位于主框架窗口的上部,菜单栏的下方,由一些带图片的按钮组成.当用户用鼠标单击工具栏上某个按钮时,程 ...
- VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
上一节讲了标签控件Tab Control以后,常用控件的内容就全部讲完了,当然并没有包括所有控件,主要是一些很常用很重要的控件.本节开始鸡啄米将为大家讲解菜单的概念及使用. 菜单简介 菜单在界面设计中 ...
- VS2010/MFC编程入门之四十九(图形图像:CDC类及其屏幕绘图函数)
上一节中鸡啄米讲了文本输出的知识,本节的主要内容是CDC类及其屏幕绘图函数. CDC类简介 CDC类是一个设备上下文类. CDC类提供了用来处理显示器或打印机等设备上下文的成员函数,还有处理与窗口客户 ...
- VS2010/MFC编程入门之五十一(图形图像:GDI对象之画刷CBrush)
上一节中鸡啄米主要讲的是画笔CPen的用法,前面也说了,GDI对象中最常用的就是画笔和画刷,本节就讲讲画刷CBrush. 鸡啄米依然是通过实例的方式来说明画刷的用法.此实例要实现的功能是,对话框上有一 ...
随机推荐
- 从浏览器输入URL到页面渲染的过程
零.背景 一个web安全工程师在学习web安全和web渗透时候,非常有必要了解整个WEB工作过程. 一.输入URL 这里是最基本的知识:URL是URI的一种实际应用,URI统一资源表示符,URL统一资 ...
- 【转】Android四大基本组件介绍与生命周期
转自:http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html Android四大基本组件分别是Activity,Serv ...
- Amazon ec2 改成密码登录方式
sudo passwd rootsu rootvi /etc/ssh/sshd_config"# PasswordAuthentication yes" uncommentsbin ...
- Core Data 迁移与版本管理
原文 http://chun.tips/blog/2014/11/28/core-data-ban-ben-qian-yi-jing-yan-zong-jie/ 主题 Core DataiOS开发 ...
- 涨知识,涨知识 :ThinkPHP框架下Where条件查询Mysql数据库某字段是否为空
代码虐我千百遍,我对代码如初恋~ 问题: 查询某字段app_date数据是否为NULL,正常我们实现的办法是: $map['app_data'] = array('eq','null'); $data ...
- 9.4Django
2018-9-4 17:19:13 昨天和基友玩到了十点 一路溜着玩喝豆腐脑,谈谈人生!感触颇深! 越努力,越幸运! 关于 Django的一开始的配置东西! 2018-9-4 19:42:27 201 ...
- 前端基础进阶之Promise
前言 Promise的重要性我认为我没有必要多讲,概括起来说就是必须得掌握,而且还要掌握透彻.这篇文章的开头,主要跟大家分析一下,为什么会有Promise出现. 在实际的使用当中,有非常多的应用场景我 ...
- [实战]MVC5+EF6+MySql企业网盘实战(2)——用户注册
写在前面 上篇文章简单介绍了项目的结构,这篇文章将实现用户的注册.当然关于漂亮的ui,这在追后再去添加了,先将功能实现.也许代码中有不合适的地方,也只有在之后慢慢去优化了. 系列文章 [EF]vs15 ...
- CVPixelBuffer
https://www.cnblogs.com/psklf/p/7700834.html https://stackoverflow.com/questions/16475737/convert-ui ...
- Celery 分布式任务队列入门
一.Celery介绍和基本使用 Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celery ...