下面这篇文章很好的回答了我这几天在mfc下绘图的苦闷,我是一名菜鸟,如果自己实现C++绘制曲线,扩展很是个问题,代码的bug让我应接不暇,毕竟能力,时间也有限。尝试mschart,icomp(iplotx),要不是效果不理想或者收费,一周后,发现speed chart control 这是个不错的绘图类控件,欣喜,尝试中。。。
为了在MFC下显示图表,决定自己写一个图表控件,可是发现工作量太大,于是搜索了一下现成的。
1. MSChart
网上都说MSChart功能强大,可以显示饼图、柱状图以及线图等等。但是它主要是给.Net用的,在C++下用起来很不方便。可以从网上下载一个MSCHRT20.ocx控件,然后regsvr32注册。但是只有一个ocx是不够的,如何生成相应的头文件就让我大费周折。我用的是VS2008,有三种方式可以生成头文件,一是将控件拖到界面编辑器中,然后右键添加变量。二是添加Typelib, from registry。三是添加Typelib, from file。这三种方式生成的文件内容和数量都不一样,真不知道MS是不是要故意折腾我们程序员。最后我还是用http://www.vckbase.com/document/viewdoc/?id=959的文件,它是在VC6下生成的。
好了,现在有了头文件了,可以用了吧。但问题远没那么简单。首先,没有文档!。其实MSChart是有官方文档的,但不是针对C++的。而且网上的示例也主要是针对ASP、C#等的,MFC下的很少。其次,MSChart主要是为数据库准备的,数据绑定比较麻烦。最后,MSChart的质量也有待提高,比如闪烁、速度慢、EditCopy的bug、鼠标平移缩放等等。
有人这么评价MSChart(有语法错误):
MS Chart is dodgy, it is extra features at the cost of speed
其实Rich feature并不是其dodgy的真正原因,真正原因是MS就没打算把它做的很好。VS、Office都是Rich feature,可有谁说它们dodgy了?
2. BCG
VS2008下MFC的部分代码就是购买自BCG,但是并非BCG的全部代码,BCG Chart就不包括在内。从BCG网站上的展示来看,其图表效果还是不错的。但是其Chart部分是收费的,目前还没有测试。
3. Xtreme Toolkit Pro
codejock公司的Xtreme Toolkit是继BCG之后的第二选择(也是收费的),它也可以生成诸如Visual Studio、Office之类的风格,而且它也包含Chart部分,成为Xtreme Chart Pro。其图表效果也很绚丽,支持的风格也比较多,尤其是其Interlaced Fill Mode,很漂亮。初看起来十分美好,于是就下载来试用。Xtreme Chart使用起来还是很方便的,几行代码就可以搞定,比MSChart好多了。但是Xtreme Toolkit的帮助文档里居然没有Chart部分,好在它是Full Source的,代码里还是有注释的。另外那个ChartBuilder也很好用,可以直观方便地调节图表参数。
但Xtreme Chart也并非尽善尽美。源代码以及注释中的错别字自不必多说,VS2008的MFC也是这样。其次,Xtreme Chart在避免图表Clutter方面做的还不够。当将Argument Scale Type设为Numerical或DateTime的时候它可以生成Clutter-free的图表,但设为Qualitative的时候就没戏了。我不知道codejoke的程序员是怎么想的,难道X轴不是数值型的时候就不需要避免Clutter吗?要知道预防Clutter是Chart的重要责任,有没有Clutter是一个Chart好坏的重要标志。
以上左图是Numerical模式下的图表,很好,X轴(1~38)没有Clutter。右图是Qualitative模式下的图表,你还能看清X轴吗?
再看一个更典型的例子:
神也不会知道X轴上都标注了什么。只放大X轴才可以看到其庐山真面目:
Matlab可以生成专业的科学图表,尽管没那么绚丽。但是它对Clutter的处理又如何呢?答案是还可以,但有例外,见下图。
我想说的是,实现Clutter Free真的有那么难吗?非也。每个Label字符串在输出之前都可以通过GDI的CALC_RECT模式(或类似技术)得到其所占区域,如果两个相邻label的区域有重叠少画一个不就可以了吗?
还有Side Margin,即图表的边界大小。图表要留有一定的边界才好看,不然第一个点岂不是正好落在边缘上。Xtreme Chart考虑了这一点但做的不够。
Xtreme Chart有如下代码:(见Source\Chart\Diagram2D\XTPChartAxis.cpp, line 220)
我不知道codejoke的coder在写下return 0.5这一行程序的时候都在想些什么。他是否在想,客户的图表数据都是1, 2, 3, 4, 5...这种,所以不会有什么问题的。0.5, 15.0这种magic number谁又知道它们是什么意思呢?在一个严谨的代码里,是不允许有这些magic number的,它们应该由宏或常量来替代。其实也不能怪这位coder,他只是一个implementor,忠实地实现着PM下达的feature。他并不知道用户会使用那些数据,他只是有一些潜在的自我假设,尽管这些假设貌似正确。比如将X轴设为DateTime,以COleDateTime的double表示,这个double值每差1时间就会差一天。好了,如果我用X轴表示年,那么margin就完全看不到了,因为margin为0.5天,相比于一年的365天是一个很小的数。而如果我用X轴表示秒,那么Chart里数据将会缩成一条线,因为0.5天就是43200秒,相比于一秒来说是一个巨大的数字。假如这位implementor考虑的哪怕周全一点,他也许会留一条后路让用户自己去设置side margin的值。可是他没有,没有这样的后路,你要么完全遵循他的潜在假设,要么去修改源代码。我选择了后者。这其实也反映了另外两个问题:1)PM并非End User,他不会了解End User的所有需求。2)codejoke的测试显然做的不够,也许,他在赶着release吧。
再来谈Xtreme Chart的zoom与scroll。首先它不提供box magnifier,不能拉框放大,只能用鼠标滚轮缩放。其次,鼠标滚轮会同时缩放X轴和Y轴,这在很多情况下是不合适的。比如我的X轴很长,值的范围(即Y轴)则对所有数据来说相对稳定,这时候我就希望单独zoom X轴。Xtreme Chart也没有考虑到这样的需求。
最后谈Xtreme Chart的速度与效率。Xtreme Chart将图表逻辑与具体绘制分离开,绘制为单独一个部分(Source\Chart\Drawing\,XTPChartDeviceCommand类)。这样看起来很elegant,但代价是沉重的(过度设计?)。对于线图来说绘制就是许多点之间的线段。假若点很多,就会有很多的线,而每条线段就是一个单独的绘制命令,包含了GDI的画笔、画刷创建与销毁等操作。不断地创建与销毁GDI对象是一个低效的,不明智的做法,因为这些对象都是可复用的。也许Xtreme Chart本身就没有考虑速度,它追求的是功能的大而全,是美观,这些才能吸引客户花钱购买其产品。
我们可以这样评价Xtreme Chart:
Xtreme Chart is elegant, but is on the air. It is beautiful but slick。You should follow its assumptions, which are hidden under the code.
4. High-speed Charting Control
这是codeproject上的一个项目,速度快,适合绘制大量的数据。它在绘制之前会判断所要绘制的图元在不在裁剪区域内,不过不再就跳过不绘制。Xtreme Chart应该是没有这么做。我曾修改过该代码让它更适合MFC以及加入更多功能(比如marker)。High-speed Charting Control不能画饼图、金字塔图等,在这一点上不如Xtreme Chart,而且效果也没有Xtreme Chart漂亮。该控件在Cluter的处理上也不够完善。
总结:
1. 不推荐在MFC下使用MSChart。
2. 如果只是画简单的线图,而且数据量可能较大,High-speed Charting Control是比较合适的。
3. 如果图表很简单而且要求比较漂亮,可以用BCG或Xtreme,但它们都不是免费的。
High-speed Charting Control参考 :
http://blog.csdn.net/czyt1988/article/details/8740500
http://www.codeproject.com/Articles/14075/High-speed-Charting-Control