用Chart控件繪製動態圖表

進行程序設計時,選用一個合適的ActiveX控件,有時可大大減小編程工做量。ActiveX 控件(又稱OCX)基於COM技術,做爲獨立的軟件模塊,它能夠在任何程序設計語言中插入使用。本文僅以VC++爲例說明Chart控件的使用。

  Chart控件指Mschart.ocx(5.0版)或Mschrt20.ocx(6.0 版),是Visual Studio自帶的ActiveX控件之一,其屬性、事件不少,功能很是強大,可實現柱狀直方圖、曲線走勢圖、餅狀比例圖等,甚至能夠是混合圖表,能夠是二維或三維圖表,能夠帶或不帶座標系,能夠自由配置各條目的顏色、字體等等。web

一 安裝和使用Chart控件

   在用到Chart控件的項目中安裝該控件:從Project->Add to Project->Components And Controls->Registered Active Xcontrols,選擇Chart控件,則 ClassWizard會生成相應的C++類,其中類CMSChart是由CWnd派生來的,它是Chart 控件的主要類,其餘的類所有是由COleDispatchDriver派生來,控制控件中的相應對象,完成各部分相關功能,如CvcAxis類是實現座標軸相關功能的源代碼。同時在項目的控件工具箱上會出現表明Chart控件的按鈕,使用時把Chart控件按鈕從工具箱拖到對話框中,調整大小便可。

  Chart控件至少有45個屬性、9個方法、49個事件,在這裏就不一一列舉了。數據庫

  在設計中,咱們能夠在主要屬性頁裏修改各屬性的屬性值:右擊對話框窗口中的Chart控件,選擇「Properties」菜單項,就會彈出主要屬性頁對話框,對其中各屬性值進行設置。有些屬性在主要屬性頁裏沒有列出,只能編程修改。另外要動態繪製圖表,必須掌握對控件的編程控制。編程

  首先在對話框類中定義控件變量,以便編程時操縱控件。如對話框類定義以下:函數

class CAbcDlg : public CDialog{
public:
    CAbcDlg(CWnd* pParent = NULL);	
//{{AFX_DATA(CAbcDlg)
    enum { IDD = IDD_ABC_DIALOG };
    CMSChart	m_Chart;
    //}}AFX_DATA
    ......
};

  ActiveX控件的屬性和方法在控件內部對應惟一一個整數索引值,編程時能夠經過索引來設置或獲取控件的屬性值,也能夠經過調用控件的C++類(在這裏就是CMSChart)的成員函數設置或獲取控件的屬性值及調用控件的方法。例如:工具

  在CMSChart類實現中有以下代碼:字體

