转自:http://www.jizhuomi.com/software/246.html

上一节讲了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类添加成员数组:

C++代码
int   m_nzValues[POINT_COUNT]; 

此数组用于存放波形数据。

6、在CExample50Dlg类的构造函数中为数组m_nzValues的元素赋初值:

C++代码:
CExample50Dlg::CExample50Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CExample50Dlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); // 将数组m_nzValues的元素都初始化为0
memset(m_nzValues, , sizeof(int) * POINT_COUNT);
}

7、在CExample50Dlg对话框的初始化成员函数CExample50Dlg::OnInitDialog()中,构造随机数生成器,并启动定时器。CExample50Dlg::OnInitDialog()修改如下:

C++代码
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(, , NULL); return TRUE; // return TRUE unless you set the focus to a control
}
 

8、为CExample50Dlg类添加波形绘制的成员函数CExample50Dlg::DrawWave(CDC *pDC, CRect &rectPicture),参数分别为设备上下文指针和绘图的矩形区域。

C++代码
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 - );
fDeltaY = (float)rectPicture.Height() / ; // 创建黑色新画刷
newBrush.CreateSolidBrush(RGB(,,));
// 选择新画刷,并将旧画刷的指针保存到pOldBrush
pOldBrush = pDC->SelectObject(&newBrush);
// 以黑色画刷为绘图控件填充黑色,形成黑色背景
pDC->Rectangle(rectPicture);
// 恢复旧画刷
pDC->SelectObject(pOldBrush);
// 删除新画刷
newBrush.DeleteObject(); // 创建实心画笔,粗度为1,颜色为绿色
newPen.CreatePen(PS_SOLID, , RGB(,,));
// 选择新画笔,并将旧画笔的指针保存到pOldPen
pOldPen = pDC->SelectObject(&newPen); // 将当前点移动到绘图控件窗口的左下角,以此为波形的起始点
pDC->MoveTo(rectPicture.left, rectPicture.bottom);
// 计算m_nzValues数组中每个点对应的坐标位置,并依次连接,最终形成曲线
for (int i=; 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消息的处理函数修改如下:

C++代码
void CExample50Dlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: Add your message handler code here and/or call default
CRect rectPicture; // 将数组中的所有元素前移一个单位,第一个元素丢弃
for (int i=; i<POINT_COUNT-; i++)
{
m_nzValues[i] = m_nzValues[i+];
}
// 为最后一个元素赋一个80以内的随机数值(整型)
m_nzValues[POINT_COUNT-] = rand() % ; // 获取绘图控件的客户区坐标
// (客户区坐标以窗口的左上角为原点,这区别于以屏幕左上角为原点的屏幕坐标)
m_picDraw.GetClientRect(&rectPicture); // 绘制波形图
DrawWave(m_picDraw.GetDC(), rectPicture); CDialogEx::OnTimer(nIDEvent);
}
  

10、在对话框销毁时,定时器应关闭。所以为CExample50Dlg类添加WM_DESTROY消息的处理函数,并修改如下:

C++代码
void CExample50Dlg::OnDestroy()
{
CDialogEx::OnDestroy(); // TODO: Add your message handler code here // 关闭定时器
KillTimer();
}
 
 

11、一切准备就绪,编译运行。最终的效果如下图:

关于画笔就讲到这里了,下一节将为大家简单讲讲画刷的使用。谢谢大家的关注!

