有好些朋友给我反映,就是一个窗口中添加好几个CChartWnd之后,工作不正常。这个的确是这样,CChartWnd会接管原来窗口的消息循环,添加多个CChartWnd之后,就相当于出租房转手好几道,消息循环乱套了。虽然道理上可以给二房东立规矩,但笨笨尚未想到一个万全之策,所以在目前的状况下,请大家不要在一个窗口上Attach多个CChartWnd。
(笨笨注:上述描述已经是老黄历了,新版本的CChart已经实现了在一个窗口上多次Attach,不过这里的方法仍然非常具有参考价值。)
但是不是就不能在一个窗口的多个区域同时绘图呢?非也非也。请不要忘了,CChartWnd的基础是CChart类,往往越是原始的东西功能就越强大。
下面笨笨就给大家简单示范一下利用CChart在单窗口的多个区域绘图。计划分三种情况,即普通窗口,对话框窗口,duilib窗口,供大伙参考。
本课先介绍在普通窗口下分区域绘图。
笨笨已经提供了一种分裂视图,实际就是一种分区域绘图的方式。但目前分裂视图的各个子视图的类型是一样的,如果想在一个子视图画曲线图,另一个子视图画饼图,笨笨只能说抱歉了。
本课介绍的分区域绘图可以克服这个缺点。
笨笨新近在CChart中增加了一种绘图类型,就是等高线图和云图的合体版,顺便在本课一起介绍了。
本课的示例代码将在一个MFC窗口中分两块区域绘图,一块绘制饼图,一块绘制等高线云图。
现在开始。
仍然以实例的形式。
第一步,打开VC,建立一个基于MFC AppWizard(exe)向导的项目LessonA06,向导中不做任何更改,直接点Finish。
第二步,拷贝库文件到LessonA06文件夹。
第三步,在VC中打开LessonA06View.h文件,在其头部添加如下代码。
#include "Chart.h"
#if defined(_UNICODE) || defined(UNICODE)
# pragma comment(lib,"CChartu.lib")
#else
# pragma comment(lib,"CChart.lib")
#endif
using namespace NsCChart;
第四步,在LessonA06View.h文件中,给CLessonA06View类添加两个CChart变量。
CChart m_Chart1, m_Chart2;
第五步,利用ClassWizard给CLessonA06View类添加OnCreate消息处理函数,并修改OnCreate函数如下。
int CLessonA06View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
m_Chart1.SetType(kTypePie);
m_Chart1.AddPie(4, "王菲");
m_Chart1.AddPie(3, "张柏芝");
m_Chart1.AddPie(2, "Irene");
m_Chart2.SetType(kTypeContour);
m_Chart2.SetContourByPoints();
m_Chart2.AddContourPoint(-1, -1, 5);
m_Chart2.AddContourPoint(-1, 1, 5);
m_Chart2.AddContourPoint(1, -1, 5);
m_Chart2.AddContourPoint(1, 1, 5);
m_Chart2.AddContourPoint(-1, 0, -5);
m_Chart2.AddContourPoint(0, -1, -5);
m_Chart2.AddContourPoint(1, 0, -5);
m_Chart2.AddContourPoint(0, 1, -5);
m_Chart2.AddContourPoint(0, 0, 10);
m_Chart2.SetPlotRange(-1.5, 1.5, -1.5, 1.5);
m_Chart2.SetContourPrecision(8);
m_Chart2.SetContourLineNum(20);
m_Chart2.SetUseLegend(false);
return 0;
}
这里请大家注意等高线云图的用法。
首先,它的代码是kTypeContour。
其次,画等高线云图和等高线图、云图一样,需要一个原型为double f(double x, double y);的场函数。
由于好几位朋友问到,他们只有数据点,能不能画等高线图。一直在道理上都是可以的,就是需要编写一个场函数,在场函数里面用数据点插值即可。但这个可能比较麻烦。最近笨笨在CChart中内置了插值函数,采用双线性的方式插值,精度可能没有二次以上的方式的高,但够用就行吧。注意到这一行了吗。
m_Chart2.SetContourByPoints();
这就是表示等高线采用笨笨内置的插值函数绘制,不需要再提供场函数,但需要提供数据点。提供数据点的函数如下。
void CChart::AddContourPoint(double x, double y, double h);
其中x,y就是坐标,h是高度。
下面这个函数表示等高线的绘制范围。
void CChart::SetPlotRange(double xl, double xu, double yl, double yu);
下面这个函数表示等高线的绘制精度,在以前的课程中已经介绍了。
void CChart:: SetContourPrecision (int precision);
下面这个函数表示等高线的绘制时的高度数,在以前的课程中也已经出现了。
void CChart::SetContourLineNum(int num);
等高线的新功能介绍就结束了。
第六步,重载OnSize函数如下。
void CLessonA06View::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
CRect rect;
GetClientRect(&rect);
CRect rt1, rt2;
rt1 = rect;
rt1.right = (rect.left + rect.right)/2;
m_Chart1.SetConfineRect(rt1);
rt2 = rect;
rt2.left = (rect.left + rect.right)/2;
m_Chart2.SetConfineRect(rt2);
}
这里就是分配各个视图所占的窗口区域。在本例中,m_Chart1占据窗口的左半,m_Chart2占据窗口的右半。主要需要利用到这个函数。
void CChart::SetConfineRect(RECT rect);
第七步,修改OnDraw如下。
void CLessonA06View::OnDraw(CDC* pDC)
{
CLessonA06Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
m_Chart1.OnDraw(pDC->m_hDC);
m_Chart2.OnDraw(pDC->m_hDC);
}
现在可以运行了,效果如下。
刚启动的时候可能有点慢,因为需要初始化等高线。
有同学要问了,鼠标没有反应呀?我们这里没有采用CChartWnd,所以需要自己处理消息。
第八步,重载OnLButtonDown,OnLButtonUp,OnLButtonDblClk,OnMouseMove,OnContextMenu,OnEraseBkgnd这几个函数,并修改如下。
void CLessonA06View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_Chart1.OnLButtonDownR(m_hWnd, point, nFlags);
m_Chart2.OnLButtonDownR(m_hWnd, point, nFlags);
}
void CLessonA06View::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_Chart1.OnLButtonUpR(m_hWnd, point, nFlags);
m_Chart2.OnLButtonUpR(m_hWnd, point, nFlags);
}
void CLessonA06View::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_Chart1.OnLButtonDblClkR(m_hWnd, point, nFlags);
m_Chart2.OnLButtonDblClkR(m_hWnd, point, nFlags);
}
void CLessonA06View::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_Chart1.OnMouseMoveR(m_hWnd, point, nFlags);
m_Chart2.OnMouseMoveR(m_hWnd, point, nFlags);
}
void CLessonA06View::OnContextMenu(CWnd* pWnd, CPoint point)
{
// TODO: Add your message handler code here
m_Chart1.OnContextMenuR(NULL, m_hWnd, point);
m_Chart2.OnContextMenuR(NULL, m_hWnd, point);
}
BOOL CLessonA06View::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
return TRUE;
//return CView::OnEraseBkgnd(pDC);
}
消息响应回来了!!
大家可以比较一下这里和第15课里面消息响应代码的异同。
实际上,就是在各个消息的响应代码里面,把每个CChart对象都处理一遍就可以了。
好了,现在下课喽。