文档章节

TeeChart替代品,MFC下好用的高速绘图控件-(Hight-Speed Charting)

尘中远
 尘中远
发布于 2016/05/12 23:54
字数 2490
阅读 20
收藏 0
点赞 2
评论 0

相关链接:


C++ GUI 绘图控件目录

MFC

Qt














也许这是vc下最好最方便的绘图类,它有TeeChart的绘图和操作风格,不用当心注册破解的问题,因为它是开源的。不用打包注册,因为它是封装成类的,能方便扩展继承。vc6.0到vs2010都能使用,而且非常简单。

此类发表于codeproject

在使用它的时候,展示一下它的效果吧:



如果你想需要上面这些效果的,果断选它吧!


下面用图文并茂的方式,来详细介绍这个绘图控件

首先,下载这个控件,最新可以从这里获取codeproject

1 ChartCtrl类的导入

在工程下建立一个文件夹
叫ChartCtrl吧,里面放置ChartCtrl的源代码


文件夹内容如图所示


然后让vs导入这些类


全选,确定-ok



这时工程就添加好这个控件了

2.创建控件

2.1 对话框编辑器创建

对于一些不需要改变大小的对话框来说,在对话框编辑器里拖曳创建控件是最舒服的方法了,这个ChartCtrl可以用用户控件来创建
首先在对话框上放置一个Custom Control


修改属性如下图所示。这里要改的属性有Style,就在5右边的0改为2,0x52010000,Class命名为ChartCtrl,ID随便改了

给对话框添加变量,和传统的方法一样。这里需要注意的是,由于文件都放置在工程文件的一个文件夹下,包含头文件时需要指明路径



头文件包含的样式如下:
#include "ChartCtrl/ChartCtrl.h"
在对话框类添加变量,叫m_ChartCtrl1(后面还有m_ChartCtrl2通过动态创建的)
CChartCtrl m_ChartCtrl2;
 在DoDataExchange函数里添加关联
void CSpeedChartCtrlDemoDlg::DoDataExchange(CDataExchange* pDX) 
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_ChartCtrl1, m_ChartCtrl1);
}
编译运行,绘图控件就出来了


2.2 动态创建

添加头文件:
同样头文件如下写:
#include "ChartCtrl/ChartCtrl.h"
然后添加成员变量
CChartCtrl m_ChartCtrl2;
在resource.h里添加一个资源



添加IDC_ChartCtrl2,为1001,注意记得把_APS_NEXT_CONTROL_VALUE改成下一个资源号


在OnInitDialog里创建

如:
CRect rect,rectChart; 
GetDlgItem(IDC_ChartCtrl1)->GetWindowRect(&rect);
ScreenToClient(rect);
rectChart = rect;
rectChart.top = rect.bottom + 3;
rectChart.bottom = rectChart.top + rect.Height();
m_ChartCtrl2.Create(this,rectChart,IDC_ChartCtrl2);
m_ChartCtrl2.ShowWindow(SW_SHOWNORMAL);

这样就可以创建了,下图两个控件分别通过对话框编辑器创建和动态创建,代码在附件下载里

此时什么也不会显示,需要添加坐标轴


3.创建坐标轴

 
ChartCtrl一共有3种坐标,都继承于CChartAxis



头文件ChartCtrl.h已经包含这些坐标,不需要引入
下面分别建立两种坐标轴,一个是数值型一个是时间型
在m_ChartCtrl1建立两个都是数值型的坐标
在创建m_ChartCtrl1之后加入如下创建坐标轴的代码:(这里写在OnInitDialog里)
CChartAxis *pAxis= NULL; 
pAxis = m_ChartCtrl1.CreateStandardAxis(CChartCtrl::BottomAxis);
pAxis->SetAutomatic(true);
pAxis = m_ChartCtrl1.CreateStandardAxis(CChartCtrl::LeftAxis);
pAxis->SetAutomatic(true);
这样就建立两个坐标轴了,如图所示