VS2010-MFC(图形图像:GDI对象之画笔CPen)的更多相关文章

  1. VS2010/MFC编程入门之五十(图形图像:GDI对象之画笔CPen)

    上一节中鸡啄米讲了CDC类及其屏幕绘图函数,本节的主要内容是GDI对象之画笔CPen. GDI对象 在MFC中,CGdiObject类是GDI对象的基类,通过查阅MSDN我们可以看到,CGdiObje ...

  2. MFC图形图像

    一.CDC类 CDC类简介 CDC类是一个设备上下文类. CDC类提供了用来处理显示器或打印机等设备上下文的成员函数,还有处理与窗口客户区关联的显示上下文的成员函数.使用CDC的成员函数可以进行所有的 ...

  3. 每天学一点---图形图像GDI编程

    首先先了解什么是 GDI 呢?GDI 是从 Windows 95 到 Windows 2000 随附的旧版绘图装置接口 (Graphics Device Interface), 是属于绘图方面的 AP ...

  4. VS2010/MFC编程入门教程之目录和总结

    鸡啄米的这套VS2010/MFC编程入门教程到此就全部完成了,虽然有些内容还未涉及到,但帮助大家进行VS2010/MFC的入门学习业已足够.以此教程的知识为基础,学习VS2010/MFC较为深入的内容 ...

  5. GDI+图形图像技术1

    System.Drawing命名空间提供了对GDI+基本图形功能的访问,其中一些子命名空间中提供了更高级的功能. GDI+由GDI发展而来,是Windows图形显示程序与实际物理设备之间的桥梁. GD ...

  6. VS2010/MFC编程入门之五十一(图形图像:GDI对象之画刷CBrush)

    上一节中鸡啄米主要讲的是画笔CPen的用法,前面也说了,GDI对象中最常用的就是画笔和画刷,本节就讲讲画刷CBrush. 鸡啄米依然是通过实例的方式来说明画刷的用法.此实例要实现的功能是,对话框上有一 ...

  7. VS2010/MFC编程入门之四十九(图形图像:CDC类及其屏幕绘图函数)

    上一节中鸡啄米讲了文本输出的知识,本节的主要内容是CDC类及其屏幕绘图函数. CDC类简介 CDC类是一个设备上下文类. CDC类提供了用来处理显示器或打印机等设备上下文的成员函数,还有处理与窗口客户 ...

  8. VS2010-MFC(图形图像:GDI对象之画刷CBrush)

    转自:http://www.jizhuomi.com/software/248.html 上一节讲的是画笔CPen的用法,前面也说了,GDI对象中最常用的就是画笔和画刷,本节就讲讲画刷CBrush. ...

  9. MFC绘图相关GDI工具对象和函数介绍

    在利用MFC进行界面编程时,除了需要熟悉各种类型控件的操作外,还会经常遇到图形绘制和显示的问题,比如时频分析界面.图像处理界面等.处理这些软件界面开发问题时,不可避免地需要用到一系列GDI工具对象和相 ...

随机推荐

  1. 反编译字节码角度分析synchronized关键字的原理

    1.synchronized介绍 synchronized是java关键字.JVM规范中,synchronized关键字用于在线程并发执行时,保证同一时刻,只有一个线程可以执行某个代码块或方法:同时还 ...

  2. 【Bootstrap】 框架 栅格布局系统设计原理

    前提条件(Bootstrap 自带) 首先使用这个布局之前要定义一下代码: 这行代码如果不懂,可以搜索一下,总之大致意思就是,被定义的元素的内边距和边框不再会增加它的宽度,不加入的话排版会有问题. 不 ...

  3. 今天工作整整一个月了,来记录一下(web前端)

    25号,工作整整一个月了,时光飞逝, 这一个月以来,无论是工作上还是生活上,都让我成长了很多,也多了些对人生的思考… 先回顾一下找工作的那段时间吧年前找工作有多辛酸,年后找工作就有多幸运那段日子,我这 ...

  4. Hbase的安装和部署

    1.Zookeeper集群的正常部署并启动 $ /opt/modules/cdh/zookeeper-3.4.5-cdh5.3.6/bin/zkServer.sh start 2.Hadoop集群的正 ...

  5. NX二次开发-UFUN求对象的最大边界框UF_MODL_ask_bounding_box

    NX9+VS2012 #include <uf.h> #include <uf_obj.h> #include <uf_modl.h> #include <u ...

  6. 秒懂神经网络---BP神经网络具体应用不能说的秘密.

    秒懂神经网络---BP神经网络具体应用不能说的秘密 一.总结 一句话总结: 还是要上课和自己找书找博客学习相结合,这样学习效果才好,不能单视频,也不能单书 BP神经网络就是反向传播神经网络 1.BP神 ...

  7. Spring Boot 遇到空指针

    @Autowired private IRoadRescueService roadRescueService; 千万不要把注入的service类设为static

  8. 8.RabbitMQ 消息传递Java对象

    通过消息服务器传递Java对象,Java类必须实现序列化接口,可以把Java对象转化为字节数组,从消费者或生产者传递到另外一个JVM中,一定需要两个JVM共享这个类,比如是UserInfo类.   1 ...

  9. HDU1556-Color the ball-前缀和/线段树/树状数组

    N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色.但 ...

  10. java 判断int类型为空

    int id = 10; if("0".equals(String.valueOf(id)) || "null".equals(String.valueOf(i ...