上一节中鸡啄米讲了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类声明的上面添加宏定义:
C++代码 #definePOINT_COUNT100
此符号常量的意义是波形的点数,这里用define将其定义为符号常量是为了方便以后可能的修改,假如我们以后想将点数改为200,则只改此宏定义就可以了:#define POINT_COUNT 200,而如果没有使用符号常量,在程序中直接使用了100,那么就需要将所有使用100的位置找出来,并替换为200,这样不仅麻烦也很容易出错,所以最好是将其定义为符号常量。
5、在CExample50Dlg.h文件中为CExample50Dlg类添加成员数组:
C++代码 intm_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的元素都初始化为0memset(m_nzValues,0,sizeof(int)*POINT_COUNT); }
7、在CExample50Dlg对话框的初始化成员函数CExample50Dlg::OnInitDialog()中,构造随机数生成器,并启动定时器。CExample50Dlg::OnInitDialog()修改如下:
C++代码 BOOLCExample50Dlg::OnInitDialog() { CDialogEx::OnInitDialog(); //Add"About..."menuitemtosystemmenu.//IDM_ABOUTBOXmustbeinthesystemcommandrange.ASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX<0xF000); CMenu*pSysMenu=GetSystemMenu(FALSE); if(pSysMenu!=NULL) { BOOLbNameValid; CStringstrAboutMenu; bNameValid=strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if(!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX,strAboutMenu); } } //Settheiconforthisdialog.Theframeworkdoesthisautomatically//whentheapplication'smainwindowisnotadialogSetIcon(m_hIcon,TRUE);//SetbigiconSetIcon(m_hIcon,FALSE);//Setsmallicon//TODO:Addextrainitializationhere//以时间为种子来构造随机数生成器srand((unsigned)time(NULL)); //启动定时器,ID为1,定时时间为200msSetTimer(1,200,NULL); returnTRUE;//returnTRUEunlessyousetthefocustoacontrol}
8、为CExample50Dlg类添加波形绘制的成员函数CExample50Dlg::DrawWave(CDC *pDC, CRect &rectPicture),参数分别为设备上下文指针和绘图的矩形区域。
C++代码 voidCExample50Dlg::DrawWave(CDC*pDC,CRect&rectPicture) { floatfDeltaX;//x轴相邻两个绘图点的坐标距离floatfDeltaY;//y轴每个逻辑单位对应的坐标值intnX;//在连线时用于存储绘图点的横坐标intnY;//在连线时用于存储绘图点的纵坐标CPennewPen;//用于创建新画笔CPen*pOldPen;//用于存放旧画笔CBrushnewBrush;//用于创建新画刷CBrush*pOldBrush;//用于存放旧画刷//计算fDeltaX和fDeltaYfDeltaX=(float)rectPicture.Width()/(POINT_COUNT-1); fDeltaY=(float)rectPicture.Height()/80; //创建黑色新画刷newBrush.CreateSolidBrush(RGB(0,0,0)); //选择新画刷,并将旧画刷的指针保存到pOldBrushpOldBrush=pDC->SelectObject(&newBrush); //以黑色画刷为绘图控件填充黑色,形成黑色背景pDC->Rectangle(rectPicture); //恢复旧画刷pDC->SelectObject(pOldBrush); //删除新画刷newBrush.DeleteObject(); //创建实心画笔,粗度为1,颜色为绿色newPen.CreatePen(PS_SOLID,1,RGB(0,255,0)); //选择新画笔,并将旧画笔的指针保存到pOldPenpOldPen=pDC->SelectObject(&newPen); //将当前点移动到绘图控件窗口的左下角,以此为波形的起始点pDC->MoveTo(rectPicture.left,rectPicture.bottom); //计算m_nzValues数组中每个点对应的坐标位置,并依次连接,最终形成曲线for(inti=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消息处理函数的添加方法如果忘记了,可以再到VS/MFC编程入门之四十四(MFC常用类:定时器Timer)温习下。
WM_TIMER消息的处理函数修改如下:
C++代码 voidCExample50Dlg::OnTimer(UINT_PTRnIDEvent) { //TODO:Addyourmessagehandlercodehereand/orcalldefaultCRectrectPicture; //将数组中的所有元素前移一个单位,第一个元素丢弃for(inti=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消息的处理函数,并修改如下:
C++代码 voidCExample50Dlg::OnDestroy() { CDialogEx::OnDestroy(); //TODO:Addyourmessagehandlercodehere//关闭定时器KillTimer(1); }
11、一切准备就绪,编译运行。最终的效果如下图:
关于画笔,鸡啄米就讲到这里了,下一节将为大家简单讲讲画刷的使用。谢谢大家的关注!
转自:/software/181.html
如果觉得《VS/MFC编程入门之五十(图形图像:GDI对象之画笔CPen)》对你有帮助,请点赞、收藏,并留下你的观点哦!