给m_ChartCtrl2创建时间坐标
CChartDateTimeAxis* pDateAxis= NULL;
pDateAxis = NULL; 
pDateAxis = m_ChartCtrl2.CreateDateTimeAxis(CChartCtrl::BottomAxis);
pDateAxis->SetAutomatic(true);
pDateAxis->SetTickLabelFormat(false,_T("%m月%d日"));
pAxis = m_ChartCtrl2.CreateStandardAxis(CChartCtrl::LeftAxis);
pAxis->SetAutomatic(true);
SetTickLabelFormat函数用来设置时间显示方式,格式化和COleDateTime的Format一样

4.创建标题

#include "ChartClass\ChartTitle.h"

在添加标题时,先要说说ChartCtrl的字符串,ChartCtrl的字符串实际是stl的string和wstring,为了对应unicode,作者对这两种字符进行了一个宏定义,就像TCHAR一样,定义如下:

#include<string>
#include <sstream>

#if defined _UNICODE ||defined UNICODE
    typedef std::wstring TChartString;
    typedef std::wstringstream TChartStringStream;
#else
    typedef std::string TChartString;
    typedef std::stringstream TChartStringStream;
#endif

所以在多字节情况下,就是string。由于MFC大部分都是用CString,CString也是经过宏定义,所以可以比较轻松的和TChartString转换,另外TChartStringStream远比CString的Format灵活和直观,建议大家研究研究!

 加入如下代码:

TChartString str1;
str1 = _T("IDC_ChartCtrl1 - m_ChartCtrl1");
m_ChartCtrl1.GetTitle()->AddString(str1);

CString str2(_T(""));
str2 = _T("IDC_ChartCtrl2 - m_ChartCtrl2");
m_ChartCtrl2.GetTitle()->AddString(TChartString(str2));

TChartString 可以直接用“=”对字符串赋值



设置坐标轴的标题,首先需要获取坐标GetLeftAxis,GetBottomAxis ……
获取坐标后,获得坐标的文字标签GetLabel,然后进行修改
如下两种写法,一种比较安全繁琐,一种就直接过去就可以,看个人喜好

CChartAxisLabel* pLabel = NULL;
CChartAxis *pAxis = NULL;
TChartString str1 = _T("左坐标轴");

CChartAxisLabel* pLabel = NULL;

pAxis = m_ChartCtrl1.GetLeftAxis();
if(pAxis)
    pLabel = pAxis->GetLabel();
if(pLabel)
    pLabel->SetText(str1);

m_ChartCtrl2.GetLeftAxis()->GetLabel()->SetText(str1);

str1 = _T("数值坐标轴");
pAxis = m_ChartCtrl1.GetBottomAxis();
if(pAxis)
    pLabel = pAxis->GetLabel();
if(pLabel)
    pLabel->SetText(str1);
str1 = _T("时间坐标轴");

m_ChartCtrl2.GetBottomAxis()->GetLabel()->SetText(str1);


设置完效果如图



标题还可以更改颜色,这里不再重复描述。

5.画图

5.1 创建线图

ChartCtrl的画线非常简单通用,远比TeeChart简单和方便。
创建线图先要创建一个图形系列,这个和TeeChart很像
用函数CChartCtrl的CreateLineSerie()函数即可创建一个线图,这个函数会返回这个系列的指针,所有在创建之后记得保存下这个指针,以便之后的操作。线图系列的指针是CChartLineSerie,记得包含头文件
#include "ChartClass\ChartLineSerie.h"
创建完序列之后就可以用AddPoints函数把double数组的数据画出来,这个比TeeChart方便多了
 