CString CMSChart::GetData(){
    CString result;
InvokeHelper(0x9, DISPATCH_PROPERTYGET,
 VT_BSTR, (void*)&result, NULL);
    return result;
}
void CMSChart::SetData(LPCTSTR lpszNewValue){
    static BYTE parms[] =VTS_BSTR;
   InvokeHelper(0x9, DISPATCH_PROPERTYPUT,
     VT_EMPTY, NULL, parms,lpszNewValue);
}
void CMSChart::Refresh(){
	InvokeHelper(DISPID_REFRESH, 
    DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}

  這段代碼代表:屬性「Data」索引值爲0x9,咱們能夠調用函數SetData對圖表中某點的值進行設置。索引值爲DISPID_REFRESH的方法 「Refresh」,調用它進行刷新。如:spa

CString str=「34.5";
m_Chart.SetData(str);
m_Chart.Refresh();
......

  閱讀CMSChart類的實現會發現,有些屬性的值不是普通的BOOL、CString等數據類型,而是另外一個控件驅動類的類變量,如:設計

CVcPlot CMSChart::GetPlot(){
    LPDISPATCH pDispatch;
InvokeHelper(0x28, DISPATCH_PROPERTYGET,
 VT_DISPATCH, (void*)&pDispatch, NULL);
    return CVcPlot(pDispatch);
}

  在CVcPlot類的實現中有以下代碼:orm

CVcAxis CVcPlot::GetAxis
 (long axisID, const VARIANT& Index){
    LPDISPATCH pDispatch;
    static BYTE parms[] =VTS_I4 VTS_VARIANT;
InvokeHelper(0x1f, DISPATCH_PROPERTYGET,
VT_DISPATCH, (void*)&pDispatch, parms, axisID, &Index);
    return CVcAxis(pDispatch);
}

  而CVcAxis類的實現中有以下代碼:對象

CVcValueScale CVcAxis::GetValueScale(){
    LPDISPATCH pDispatch;
InvokeHelper(0x9, DISPATCH_PROPERTYGET,
 VT_DISPATCH, (void*)&pDispatch, NULL);
    return CVcValueScale(pDispatch);
}

  而CVcValueScale類的實現中又有以下代碼:

void CVcValueScale::SetMaximum(double newValue){
    static BYTE parms[] =VTS_R8;
InvokeHelper(0x3, DISPATCH_PROPERTYPUT,
 VT_EMPTY, NULL, parms,newValue);
}

  這正是Chart控件的靈活性所在,根據上述代碼,以下的調用:

    VARIANT var;
m_Chart.GetPlot().GetAxis(1, var).GetValueScale()
.SetMaximum(50.0);
可實現把縱座標的最大刻度設爲50.0。

  控件觸發的事件,如Click、MouseDown等,若是須要處理,能夠經過ClassWizard在對話框類中定義相應的處理函數,實現相關的處理功能。

二 動態繪製圖表實例

   在一個溫度採集系統中,但願把採集來的各項溫度值實時顯示,用Chart控件繪製曲線走勢圖:
  1. 各溫度項以不一樣顏色的曲線表示;
  2. 橫座標爲時間,縱座標爲溫度值,均要求滾動顯示;
  3. 在每次採樣完成後,刷新屏幕。

  設計思路

  1. 隨着時間的推移,採集來的數據不斷增長,不必定在一屏中顯示,因此係統打開一個實時數據庫,存放採集來的實時數據。顯示時,須要哪一個時間段的數據,就從數據庫中讀取。
  2. 在對話框資源編輯時,增長水平滾動條和垂直滾動條,以便配合Chart控件進行滾動顯示。
  3. 爲對話框啓動定時器,按採樣間隔進行採樣,並刷新屏幕顯示。

  主要相關代碼以下:

BOOL CAbcDlg::OnInitDialog(){
    CDialog::OnInitDialog();
pDataDB = new dbase;
//實時數據記錄庫,類dbase的基類爲CDaoRecordset
    pDataDB->Open(dbOpenDynaset, 「select
* from data");
    VARIANT var;
m_Chart.GetPlot().GetAxis(1,var).GetValueScale().
  SetAuto(FALSE);//不自動標註y軸刻度
m_Chart.GetPlot().GetAxis(1, var).GetValueScale().
  SetMaximum(37);//y軸最大刻度
m_Chart.GetPlot().GetAxis(1, var).GetValueScale().
  SetMinimum(32);//y軸最小刻度
m_Chart.GetPlot().GetAxis(1,var).GetValueScale().
  SetMajorDivision(5);//y軸刻度5等分
m_Chart.GetPlot().GetAxis(1,var).GetValueScale().
  SetMinorDivision(1);//每刻度一個刻度線
m_Chart.SetColumnCount(3); //3個溫度項,3條曲線
   m_Chart.GetPlot().GetSeriesCollection().GetItem(1).
   GetPen().GetVtColor().Set(0, 0, 255);//線色
   m_Chart.GetPlot().GetSeriesCollection().GetItem(2).
    GetPen().GetVtColor().Set(255, 0, 0);
   m_Chart.GetPlot().GetSeriesCollection().GetItem(3).
    GetPen().GetVtColor().Set(0, 255, 0);
   m_Chart.GetPlot().GetSeriesCollection().
    GetItem(1).GetPen().SetWidth(2);//線寬
   m_Chart.GetPlot().GetSeriesCollection().
    GetItem(2).GetPen().SetWidth(2);
   m_Chart.GetPlot().GetSeriesCollection().
    GetItem(3).GetPen().SetWidth(2);
   m_Chart.SetRowCount(10); //一屏顯示10個採樣時刻
 m_Chart.GetPlot().GetAxis(0,var).GetCategoryScale().
   SetAuto(FALSE);//不自動標註x軸刻度
 m_Chart.GetPlot().GetAxis(0,var).GetCategoryScale().
   SetDivisionsPerLabel(1);//每時刻一個標註
   m_Chart.GetPlot().GetAxis(0,var).GetCategoryScale().
    SetDivisionsPerTick(1);//每時刻一個刻度線
      m_ScrLeft.SetScrollRange(0,45);
     //垂直滾動條可滾動範圍(溫度值範圍0-50,
      每滾動1度,一屏顯示5度)
    m_ScrLeft.SetScrollPos(45-32);//垂直滾動條的當前位置
    m_ScrBottom.SetScrollRange(0, 0);//水平滾動條的可滾動範圍
    m_ScrBottom.SetScrollPos(0);//水平滾動條的當前位置
    SetTimer(23, 300000, NULL);//啓動定時器,定時間隔5分鐘
    Sample();//調用採樣函數進行第一次採樣,並把數據記錄入庫
     return TRUE;  
}
void CAbcDlg::OnTimer(UINT nIDEvent) {
    Sample();//採樣,並把數據記錄入庫
    if (pDataDB->GetRecordCount()>10)
  theApp.nBottomRange = pDataDB->GetRecordCount()-10;
    else
theApp.nBottomRange = 0; 
//用全局變量保存水平滾動條的範圍值
m_ScrBottom.SetScrollRange(0,theApp.nBottomRange);
    theApp.nBottomPos = theApp.nBottomRange;
   m_ScrBottom.SetScrollPos(theApp.nBottomPos);
    //修正水平滾動條的顯示
    DrawPic();//調用函數,刷新曲線顯示
	
    CDialog::OnTimer(nIDEvent);
}
void CAbcDlg::DrawPic() {
char s[10];
    UINT row = 1;
    pDataDB->MoveFirst();
pDataDB->Move(theApp.nBottomPos);
//只從數據庫中取某時間段的數據進行顯示
    while ((!pDataDB->IsEOF()) && (row <= 10)){
    m_Chart.SetRow(row);
m_Chart.SetRowLabel((LPCTSTR)pDataDB
->m_date_time.Format(「%H:%M"));
//以採樣時刻作x軸的標註
    m_Chart.SetColumn(1);
    sprintf(s, 「%6.2f", pDataDB->m_No1);
    m_Chart.SetData((LPCSTR)s);
    m_Chart.SetColumn(2);
    sprintf(s, 「%6.2f", pDataDB->m_No2);
    m_Chart.SetData((LPCSTR)s);
    m_Chart.SetColumn(3);
    sprintf(s, 「%6.2f", pDataDB->m_No3);
    m_Chart.SetData((LPCSTR)s);
    pDataDB->MoveNext();
    row++;
    }
    while ((row <= 10)){
    m_Chart.SetRow(row);
    m_Chart.SetRowLabel((LPCTSTR)「");
m_Chart.GetDataGrid().SetData(row, 1, 0, 1);
//採樣數據不足10個點, 對應的位置不顯示
    m_Chart.GetDataGrid().SetData(row, 2, 0, 1);
    m_Chart.GetDataGrid().SetData(row, 3, 0, 1);
    row++;
    }
    m_Chart.Refresh();
}
void CAbcDlg::OnHScroll(UINT nSBCode, 
    UINT nPos, CScrollBar* pScrollBar) {
    if (pDataDB->GetRecordCount()>10)
  theApp.nBottomRange = pDataDB->GetRecordCount()-10;
    else
    theApp.nBottomRange = 0;
 m_ScrBottom.SetScrollRange(0, theApp.nBottomRange);
    switch (nSBCode){
    case SB_LINERIGHT:
   if (theApp.nBottomPos < theApp.nBottomRange){
   theApp.nBottomPos = theApp.nBottomPos + 1;
   m_ScrBottom.SetScrollPos(theApp.nBottomPos);
    DrawPic();
    }
    break;
    case SB_LINELEFT:
    if (theApp.nBottomPos > 0){
   theApp.nBottomPos = theApp.nBottomPos - 1;
    m_ScrBottom.SetScrollPos(theApp.nBottomPos);
    DrawPic();
    }
    break;
    }
    CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
void CAbcDlg::OnVScroll(UINT nSBCode, 
    UINT nPos, CScrollBar* pScrollBar) {
    VARIANT var;
    double max1,min1,f;
    switch (nSBCode){
    case SB_LINEDOWN:
f = m_Chart.GetPlot().GetAxis(1, var).
  GetValueScale().GetMinimum() - 1;
    if (f>=0) {//最小刻度大於等於0, 則能夠滾動
m_Chart.GetPlot().GetAxis(1, var).GetValueScale().
  SetMinimum(f);
f = m_Chart.GetPlot().GetAxis
 (1, var).GetValueScale().GetMaximum() - 1;
m_Chart.GetPlot().GetAxis(1, var).GetValueScale().
SetMaximum(f);
  pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() + 1);
    m_Chart.Refresh();
    }
    break;
    case SB_LINEUP:
f = m_Chart.GetPlot().GetAxis(1, var).
GetValueScale().GetMaximum() + 1;
    if (f <= 50) {//最大刻度小於等於50, 則能夠滾動
m_Chart.GetPlot().GetAxis
(1, var).GetValueScale().SetMaximum(f);
f = m_Chart.GetPlot().GetAxis(1, var).
GetValueScale().GetMinimum() + 1;
m_Chart.GetPlot().GetAxis(1, var).GetValueScale().
  SetMinimum(f);
  pScrollBar->SetScrollPos(pScrollBar->GetScrollPos() - 1);
    m_Chart.Refresh();
    }
    break;
    }
    CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
}

  特別注意,程序中用到的關於控件的類,如CVcAxis等,須要在AbcDlg.cpp文件的開始處說明:#include 「VcAxis.h"。

  限於篇幅,文中僅僅是一個簡單示例的部分代碼。在實際應用中,通常會有更多的需求,好比:對座標軸進行縮放顯示;採樣有可能得不到正確的採樣值時曲線顯示不連續等等,這時須要根據需求編寫相應代碼。

相關文章
相關標籤/搜索