1. 程式人生 > >圖表控制元件TeeChart乾貨分享(繪製2D、3D實時曲線---VC++示例原始碼--網路首發)

圖表控制元件TeeChart乾貨分享(繪製2D、3D實時曲線---VC++示例原始碼--網路首發)

最近接手了一個專案,其中涉及到MFC和實時曲線顯示的問題,由於我之前從未接觸過此類技術,現學現搞,把其間用到的覺得對初學者有用的東西,總結一下。

尤其是關於TeeChart控制元件部分,網上資料零碎,且很多不全面,程式碼難以使用。我苦尋數週在外國一些網站上尋到了一些有用的資訊,把相關的可執行的程式碼示例貼在文中,希望能幫到後來者。

(如有疑問可在帖子後面留言) 

MFC部分:

一、

分割窗體

新建一個單文件的MFC工程(注意在嚮導中設定視窗最大化和分割視窗支援)。

新建兩個對話方塊,用於分割視窗

【注意】對話方塊的樣式(Style)屬性改為下層(Child),邊框(Border)屬性改為None,最開始沒有改這個,程式執行的時候報錯了。

【注意】將兩個對話方塊生成從CFormView派生的類。

在CMainFrame的OnCreateClient中新增

【例1】把框架分割成兩列,右邊的一列和對話方塊繫結。

[cpp] view plaincopyprint?
  1. m_SplitterWnd.CreateStatic(this,1,2)); //把此框架視窗分割成1行2列。
  2. m_SplitterWnd.SetColumnInfo(0, 200, 0) ; //設定第0列的最大寬度為200,最小寬度為0 (此句話非常重要)
  3. CRect rect;  
  4. GetClientRect(&rect);  
  5.    //第1行第1列的視窗與CMyView繫結。其寬度為框架寬度的3/4.高度與框架的高度一致
  6. if(!m_SplitterWnd.CreateView(0,0,RUNTIME_CLASS(CMyView),CSize(rect.Width()/4*3,rect.Height()),pContext)||  
  7.    //第1行第2列的視窗與我們的對話方塊CMyDlg繫結。其寬度為框架寬度的1/4.
  8.   !m_SplitterWnd.CreateView(0,1,RUNTIME_CLASS(CMyDlg),  
  9.                          CSize(rect.Width()/4,rect.Height()),pContext))  
  10. {    
  11.    return FALSE;  
  12. }  
  13. return TRUE;  

【例2】在分割後的子視窗上繼續分割

在CMainFrame中新增兩個成員變數,型別為CSplitterWnd,如下所示

CSplitterWnd m_splitterWnd1;

CSplitterWnd m_splitterWnd2;

新增虛擬函式virtualBOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);

程式程式碼修改部分如下:

[cpp] view plaincopyprint?
  1. BOOLCMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)  
  2. {  
  3.    //建立一個靜態分欄視窗,分為一行二列
  4.    if(m_splitterWnd1.CreateStatic(this,1,2)==NULL)  
  5.         return FALSE;  
  6.    //設定分割視窗的大小***
  7.    m_splitterWnd1.SetColumnInfo(0, 200, 0) ; //設定第0列的最大寬度為200,最小寬度為0
  8.    //將CCSplitterWndView連線到0行0列窗格上
  9.    m_splitterWnd1.CreateView(0,0,RUNTIME_CLASS(CsplitterwndView),CSize(600,500),pContext);  
  10.    //將第0行1列再分開2行1列
  11.    if(m_splitterWnd2.CreateStatic(&m_splitterWnd1,2,1,WS_CHILD|WS_VISIBLE,  
  12.        m_splitterWnd1.IdFromRowCol(0, 1))==NULL)  
  13.        return FALSE;  
  14.    //將FormView1類連線到第二個分欄物件的0行0列
  15.    m_splitterWnd2.CreateView(0,0,RUNTIME_CLASS(CForm1),CSize(0,300),pContext);//因為是上下分割,故系統不關注寬度,只看高度,故寬度可以為0
  16.    //將FormView2類連線到第二個分欄物件的1行0列
  17.    m_splitterWnd2.CreateView(1,0,RUNTIME_CLASS(CForm2),CSize(0,0),pContext);  //此高度為0,意為分割後剩下的高度 就是它的了。
  18.    return TRUE;  
  19. }  
  20. //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?
  1. m_chart.Create("",WS_VISIBLE, CRect(0, 0, 0, 0), this, 1234) ; //動態建立TeeChart控制元件
  2. m_chart.GetLegend().SetVisible(false);//隱藏圖例
  3. m_chart.GetAspect().SetView3D(FALSE);  //取消3D顯示
  4. //設定圖示標題
  5. m_chart.GetHeader().GetText().SetItem(0,COleVariant("感測器實時資料曲線"));  
  6. //設定縱軸標題
  7. m_chart.GetAxis().GetLeft().GetTitle().SetCaption("數值");  
  8. //設定漸變背景
  9. m_chart.GetPanel().GetGradient().SetVisible(true);  
  10. m_chart.GetPanel().GetGradient().SetStartColor(RGB(192,192,192));  
  11. m_chart.GetPanel().GetGradient().SetEndColor(RGB(255,255,255));  
  12. //新增曲線
  13. m_chart.AddSeries(0);  
  14. //設定曲線屬性
  15. m_chart.Series(0).SetColor(RGB(255,0,0));//顏色
  16. m_chart.Series(0).GetAsLine().GetLinePen().SetWidth(2); //線型寬度
  17. //設定x軸的取值範圍
  18. m_chart.GetAxis().GetBottom().SetMinMax(0,100);  
  19. //設定x軸上值的格式
  20. m_chart.GetAxis().GetBottom().GetLabels().SetValueFormat("0.0");  

