最近需要用MFC做一个界面动态显示曲线,自己画坐标轴画曲线太费时间,网上也有很多已实现的动态曲线接口,但是也不太灵活。正好微软有个现成的MSChart,功能比较全面,查了一些资料,总算实现了无闪烁的动态曲线。关于MSChart的安装可以参考上篇文章http://www.cnblogs.com/wy-wangyan/archive/2011/05/06/2038981.html
实现的效果截图:
实现的原理:MSChart如果要实现动态的曲线就只能在设置的定时器中根据RowCount,到一定的列数后在最后一列插入新一列(m_Chart.GetDataGrid().InsertRows),将最早出现的那一列删除(m_Chart.GetDataGrid().DeleteRows)。对于无闪烁刷新的实现类似于一般MFC画图中创建双缓冲画图避免屏幕闪烁的方法,这里是用CStatic动态创建一个Picture Control图像控件,将它设定和要画的MSChart一样大,然后把mschart的内容利用m_Chart.EditCopy()复制到剪贴板,然后再在picture control中显示出来。
下面是全部的实现的代码:
MSChart需要在view的oncreate中创建出mschart:
1
int
CRTDBView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2
{
3
if
(CView::OnCreate(lpCreateStruct)
==
-
1
)
4
return
-
1
;
5
6
//
TODO: Add your specialized creation code here
7
CRect rc;
8
GetClientRect(
&
rc);
9
VERIFY(m_Picture.Create(_T(
""
),SS_BITMAP
|
WS_CHILD
|
WS_EX_TRANSPARENT
|
WS_VISIBLE,CRect(
0
,
0
,
1200
,
600
),
this
,IDC_PICTURE));
10
if
(
!
m_Chart.Create(_T(
"
RTDB
"
),WS_CHILD
|
WS_VISIBLE, CRect(
0
,
0
,
1200
,
600
),
this
,
10
))
11
return
-
1
;
12
13
14
return
0
;
15
}
在ONSIZE中设置mschart的位置:
1
void
CRTDBView::OnSize(UINT nType,
int
cx,
int
cy)
2
{
3
CView::OnSize(nType, cx, cy);
4
5
//
TODO: Add your message handler code here
6
if
( m_Chart.GetSafeHwnd())
7
m_Chart.MoveWindow(
0
,
0
, cx, cy );
8
}
下面是主要实现部分的代码了,InitChart函数实现了mschart的背景、标题、XY坐标刻度以及颜色字体大小、曲线设定颜色以及数据点的显示等。主要是chart的初始化。
1
void
CRTDBView::InitChart(
void
)
2
{
3
//
设置标题
4
m_Chart.SetTitleText(_T(
"
RTDB
"
));
5
6
//
设置标题颜色
7
m_Chart.GetTitle().GetVtFont().GetVtColor().Set(
0
,
255
,
0
);
8
9
//
改变字体大小
10
m_Chart.GetTitle().GetVtFont().SetSize(
14
);
11
12
//
改变背景色
13
m_Chart.GetBackdrop().GetFill().SetStyle(
1
);
14
m_Chart.GetBackdrop().GetFill().GetBrush().GetFillColor().Set(
0
,
0
,
0
);
15
16
//
显示图例
17
m_Chart.SetShowLegend(TRUE);
18
m_Chart.SetColumn(
1
);
19
m_Chart.SetColumnLabel((LPCTSTR)_T(
"
point num
"
));
20
21
//
设置图例颜色
22
m_Chart.GetLegend().GetVtFont().GetVtColor() .Set(
0
,
255
,
0
);
23
24
//
初始化设置row
25
m_Chart.SetRowCount(
15
);
26
m_Chart.SetRow(
1
);
27
m_Chart.SetRowLabel((LPCTSTR)_T(
""
));
28
for
(UINT i
=
1
;i
<=
m_Chart.GetRowCount();i
++
)
29
{
30
m_Chart.GetDataGrid().SetData(i,
1
,
0
,
0
);
31
}
32
33
34
//
显示类型 组合的方式 0为3D 1为2D
35
m_Chart.SetChartType(
1
|
2
);
36
m_Chart.SetSeriesType(
11
);
37
38
//
栈模式
39
m_Chart.SetStacking(FALSE);
40
41
VARIANT var;
42
43
//
X、Y轴名称
44
m_Chart.GetPlot().GetAxis(
0
,var).GetAxisTitle().SetText(_T(
"
时间(s)
"
));
//
X轴名称
45
m_Chart.GetPlot().GetAxis(
1
,var).GetAxisTitle().SetText(_T(
"
点数
"
));
//
Y轴名称
46
47
//
X轴设置
48
m_Chart.GetPlot().GetAxis(
0
,var).GetCategoryScale().SetAuto(FALSE);
//
不自动标注X轴刻度
49
m_Chart.GetPlot().GetAxis(
0
,var).GetCategoryScale().SetDivisionsPerLabel(
1
);
//
每刻度一个标注
50
m_Chart.GetPlot().GetAxis(
0
,var).GetCategoryScale().SetDivisionsPerTick(
1
);
//
每刻度一个刻度线
51
52
//
X轴每刻度竖线,0为不设置、1为设置
53
m_Chart.GetPlot().GetAxis(
0
,var).GetAxisGrid().GetMajorPen().SetStyle(
0
);
54
55
//
曲线个数
56
m_Chart.SetColumnCount(
1
);
57
58
//
曲线颜色
59
m_Chart.GetPlot().GetSeriesCollection().GetItem(
1
).GetPen().GetVtColor().Set(
0
,
255
,
0
);
60
61
//
曲线宽度(对点线图有效)
62
m_Chart.GetPlot().GetSeriesCollection().GetItem(
1
).GetPen().SetWidth(
30
);
63
64
//
数据点类型显示数据值的模式(对柱柱状图和点线图有效)
65
//
0: 不显示 1: 显示在柱状图外
66
//
2: 显示在柱状图内上方 3: 显示在柱状图内中间 4: 显示在柱状图内下方
67
m_Chart.GetPlot().GetSeriesCollection().GetItem(
1
).GetDataPoints().GetItem(
-
1
).GetDataPointLabel().SetLocationType(
1
);
68
69
//
设置曲线上数据点颜色
70
m_Chart.GetPlot().GetSeriesCollection().GetItem(
1
).GetDataPoints().GetItem(
-
1
).GetDataPointLabel().GetVtFont().GetVtColor().Set(
0
,
255
,
0
);
71
72
//
曲线设置十字标记
73
m_Chart.GetPlot().GetSeriesCollection().GetItem(
1
).GetSeriesMarker().SetAuto(FALSE);
//
MUST!!
74
m_Chart.GetPlot().GetSeriesCollection().GetItem(
1
).GetDataPoints().GetItem(
-
1
)
75
.GetMarker().SetVisible(TRUE);
76
77
//
0横杠,1十字,2是叉,3是星,4是圆圈,5是方块,6菱形
78
//
7三角,8倒三角,9实心点,10实心方块,11实心菱形,12实心三角,13实心倒三角,14泛光的点
79
m_Chart.GetPlot().GetSeriesCollection().GetItem(
1
).GetDataPoints().GetItem(
-
1
)
80
.GetMarker().SetStyle(
1
);
81
82
//
设置曲线上十字颜色
83
m_Chart.GetPlot().GetSeriesCollection().GetItem(
1
).GetDataPoints().GetItem(
-
1
)
84
.GetMarker().GetFillColor().Set(
255
,
0
,
0
);
85
m_Chart.GetPlot().GetSeriesCollection().GetItem(
1
).GetDataPoints().GetItem(
-
1
)
86
.GetMarker().GetPen().GetVtColor().Set(
255
,
0
,
0
);
87
88
//
设定坐标轴颜色
89
m_Chart.GetPlot().GetAxis(
0
,var).GetPen().GetVtColor().Set(
0
,
255
,
0
);
90
m_Chart.GetPlot().GetAxis(
1
,var).GetPen().GetVtColor().Set(
0
,
255
,
0
);
91
m_Chart.GetPlot().GetAxis(
2
,var).GetPen().GetVtColor().Set(
0
,
255
,
0
);
92
93
//
设置坐标轴轴刻度值颜色
94
m_Chart.GetPlot().GetAxis(
1
,var).GetLabels().GetItem(
1
).GetVtFont().GetVtColor().Set(
0
,
255
,
0
);
95
m_Chart.GetPlot().GetAxis(
0
,var).GetLabels().GetItem(
1
).GetVtFont().GetVtColor().Set(
0
,
255
,
0
);
96
m_Chart.GetPlot().GetAxis(
2
,var).GetLabels().GetItem(
1
).GetVtFont().GetVtColor().Set(
0
,
255
,
0
);
97
98
//
设定坐标轴宽度
99
m_Chart.GetPlot().GetAxis(
0
,var).GetPen().SetWidth(
30
);
100
m_Chart.GetPlot().GetAxis(
1
,var).GetPen().SetWidth(
30
);
101
102
//
设定X、Y轴标题颜色
103
m_Chart.GetPlot().GetAxis(
1
,var).GetAxisTitle().GetVtFont().GetVtColor().Set(
0
,
255
,
0
);
104
m_Chart.GetPlot().GetAxis(
0
,var).GetAxisTitle().GetVtFont().GetVtColor().Set(
0
,
255
,
0
);
105
106
//
设置Y轴坐标横线颜色
107
m_Chart.GetPlot().GetAxis(
1
,var).GetAxisGrid().GetMajorPen().GetVtColor().Set(
0
,
255
,
0
);
108
109
//
设置Y轴
110
m_Chart.GetPlot().GetAxis(
1
,var).GetValueScale().SetAuto(FALSE);
//
不自动标注Y轴刻度
111
m_Chart.GetPlot().GetAxis(
1
,var).GetValueScale().SetMaximum(
100
);
//
Y轴最大刻度
112
m_Chart.GetPlot().GetAxis(
1
,var).GetValueScale().SetMinimum(
-
100
);
//
Y轴最小刻度
113
m_Chart.GetPlot().GetAxis(
1
,var).GetValueScale().SetMajorDivision(
10
);
//
Y轴刻度5等分
114
m_Chart.GetPlot().GetAxis(
1
,var).GetValueScale().SetMinorDivision(
1
);
//
每刻度一个刻度线
115
116
//
设置Y轴名称大小、类型
117
m_Chart.GetPlot().GetAxis(
1
,var).GetAxisTitle().GetVtFont().SetStyle(
0
);
118
m_Chart.GetPlot().GetAxis(
1
,var).GetAxisTitle().GetVtFont().SetSize(
12
);
119
120
//
隐藏第二Y轴
121
m_Chart.GetPlot().GetAxis(
2
,var).GetAxisScale().SetHide(FALSE);
122
123
//
刷新
124
m_Chart.Refresh();
125
126
}
最后在OnTimer函数中是动态曲线的实现以及无闪烁的实现:
1
void
CRTDBView::OnTimer(UINT_PTR nIDEvent)
2
{
3
4
//
TODO: Add your message handler code here and/or call default
5
m_Picture.BringWindowToTop();
6
m_Picture.ShowWindow(SW_SHOW);
7
HANDLE hDib;
8
pDC
=
GetDlgItem(IDC_PICTURE)
->
GetDC();
9
int
m_rowPos
=
m_Chart.GetRowCount()
+
1
;
10
11
//
插入新一列
12
m_Chart.GetDataGrid().InsertRows(m_rowPos,
1
);
13
m_Chart.SetRow(m_rowPos);
14
char
buf[
32
];
15
for
(
int
i
=
0
;i
<
sizeof
(buf);i
++
)
16
{
17
buf[i]
=
0
;
18
}
19
sprintf(buf,
"
%d
"
,m_iCount);
20
21
//
X轴坐标值
22
m_Chart.SetRowLabel((LPCTSTR)CString(buf));
23
24
//
数据设置(随机)
25
m_Chart.GetDataGrid().SetData(m_rowPos,
1
,rand()
*
100
/
RAND_MAX,
0
);
26
27
//
大于15列就开始删除最后一列
28
if
(m_Chart.GetRowCount()
>
15
)
29
{
30
m_Chart.GetDataGrid().DeleteRows(m_rowPos
-
15
,
1
);
31
}
32
m_iCount
++
;
33
34
//
把更新复制到粘贴板,在picture中显示
35
m_Chart.EditCopy();
36
if
(OpenClipboard())
37
{
38
hDib
=
GetClipboardData(CF_DIB);
39
CloseClipboard();
40
}
41
BITMAPINFO
*
pbi
=
(BITMAPINFO
*
)GlobalLock(hDib);
42
if
(pbi
!=
NULL)
43
{
44
BYTE
*
data
=
(BYTE
*
)(pbi
->
bmiColors);
45
if
(pbi
->
bmiHeader.biBitCount
<=
8
)
46
{
47
int
nColor
=
(pbi
->
bmiHeader.biClrUsed
==
0
)
?
48
1
<<
(pbi
->
bmiHeader.biBitCount) : pbi
->
bmiHeader.biClrUsed;
//
nColor颜色表中的颜色数
49
data
+=
sizeof
(RGBQUAD)
*
nColor;
50
}
51
StretchDIBits(pDC
->
GetSafeHdc(),
1
,
1
, pbi
->
bmiHeader.biWidth
-
1
,
52
pbi
->
bmiHeader.biHeight
-
1
,
0
,
0
, pbi
->
bmiHeader.biWidth,
53
pbi
->
bmiHeader.biHeight, data, pbi, DIB_RGB_COLORS, SRCCOPY);
//
显示,我为了保留外面的方框,前面宽度和高度都减了1
54
GlobalUnlock(hDib);
55
}
56
m_Chart.Refresh();
57
CView::OnTimer(nIDEvent);
58
}
另外,在动态创建picture control时候一定要注意创建的picture control的大小