<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    用Chart控件繪制動態圖表

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接 進行程序設計時,選用一個合適的ActiveX控件,有時可大大減少編程工作量。ActiveX 控件(又稱OCX)基于COM技術,作為獨立的軟件模塊,它可以在任何程序設計語言中插入使用。本文僅以VC++為例說明Chart控件的使用。

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

    一 安裝和使用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”,調用它進行刷新。如:

    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類的實現中有如下代碼:

    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"。

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

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全