Chinaunix首页 | 论坛 | 博客
  • 博客访问: 914602
  • 博文数量: 148
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 3920
  • 用 户 组: 普通用户
  • 注册时间: 2007-06-30 18:17
文章分类

全部博文(148)

文章存档

2008年(148)

我的朋友

分类:

2008-05-16 14:50:50

  用Chart控件绘制动态图表    
   
   
  ----   进行程序设计时,选用一个合适的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控件绘制曲线走势图:    
  各温度项以不同颜色的曲线表示;    
  横坐标为时间,纵坐标为温度值,均要求滚动显示;    
  在每次采样完成后,刷新屏幕。  
     
   
  ----设计思路    
   
  随着时间的推移,采集来的数据不断增加,不一定在一屏中显示,所以系统打开   一个实时数据库,存放采集来的实时数据。显示时,需要哪个时间段的数据,就从数据   库中读取。    
  在对话框资源编辑时,增加水平滚动条和垂直滚动条,以便配合Chart控件进行滚动   显示。    
  为对话框启动定时器,按采样间隔进行采样,并刷新屏幕显示。  
  ----主要相关代码如下:    
   
  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"。    
   
  ----限于篇幅,文中仅仅是一个简单示例的部分代码。在实际   应用中,一般会有更多的需求,比如:对坐标轴进行缩放显示;采样有可能得不到正确   的采样值时曲线显示不连续等等,这时需要根据需求编写相应代码。    
  Passing   an   Array   of   Values   to   the   Visual   C++   MSChart   OCX      
  Great   step-by-step   instructions   to   passing   data   to   the   VC++   MSChart   component    
   
  Published     July   27,   2000        
  By     JL   Colson    
     
   
   
       
   
  Step   1   :   Creating   the   Project  
  Start   Visual   C++   en   create   a   simple   dialog   based   application   labelled   "Graph"    
  Step   2   :   Add   the   MSChart   OCX   to   Your   Project  
  Select   "project   menu"   option   and   select   "Components   and   contols"   and   then   choose   the   MSChart   component   and   click   "add."    
   
   
     
   
   
  Step   3   :   Add   the   MSChart   OCX   to   Your   Dialog  
  Select   resources   view   tab   and   open   the   main   dialog.   (It抯   a   simple   dialog-based   application).   Drop   the   ocx   on   your   dialog   .    
  Now,   label   your   Chart   "IDC_MSCAHRT1"  
   
   
   
     
  Now,   choose   menu   option   "   Classwizard   "   to   create   a   member   variable   of   your   chart   labelled   "m_Chart"    
   
       
   
  Step   4:   Add   the   Code  
  Now   add   a   button   labeled   "Go"   to   your   dialog.   Double-click   it   to   edit   the   code   and   add   the   following   code   in   the   On_Go   function:    
   
  COleSafeArray   saRet;  
   
  DWORD   numElements[]   =   {10,   10};   //   10x10  
   
  //   Create   the   safe-array...  
   
  saRet.Create(VT_R8,   2,   numElements);  
   
  //   Initialize   it   with   values...  
   
  long   index[2];  
   
  for(index[0]=0;   index[0]<10;   index[0]++)   {  
    for(index[1]=0;   index[1]<10;   index[1]++)   {  
      double   val   =   index[0]   +   index[1]*10;  
      saRet.PutElement(index,   &val);  
    }  
  }  
   
  //   Return   the   safe-array   encapsulated   in   a   VARIANT...  
   
  m_Chart.SetChartData(saRet.Detach());  
   
  m_Chart.Refresh;    
   
  Step   5:   Building   and   Running   the   Application  
  Build   and   execute   your   app,   then   click   the   "Go"   button.   Here   is   the   result 
阅读(1638) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~