如下这是画图的函数
m_ChartCtrl1.EnableRefresh(false);
m_ChartCtrl2.EnableRefresh(false);
//////////////////////////////////////////////////////////////////////////
//画图测试
//////////////////////////////////////////////////////////////////////////
double x[1000], y[1000];
for (int i=0; i<1000; i++)
{
    x[i] = i;
    y[i] = sin(float(i));
}
CChartLineSerie *pLineSerie1;
m_ChartCtrl1.RemoveAllSeries();//先清空
pLineSerie1 = m_ChartCtrl1.CreateLineSerie();
pLineSerie1->SetSeriesOrdering(poNoOrdering);//设置为无序
pLineSerie1->AddPoints(x, y,1000);
pLineSerie1->SetName(_T("这是IDC_ChartCtrl1的第一条线"));//SetName的作用将在后面讲到

//////////////////////////////////////////////////////////////////////////
//时间轴画图
//////////////////////////////////////////////////////////////////////////
COleDateTime t1(COleDateTime::GetCurrentTime());
COleDateTimeSpan tsp(1,0,0,0);
for (int i=0; i<1000; i++)
{
    x[i] = t1.m_dt;
    y[i] = sin(float(i));
    t1 += tsp;
}
CChartLineSerie *pLineSerie2;
m_ChartCtrl2.RemoveAllSeries();//先清空
pLineSerie2 = m_ChartCtrl2.CreateLineSerie();
pLineSerie2->SetSeriesOrdering(poNoOrdering);//设置为无序
pLineSerie2->AddPoints(x, y,1000);
pLineSerie2->SetName(_T("这是IDC_ChartCtrl2的第一条线"));//SetName的作用将在后面讲到

m_ChartCtrl1.EnableRefresh(true);
m_ChartCtrl2.EnableRefresh(true);


 


RemoveAllSeries函数可以清楚所有线条,EnableRefresh函数可以提供绘图效率,另外告诉大家一个bug,时间轴坐标在调第二次用RemoveAllSeries函数后,画图时一定要EnableRefresh(false)再EnableRefresh(true);否则会断言

下面将介绍更多的会图方法
在上一篇已经介绍了简单的线条绘制,实际上可能需要多的功能

5.2 添加曲线

控件可以绘制不止一条曲线,可以绘制足够多的曲线在上面,下面演示如何添加多个曲线
只要在画图时不清楚原来的曲线就会添加多一条曲线
把代码的RemoveAllSeries去掉就会添加多条曲线
m_ChartCtrl1.EnableRefresh(false);
m_ChartCtrl2.EnableRefresh(false);
//////////////////////////////////////////////////////////////////////////
//画图测试
//////////////////////////////////////////////////////////////////////////
double x[1000], y[1000];
for (int i=0; i<1000; i++)
{
x[i] = i;
y[i] = sin(float(i)*m_ChartCtrl1.GetSeriesCount());
}
CChartLineSerie *pLineSerie1;
// m_ChartCtrl1.RemoveAllSeries();//不清空
pLineSerie1 = m_ChartCtrl1.CreateLineSerie();
pLineSerie1->SetSeriesOrdering(poNoOrdering);//设置为无序
pLineSerie1->AddPoints(x, y,1000);
TChartStringStream strs1;
strs1 << _T("这是IDC_ChartCtrl1的第")
<< m_ChartCtrl1.GetSeriesCount()
<< _T("条曲线");
pLineSerie1->SetName(strs1.str());

//////////////////////////////////////////////////////////////////////////
//时间轴画图
//////////////////////////////////////////////////////////////////////////
COleDateTime t1(COleDateTime::GetCurrentTime());
COleDateTimeSpan tsp(1,0,0,0);
for (int i=0; i<1000; i++)
{
x[i] = t1.m_dt;
y[i] = sin(float(i)*m_ChartCtrl2.GetSeriesCount());
t1 += tsp;
}
CChartLineSerie *pLineSerie2;
// m_ChartCtrl2.RemoveAllSeries();//不清空
pLineSerie2 = m_ChartCtrl2.CreateLineSerie();
pLineSerie2->SetSeriesOrdering(poNoOrdering);//设置为无序
pLineSerie2->AddPoints(x, y,1000);
TChartStringStream strs2;
strs2 << _T("这是IDC_ChartCtrl2的第")
<< m_ChartCtrl2.GetSeriesCount()
<< _T("条曲线");
pLineSerie2->SetName(strs2.str());

