圖表控制元件TeeChart乾貨分享(繪製2D、3D實時曲線---VC++示例原始碼--網路首發)
最近接手了一個專案,其中涉及到MFC和實時曲線顯示的問題,由於我之前從未接觸過此類技術,現學現搞,把其間用到的覺得對初學者有用的東西,總結一下。
尤其是關於TeeChart控制元件部分,網上資料零碎,且很多不全面,程式碼難以使用。我苦尋數週在外國一些網站上尋到了一些有用的資訊,把相關的可執行的程式碼示例貼在文中,希望能幫到後來者。
(如有疑問可在帖子後面留言)
MFC部分:
一、
分割窗體
新建一個單文件的MFC工程(注意在嚮導中設定視窗最大化和分割視窗支援)。
新建兩個對話方塊,用於分割視窗
【注意】對話方塊的樣式(Style)屬性改為下層(Child),邊框(Border)屬性改為None,最開始沒有改這個,程式執行的時候報錯了。
【注意】將兩個對話方塊生成從CFormView派生的類。
在CMainFrame的OnCreateClient中新增
【例1】把框架分割成兩列,右邊的一列和對話方塊繫結。
[cpp] view plaincopyprint?- m_SplitterWnd.CreateStatic(this,1,2)); //把此框架視窗分割成1行2列。
- m_SplitterWnd.SetColumnInfo(0, 200, 0) ; //設定第0列的最大寬度為200,最小寬度為0 (此句話非常重要)
- CRect rect;
- GetClientRect(&rect);
-
//第1行第1列的視窗與CMyView繫結。其寬度為框架寬度的3/4.高度與框架的高度一致
- if(!m_SplitterWnd.CreateView(0,0,RUNTIME_CLASS(CMyView),CSize(rect.Width()/4*3,rect.Height()),pContext)||
- //第1行第2列的視窗與我們的對話方塊CMyDlg繫結。其寬度為框架寬度的1/4.
- !m_SplitterWnd.CreateView(0,1,RUNTIME_CLASS(CMyDlg),
- CSize(rect.Width()/4,rect.Height()),pContext))
- {
-
return FALSE;
- }
- return TRUE;
【例2】在分割後的子視窗上繼續分割
在CMainFrame中新增兩個成員變數,型別為CSplitterWnd,如下所示
CSplitterWnd m_splitterWnd1;
CSplitterWnd m_splitterWnd2;
新增虛擬函式virtualBOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
程式程式碼修改部分如下:
[cpp] view plaincopyprint?- BOOLCMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
- {
- //建立一個靜態分欄視窗,分為一行二列
- if(m_splitterWnd1.CreateStatic(this,1,2)==NULL)
- return FALSE;
- //設定分割視窗的大小***
- m_splitterWnd1.SetColumnInfo(0, 200, 0) ; //設定第0列的最大寬度為200,最小寬度為0
- //將CCSplitterWndView連線到0行0列窗格上
- m_splitterWnd1.CreateView(0,0,RUNTIME_CLASS(CsplitterwndView),CSize(600,500),pContext);
- //將第0行1列再分開2行1列
- if(m_splitterWnd2.CreateStatic(&m_splitterWnd1,2,1,WS_CHILD|WS_VISIBLE,
- m_splitterWnd1.IdFromRowCol(0, 1))==NULL)
- return FALSE;
- //將FormView1類連線到第二個分欄物件的0行0列
- m_splitterWnd2.CreateView(0,0,RUNTIME_CLASS(CForm1),CSize(0,300),pContext);//因為是上下分割,故系統不關注寬度,只看高度,故寬度可以為0
- //將FormView2類連線到第二個分欄物件的1行0列
- m_splitterWnd2.CreateView(1,0,RUNTIME_CLASS(CForm2),CSize(0,0),pContext); //此高度為0,意為分割後剩下的高度 就是它的了。
- return TRUE;
- }
- //CsplitterwndView、CForm1、CForm2都是我們自定義的類,可以把他們換成對話方塊或表單等。
//初始左右分割框架,要呼叫函式SetColumnInfo來設定分割線位置
對分割出來的一列再進行分割,則是由CreateView中CSize的高度來確定分割線位置
*總結:
* 給框架視窗新增靜態拆分檢視的過程如下:
* 1. 給框架視窗類新增一個CsplitterWnd資料成員。
* 2. 覆蓋框架視窗的OnCreateClient函式,並呼叫CsplitterWnd::CreateStatic來建立靜態拆分檢視。
* 3. 使用CsplitterWnd::CreateView在每個靜態拆分視窗的窗格中建立檢視
* 使用靜態拆分視窗的一個優點是由於您自己給窗格新增檢視,所以可以控制放入檢視的種類
二、
新增自定義訊息響應
1、在Resource.h中新增
#define WM_MY_MESSAGE (WM_USER+100)
2、在CMyView的定義中新增: //CMyView是要響應自定義訊息的我們的檢視類
//{{AFX_MSG(CMyView)
afx_msg LRESULT OnMyMsg(WPARAM, LPARAM) ;
DECLARE_MESSAGE_MAP()
//}}AFX_MSG
3、在CMyView的實現cpp檔案中新增
BEGIN_MESSAGE_MAP(CMyView, CFormView)
//{{AFX_MSG_MAP(CMyView)
ON_MESSAGE(WM_MY_MESSAGE, OnMyMsg) //新增訊息對映
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
4、實現訊息對映函式LRESULT CMyView::OnMyMsg(WPARAM wParam, LPARAM lParam)
5、傳送訊息,觸發訊息響應函式
pMyView->PostMessage(WM_MY_MESSAGE,0, 0); //至於PostMessage和SendMessage的區別 請百度之。
//pMyView是CMyView類物件的指標。
TeeChart部分(以VC++6.0 TeeChart8.0為例)
至於如何獲取TeeChart控制元件,如何註冊控制元件,請百度之,網上有很多。
零
在相應的原始檔中新增TeeChart的標頭檔案 (有需要的自己再新增)
#include "tchart.h"
#include "series.h"
#include "valuelist.h"
#include "axes.h"
#include "axis.h"
#include "pen.h"
#include "axislabels.h"
#include "teefont.h"
#include "axistitle.h"
#include "aspect.h"
#include "fastlineseries.h"
#include "titles.h"
#include "fastlineseries.h"
#include "panel.h"
#include "legend.h"
#include "annotationtool.h"
#include "page.h"
#include "strings.h"
#include "gradient.h"
#include "IsoSurfaceSeries.h"
一、
在檢視類中動態新增TeeChart控制元件。(解決手工拖動新增控制元件,編譯報”Debug Assertion Failed” 錯的問題)
我們新增對話方塊資源讓其繼承自CFromView。首先手工靜態把控制元件拖到對話方塊上,然後建立類嚮導,生成一個物件m_chart。
在主框架CMainFrame::OnCreateClient()或OnCreate()中【看在哪個函式中分割視窗 產生檢視】
RecalcLayout(); //這一句很重要,沒有它,會報錯。
pView->OnInitialUpdate(); //pView是我們分割視窗得到的CMyDlgView檢視的指標。
在檢視類CMyDlgView中新增OnInitialUpdate()函式
CRect rect;
GetClientRect(&rect);
m_chart.MoveWindow(&rect, TRUE);
在檢視類CMyDlgView中新增WM_CREATE訊息響應函式OnCreate()在其中新增
m_chart.Create("",WS_VISIBLE, CRect(0, 0, 0, 0), this, 1234) ;//動態生成控制元件
m_chart.AddSeries(0);//操作控制元件
m_chart.Series(0).FillSampleValues(50);
//m_chart是我們的控制元件TeeChart
即可。
//但此為動態新增的控制元件,所有設定操作都得通過程式碼操作。
二、
繪製2D曲線
這個在網上有很多資料了。我在這裡再簡單總結一下其過程。
A、初始化部分:
在TeeChart控制元件所在的檢視類的OnCreate函式中,進行TeeChart控制元件的初始化工作。
[cpp] view plaincopyprint?- m_chart.Create("",WS_VISIBLE, CRect(0, 0, 0, 0), this, 1234) ; //動態建立TeeChart控制元件
- m_chart.GetLegend().SetVisible(false);//隱藏圖例
- m_chart.GetAspect().SetView3D(FALSE); //取消3D顯示
- //設定圖示標題
- m_chart.GetHeader().GetText().SetItem(0,COleVariant("感測器實時資料曲線"));
- //設定縱軸標題
- m_chart.GetAxis().GetLeft().GetTitle().SetCaption("數值");
- //設定漸變背景
- m_chart.GetPanel().GetGradient().SetVisible(true);
- m_chart.GetPanel().GetGradient().SetStartColor(RGB(192,192,192));
- m_chart.GetPanel().GetGradient().SetEndColor(RGB(255,255,255));
- //新增曲線
- m_chart.AddSeries(0);
- //設定曲線屬性
- m_chart.Series(0).SetColor(RGB(255,0,0));//顏色
- m_chart.Series(0).GetAsLine().GetLinePen().SetWidth(2); //線型寬度
- //設定x軸的取值範圍
- m_chart.GetAxis().GetBottom().SetMinMax(0,100);
- //設定x軸上值的格式
- m_chart.GetAxis().GetBottom().GetLabels().SetValueFormat("0.0");
B、繪製部分:
在TeeChart控制元件所在的檢視類的自定義訊息響應函式OnMyMsg中,或是在定時器中,新增:
[cpp] view plaincopyprint?- COleDateTimeCurTime = COleDateTime::GetCurrentTime();
- COleDateTimeSpantmSpan = COleDateTimeSpan(0,0,1,0); //1s
- CStringcsTime ;
- csTime= CurTime.Format("%H:%M:%S"); //獲取當前時間
- //在CMyView中畫曲線
- m_chart.Series(0).Add(yVal, csTime,RGB(255,0,0)); //第一個引數是y軸值,第二個引數是對應的x軸的標籤值(此為當前時間字串),第三個引數是所繪點的顏色。
- CurTime+= tmSpan;
- m_chart.Series(0).RefreshSeries();
- if(m_chart.Series(0).GetCount() > 100)
- {
- m_chart.GetAxis().GetBottom().Scroll(1.0,true); //x座標軸一次移動1格
- }
由於TeeChart繪製曲線點的函式Add,每呼叫一次才繪製一次,故需要有外部訊息激發訊息響應函式,才能把曲線動態繪製出來。
可以用設定定時器和自定義訊息響應函式的方式來實現。(定時器比較簡單,訊息響應函式上面MFC部分已經講過)
三、
繪製3D曲線
解決TeeChart8中繪製3D圖形報”Invalid class typecast” 錯的問題。
A、在承載TeeChart的對話方塊類Dlg的類定義中,新增:VARIANT SeriesIndex;
B、在類的相關方法中繪製,新增程式碼:
m_chart.RemoveAllSeries();
//下面的設定很重要(沒有的話,會出錯)
SeriesIndex.vt=VT_INT;
SeriesIndex.intVal=m_chart.AddSeries(scWaterfall);//scWaterfall=33瀑布圖的編號
m_chart.Series(0).GetAsWaterfall().SetIrregularGrid(true);
m_chart.Series(0).GetAsWaterfall().AddXYZ(x,y, z, NULL, RGB(255,0,0));
(TeeChart的3D圖有很多種,上面是以瀑布圖為例的,其他圖種的編號如下:)
const unsigned long scLine = 0;
const unsigned long scBar = 1;
const unsigned long scHorizBar = 2;
const unsigned long scArea = 3;
const unsigned long scPoint = 4;
const unsigned long scPie = 5;
const unsigned long scFastLine = 6;
const unsigned long scShape = 7;
const unsigned long scGantt = 8;
const unsigned long scBubble = 9;
const unsigned long scArrow = 10;
const unsigned long scCandle = 11;
const unsigned long scPolar = 12;
const unsigned long scSurface = 13;
const unsigned long scVolume = 14;
const unsigned long scErrorBar = 15;
const unsigned long scBezier = 16;
const unsigned long scContour = 17;
const unsigned long scError = 18;
const unsigned long scPoint3D = 19;
const unsigned long scRadar = 20;
const unsigned long scClock = 21;
const unsigned long scWindRose= 22;
const unsigned long scBar3D = 23;
const unsigned long scImageBar = 24;
const unsigned long scDonut = 25;
const unsigned long scTriSurface = 26;
const unsigned long scBox = 27;
const unsigned long scHorizBox = 28;
const unsigned long scHistogram = 29;
const unsigned long scColorGrid = 30;
const unsigned long scBarJoin = 31;
const unsigned long scHighLow = 32;
const unsigned long scWaterfall = 33;
const unsigned long scSmith = 34;
const unsigned long scPyramid = 35;
const unsigned long scMap = 36;
const unsigned long scHorizLine = 37;
const unsigned long scFunnel = 38;
const unsigned long scCalendar = 39;
const unsigned long scHorizArea = 40;
const unsigned long scPointFigure = 41;
const unsigned long scGauge = 42;
const unsigned long scVector3D = 43;
const unsigned long scTower = 44;
const unsigned long scPolarBar = 45;
const unsigned long scBubble3D = 46;
const unsigned long scHorizHistogram = 47;
const unsigned long scVolumePipe = 48;
const unsigned long scIsoSurface = 49;
const unsigned long scDarvas = 50;
const unsigned long scHighLowLine = 51;
const unsigned long scPolarGrid = 52;
const unsigned long scDeltaPoint = 53;
const unsigned long scImagePoint = 54;
const unsigned long scOrganizational = 55;
const unsigned long scWorld = 56;
const unsigned long scTagCloud = 57;
const unsigned long scKagi = 58;
const unsigned long scRenko = 59;
const unsigned long scNumericGauge = 60;
const unsigned long scLinearGauge = 61;
const unsigned long scCircularGauge = 62;
const unsigned long scBigCandle = 63;
const unsigned long scLinePoint = 64;
//如需要相關圖種,只需把上面程式碼
SeriesIndex.intVal= m_chart.AddSeries(scWaterfall);//把scWaterfall改為你所需圖種的編號
m_chart.Series(0).GetAsWaterfall().SetIrregularGrid(true);// GetAsWaterfall改為你所需圖種的相關函式名
----------------
一個完整的例子:
A、在CMyView(承載TeeChart的對話方塊檢視)的定義中,新增VARIANTSeriesIndex;
B、在int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct)函式中:
[cpp] view plaincopyprint?- int CMyView::OnCreate(LPCREATESTRUCTlpCreateStruct)
- {
- if(CFormView::OnCreate(lpCreateStruct) == -1)
- return-1;
- //TODO: Add your specialized creation code here
- m_chart.Create("",WS_VISIBLE, CRect(0, 0, 0, 0), this, 1234) ;
- m_chart.GetLegend().SetVisible(false);//隱藏圖例
- m_chart.GetAspect().SetView3D(true); //3D顯示
- m_chart.GetAxis().GetDepth().SetVisible(TRUE); //顯示Z軸
- m_chart.GetAxis().GetDepth().GetLabels().SetVisible(TRUE); //顯示Z軸上的座標
- m_chart.GetAxis().GetDepth().GetLabels().SetStyle(0); //設定顯示座標的風格
- //設定漸變背景
- m_chart.GetPanel().GetGradient().SetVisible(true);
- m_chart.GetPanel().GetGradient().SetStartColor(RGB(192,192,192));
- m_chart.GetPanel().GetGradient().SetEndColor(RGB(255,255,255));
- //設定圖示標題
- m_chart.GetHeader().GetText().SetItem(0,COleVariant("瀑布圖"));
- //開始繪製3D
- m_chart.RemoveAllSeries();
- SeriesIndex.vt=VT_INT;
- SeriesIndex.intVal=m_chart.AddSeries(49);// 49號圖種,IsoSurface型別3D
- m_chart.Series(0).GetAsIsoSurface().SetIrregularGrid(true);
- //設定曲線顏色
- m_chart.Series(0).SetColor(RGB(255,0,0));
- m_chart.GetAxis().GetBottom().SetMinMax(0,100);
- m_chart.GetAxis().GetBottom().GetLabels().SetValueFormat(