B、繪製部分:

在TeeChart控制元件所在的檢視類的自定義訊息響應函式OnMyMsg中,或是在定時器中,新增:

[cpp] view plaincopyprint?
  1. COleDateTimeCurTime = COleDateTime::GetCurrentTime();  
  2. COleDateTimeSpantmSpan = COleDateTimeSpan(0,0,1,0); //1s
  3. CStringcsTime ;  
  4. csTime= CurTime.Format("%H:%M:%S"); //獲取當前時間
  5. //在CMyView中畫曲線
  6. m_chart.Series(0).Add(yVal, csTime,RGB(255,0,0)); //第一個引數是y軸值,第二個引數是對應的x軸的標籤值(此為當前時間字串),第三個引數是所繪點的顏色。
  7. CurTime+= tmSpan;  
  8. m_chart.Series(0).RefreshSeries();  
  9. if(m_chart.Series(0).GetCount() > 100)  
  10. {  
  11.   m_chart.GetAxis().GetBottom().Scroll(1.0,true); //x座標軸一次移動1格
  12. }  

由於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?
  1. int CMyView::OnCreate(LPCREATESTRUCTlpCreateStruct)  
  2. {  
  3.        if(CFormView::OnCreate(lpCreateStruct) == -1)  
  4.               return-1;  
  5.        //TODO: Add your specialized creation code here
  6.        m_chart.Create("",WS_VISIBLE, CRect(0, 0, 0, 0), this, 1234) ;  
  7.        m_chart.GetLegend().SetVisible(false);//隱藏圖例
  8.        m_chart.GetAspect().SetView3D(true);  //3D顯示
  9.        m_chart.GetAxis().GetDepth().SetVisible(TRUE);  //顯示Z軸
  10.        m_chart.GetAxis().GetDepth().GetLabels().SetVisible(TRUE);  //顯示Z軸上的座標
  11.        m_chart.GetAxis().GetDepth().GetLabels().SetStyle(0);  //設定顯示座標的風格
  12.        //設定漸變背景
  13.        m_chart.GetPanel().GetGradient().SetVisible(true);  
  14.        m_chart.GetPanel().GetGradient().SetStartColor(RGB(192,192,192));  
  15.        m_chart.GetPanel().GetGradient().SetEndColor(RGB(255,255,255));  
  16.        //設定圖示標題
  17.        m_chart.GetHeader().GetText().SetItem(0,COleVariant("瀑布圖"));  
  18.        //開始繪製3D
  19.        m_chart.RemoveAllSeries();  
  20.        SeriesIndex.vt=VT_INT;  
  21.        SeriesIndex.intVal=m_chart.AddSeries(49);// 49號圖種,IsoSurface型別3D
  22.        m_chart.Series(0).GetAsIsoSurface().SetIrregularGrid(true);  
  23.        //設定曲線顏色
  24.        m_chart.Series(0).SetColor(RGB(255,0,0));  
  25.        m_chart.GetAxis().GetBottom().SetMinMax(0,100);  
  26.        m_chart.GetAxis().GetBottom().GetLabels().SetValueFormat(