m_ChartCtrl1.EnableRefresh(true);
m_ChartCtrl2.EnableRefresh(true);


这里我添加了n条。



5.3 动态曲线

以前写TeeChart画图的文章时好多人问怎么动态画图,其实所有画图控件的动态画图都一样,就是不停的画,动的只是数组,控件只负责画图,你想让图像动起来,就让数据动起来!

数据动起来涉及到数组的左右移动,很简单的一个算法而已
如有一个double数组 double pdx[100],pdy[100];
每次画图时让pdx左移一位
for(int i(0);i<99;++i)
{
   pdx[i] = pdx[i+1];
   pdy[i] = pdy[i+1];
}
pdy[99] = ……需要显示的新数据……


然后每隔0.几秒画出来,就发现动起来了



ChartCtrl提供了两种绘图函数,AddPoints和AddPoint,这两种函数在绘制动态图时会有所区别,区别见上图,上面的是AddPoints,绘图长度固定,AddPoint效果见下图,图线会不停积累。


这是我在上研究生时写的一篇文章,当时放进草稿箱里一直没发出来,今天无意看到,决定把他完成,demo代码已经找不到了,可能在我以前实验室的电脑里吧~工作后也很少用mfc了,现在偶尔用用qt过把瘾,大家如果对绘图工控有需求的话,可以使用qt的qwt控件,也比较简单,这个控件帮了我很多忙,在此向作者表示感谢,大家可以查看其源代码学习其编程思想。



下载地址:http://www.codeproject.com/Articles/14075/High-speed-Charting-Control   

csdn资源下载地址:http://download.csdn.net/detail/czyt1988/6880917



© 著作权归作者所有

共有 人打赏支持
尘中远
粉丝 1
博文 26
码字总数 47436
作品 0
朝阳
程序员
VS2010/MFC编程入门教程之目录和总结(鸡啄米)

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

weixin_40647819 ⋅ 05/23 ⋅ 0

缓存型 C++ 编译器 Zapcc 开源,号称比 Clang 更快

还记得 Zapcc 编译器吗?一个旨在减少编译时间的编译器,现在已正式开源。 Zapcc 是基于 LLVM/Clang 的 C++ 编译器,自 2015 年开始,它就一直在致力于让自己成为比 Clang 本身更快的 C++ 编...

局长 ⋅ 今天 ⋅ 0

MFC功能扩展控件BCGSuite for MFC发布v27.1|附下载

BCGSuite for MFC是一款Visual Studio 2008/2010/2012/2013 的MFC功能扩展控件。虽然Visual Studio 2008中包含的新版MFC是基于BCGControlBar Pro技术的,但某些典型的控件如网格、日历、编辑...

Miss_Hello_World ⋅ 04/26 ⋅ 0

libuidk(MFC贴图界面库)v7.5最新免费版下载

libuidk是一款专业的MFC贴图界面库,可以使用MFC界面制作超酷界面,采用所见即所得的编辑方式,支持皮肤和代码分离,可以大大提高产品开发速度,增加图形用户界面的亲和力。对MFC设计师来说十...

qq_39928239 ⋅ 05/11 ⋅ 0

MFC中Spin control的使用

在编写一个工具时需要对经纬度进行微调,经纬度使用度分秒的格式来呈现的。想到了用控件 Spin Control 来实现。现对这个控件的用法做一个简要的回顾。 新建一个MFC基于对话框的应用程序,工程...

yousss ⋅ 04/24 ⋅ 0

c语言编程软件有哪些 Win7下用哪种C语言编译器

C语言是一门历史很长的编程语言,其编译器和开发工具也多种多样,其开发工具包括编译器,现举几个开发工具供大家选择,当然也要根据自己的操作系统来选择适合自己的开发工具 好多刚开始接触c...

mini92 ⋅ 04/20 ⋅ 0

GCC,LLVM,Clang编译器对比

