分享

MFC 动态曲线 支持缩放 显示图例(CStatic派生类)

 成长中辉煌 2014-08-11

首先声明,本项目不是作者独立完成的,而是借助别人的修改得到,也不保证目前的版本没有Bug,仅供大家参考和交流。若有不妥之处,还望指正。

之所以标为“原创”,是因为作者的确花了很大心思,并做了大幅修改和整理,望广大网友包涵,哈哈。。。

开发环境:Windows 7 ,Visual Studio 2008。

 

(一)开发需求和目标

  由于项目需要,需要开发一个GUI,要求能够同时显示至少两条曲线,而且要标出坐标,动态更新,支持缩放。。。。

(二)本项目实现了哪些功能

 继承CStatic控件,编写了一个绘图类,可以同时显示多条曲线,显示横纵坐标,坐标的范围可以自动调整,支持单击鼠标左键拖动放大,双击鼠标左键复原(这个想法来自MATLAB)。曲线和坐标轴属性可以自行设定。能够显示图例。

(三)效果截图

局部放大的结果:

(四)源代码

华为网盘:http://dl./c012cb0h8c

CSDN资源: http://download.csdn.net/detail/nuaazdh/4494341

这个博客不能添加附件,晕。。。  那个CSDN的文件要收两个积分的,大家不要介意哦。。。

备注,为了减小文件大小,我讲Debug和Release文件夹加下的文件,请大家编译后运行。

 

好了,一般人看到这里就可以了,下面赘述一下整个开发过程。

(一)关于参考程序

      本人对MFC不是很熟,只能“拿来主义”,本项目的就是基于http://www./read-25.html这篇博文而作。感谢作者的付出。

上面这篇参考博文写的不错,而且附了源程序可供下载,但是实际运行我发现有内存泄漏!本人不是有意诋毁原作者的心血,只是实事求是,欢迎讨论。

内存泄漏的原因不得而知,不过我学到了内存泄漏的检测方法,顺便说一下:

 一是 Visual Studio自带的,这个微软MSDN上有说,包含如下语句:

  1. #define _CRTDBG_MAP_ALLOC    
  2. #include    
  3. #include    
  4. #include "debug_new.h"   

然后在每个要检测的函数后面添加:

  1. _CrtDumpMemoryLeaks();  

这样在输出窗口会显示是否出现了内存泄漏,并定位;

二是下载Visual Leaker Detector,网址附上:http://vld./documentation。怎么用,大家可以自己网上搜一下,不再赘述。

还有,微软MSDN上说,调用GetDC()后再ReleaseDC()也会造成内存泄漏,这个是个Bug,他们已经承认了,可以参考:http://support.microsoft.com/kb/819635/zh-cn


(二)双缓冲绘图

    既然参考的程序有内存泄漏的问题,那只能自己动手了。

   像我这种菜鸟,拿到这个项目,当然先去网上搜”MFC 动态曲线“,”VC++ 动态绘图“,”MFC GDI+ 绘图“等等,看多了,对双缓冲绘图也就耳濡目染了。

虽然我对具体实现还不是很了解,大概知道了其过程:现在内存中高个Bitmap对象,绘制完成后,拷贝到前端。这样就不用先擦出前端的图像,再一步一步绘制。双缓冲的好处

就是避免闪烁,这个大家都懂的。。。

  关于双缓冲绘图的过程大致如下:

  1. CPaintDC dc(this); // 绘图设备   
  2. CDC *pDC = GetDC();  
  3. CRect rect;  
  4. GetClientRect (&rect);        
  5.   
  6. //创建一个内存中的显示设备   
  7. CDC nDC;  
  8. nDC.CreateCompatibleDC(NULL);   
  9.   
  10. //创建一个内存中的图像   
  11. CBitmap nBitmap;  
  12. nBitmap.CreateCompatibleBitmap(pDC, rect.Width(),rect.Height());  
  13.   
  14. //指定内存显示设备在内存中的图像上画图   
  15. nDC.SelectObject(&nBitmap);   
  16.   
  17. // 刷新控件   
  18. RefreshPlot(&nDC);  
  19.   
  20. //将内存中画好的图像直接拷贝到屏幕指定区域上   
  21. pDC->BitBlt(rect.left,rect.top,rect.right,rect.bottom,&nDC, 0, 0, SRCCOPY);   
  22.   
  23. //释放相关资源   
  24. nBitmap.DeleteObject();  
  25. nDC.DeleteDC();  
  26. ReleaseDC(pDC);  

其他的没有好说的,重点是RefreshPlot()方法,这个是我自定义的函数。其内部完成:绘制背景、坐标轴、网格、曲线、网格坐标的显示、标题的显示、图例显示。。。。。

这部分代码是放在重载的OnPaint()函数中的。

 

(三)绘图结构和方法

  根据从网上查找的资料,我见过三种绘图结构。这里说的绘图结果,意思上,将上面的哪些代码(绘图的具体细节)贴到那个函数中去,我见过的三种情况是:重载:

DrawItem()方法,重载OnPaint()方法,重载OnEraseBkgnd()方法。貌似都可以,但是某些可能需要一些其他辅助的设置。本项目是采用的第二种方法。

  除了结构外最重要的便是具体怎么在一个CDC上绘图了。其实非常简单,只用几个函数,下面简单介绍一下:(pDC是CDC对象的指针)

pDC->MoveTo(int x,int y); 移动到一个点(CPoint的对象),x,y是点的坐标,关于MFC上绘图坐标大家应该非常熟悉了吧,左上为坐标原点,X轴向右,Y轴向下。

                                          当然也可以选择使用重载 MoveTo(POINT point)方法,预先把点算好,然后MoveTo()就好了。

pDC->LineTo(int x,int y); 这个函数意思很明显,画一条线,起点是上一给点,终点是当前的点(x,y),上一个点可以是MoveTo()确定的,或是LineTo()确定的。

                                        上面两个函数可以完成哪些工作呢?只要是直线都可以,如绘制坐标轴,绘制网格,和绘制曲线。绘制曲线其实就是调用多次LineTo()就OK啦

另一个函数是pDC->DrawText(),这个函数是用来显示文字的,当然绘制文字之前得告诉它目标位置、文字格式、颜色等等信息,这个后面会有介绍。

   有个这三个函数,基本就大功告成了,剩下的便是控制位置的格式和位置。这里要介绍三个对象:CPen,CFont和CRect。

CPen:画笔,其中的一个构造函数为CPen(int nPenStyle,int nWidth,COLORREF crColr),画线的时候用到,

CFont:字体,一般这么用 font.CreatePointFont(int nPointSize, LPCTSTR lpszFaceName, CDC* pDC = NULL) ,font为CFont的一个对象,这个在绘制文字的时候用到

CRect:矩形,构造的时候指明四个坐标值left,top,right,bottom就好了,这个在布局的时候和绘制文字会用到。

   现在就可以开始绘图啦,步骤:

①pDC->SelectObject(& font\pen),选择好画笔,格式由内部的参数font或者pen的引用指定;

②pDC->DrawText()或者pDC->MoveTo() + pDC->LineTo(),绘图;

③pDC->SelectObject(CPen * oldPen),恢复画笔。

 无论是绘制哪个部分,都套用上述三个步骤,就OK啦。

 

关于布局和缩放的功能就留作后面介绍吧。

 

参考:

[1]http://www./read-25.html

[2]http://support.microsoft.com/kb/819635/zh-cn

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多