在XCode中,我们经常会看到这些编译选项(如下图),有些人可能会有些茫然,本文将对GCC4.2、LLVM GCC 4.2、LLVM compliler 2.0三个编译选项进行一个详细的介绍。 GCC GCC(GNU Compiler Co...

u013702462 ⋅ 05/10 ⋅ 0

C语言编程学习程序解析:数据类型和函数返回值解析

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界 ⋅ 05/27 ⋅ 0

RAD Studio 10.2.2 (C++Builder)试用体验

因为最早学C/C++的时候,是从Turbo C和borland C++开始的,所以RAD studio有新版本的时候,就下载体验一下。 从发布历史来看,现在是每半年做一次更新,所以原来的版本更新非常快。安装包也是...

zdhsoft ⋅ 03/08 ⋅ 0

大神有话说之c++,还在迷茫的朋友可以来看一下

C++ 是一种中级语言,它是由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的。C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言。C++ 可运行于多种平台上,如 Window...

悟空_b201 ⋅ 05/30 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring发展历程总结

转自与 https://www.cnblogs.com/RunForLove/p/4641672.html 目前很多公司的架构,从Struts2迁移到了SpringMVC。你有想过为什么不使用Servlet+JSP来构建Java web项目,而是采用SpringMVC呢?...

onedotdot ⋅ 39分钟前 ⋅ 0

Python模块/包/库安装(6种方法)

Python模块/包/库安装(6种方法) 冰颖机器人 2016-11-29 21:33:26 一、方法1: 单文件模块 直接把文件拷贝到 $python_dir/Lib 二、方法2: 多文件模块,带setup.py 下载模块包(压缩文件zip...

cswangyx ⋅ 59分钟前 ⋅ 0

零基础学习大数据人工智能,学习路线篇!系统规划大数据之路?

大数据处理技术怎么学习呢?首先我们要学习Python语言和Linux操作系统,这两个是学习大数据的基础,学习的顺序不分前后。 Python:Python 的排名从去年开始就借助人工智能持续上升,现在它已经...

董黎明 ⋅ 今天 ⋅ 0

openJdk和sun jdk的区别

使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SUN JDK有什么关系和区别呢? 历史上的原因是,Ope...

jason_kiss ⋅ 今天 ⋅ 0

梳理

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 它是JS的状态容器,是一种解决问题的方式,所以即可以用于 react 也可以用于 vue。 需要理解其思想及实现方式。 应用中所有的 stat...

分秒 ⋅ 今天 ⋅ 0

Java 后台判断是否为ajax请求

/** * 是否是Ajax请求 * @param request * @return */public static boolean isAjax(ServletRequest request){return "XMLHttpRequest".equalsIgnoreCase(((HttpServletReques......

JavaSon712 ⋅ 今天 ⋅ 0

Redis 单线程 为何却需要事务处理并发问题

Redis是单线程处理,也就是命令会顺序执行。那么为什么会存在并发问题呢? 个人理解是,虽然redis是单线程,但是可以同时有多个客户端访问,每个客户端会有 一个线程。客户端访问之间存在竞争...

码代码的小司机 ⋅ 今天 ⋅ 0

到底会改名吗?微软GVFS 改名之争

微软去年透露了 Git Virtual File System(GVFS)项目,GVFS 是 Git 版本控制系统的一个开源插件,允许 Git 处理 TB 规模的代码库,比如 270 GB 的 Windows 代码库。该项目公布之初就引发了争...

linux-tao ⋅ 今天 ⋅ 0

笔试题之Java基础部分【简】【二】

1.静态变量和实例变量的区别 在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变...

anlve ⋅ 今天 ⋅ 0

Lombok简单介绍及使用

官网 通过简单注解来精简代码达到消除冗长代码的目的 优点 提高编程效率 使代码更简洁 消除冗长代码 避免修改字段名字时忘记修改方法名 4.idea中安装lombnok pom.xml引入 <dependency> <grou...

to_ln ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部