• (七)用ChartDirector绘制实时图表


    本示例演示如何用Web图表控件 ChartDirector 绘制一个配置有刷新率的实时图表。在本例中,由一个计时器驱动的随机数生成器生成新的数据值,新产生的值会转换到数据数组中,然后显示在图表上。图表由一个秒表进行更新,这样图表的刷新率可独立于数据率。此外,这个图表支持暂停以方便用户查看,但是后台的数据仍然在继续更新。

     

    用Web图表控件 ChartDirector 绘制一个配置有刷新率的实时图表

    图表刷新计时器调用CChartViewer.updateViewPort以触发CVN_ViewPortChanged消息,然后图表就会更新它的处理程序。下面提供了ChartDirector绘制实时图表的示例代码(PS:这个图表只适用于Windows)。

    代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    // realtimedemoDlg.cpp : implementation file
    //
     
    #include "stdafx.h"
    #include "realtimedemo.h"
    #include "realtimedemoDlg.h"
    #include <math.h>
     
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
     
    /////////////////////////////////////////////////////////////////////////////
    // CRealtimedemoDlg dialog
     
    static const int DataRateTimer = 1;
    static const int ChartUpdateTimer = 2;
    static const int DataInterval = 250;
     
    //
    // Constructor
    //
    CRealtimedemoDlg::CRealtimedemoDlg(CWnd* pParent /*=NULL*/)
        : CDialog(CRealtimedemoDlg::IDD, pParent)
    {
        m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }
     
    void CRealtimedemoDlg::DoDataExchange(CDataExchange* pDX)
    {
        CDialog::DoDataExchange(pDX);
        //{{AFX_DATA_MAP(CRealtimedemoDlg)
        DDX_Control(pDX, IDC_GammaValue, m_ValueC);
        DDX_Control(pDX, IDC_BetaValue, m_ValueB);
        DDX_Control(pDX, IDC_AlphaValue, m_ValueA);
        DDX_Control(pDX, IDC_ChartViewer, m_ChartViewer);
        DDX_Control(pDX, IDC_RunPB, m_RunPB);
        DDX_Control(pDX, IDC_UpdatePeriod, m_UpdatePeriod);
        //}}AFX_DATA_MAP
    }
     
    BEGIN_MESSAGE_MAP(CRealtimedemoDlg, CDialog)
        //{{AFX_MSG_MAP(CRealtimedemoDlg)
        ON_WM_PAINT()
        ON_WM_QUERYDRAGICON()
        ON_WM_TIMER()
        ON_WM_DESTROY()
        ON_BN_CLICKED(IDC_RunPB, OnRunPB)
        ON_BN_CLICKED(IDC_FreezePB, OnFreezePB)
        ON_CBN_SELCHANGE(IDC_UpdatePeriod, OnSelchangeUpdatePeriod)
        ON_CONTROL(CVN_ViewPortChanged, IDC_ChartViewer, OnViewPortChanged)
        //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
     
    /////////////////////////////////////////////////////////////////////////////
    // CRealtimedemoDlg message handlers
     
    //
    // Initialization
    //
    BOOL CRealtimedemoDlg::OnInitDialog()
    {
        CDialog::OnInitDialog();
     
        // *** code automatically generated by VC++ MFC AppWizard ***
        // Set the icon for this dialog.  The framework does this automatically
        //  when the application's main window is not a dialog
        SetIcon(m_hIcon, TRUE);         // Set big icon
        SetIcon(m_hIcon, FALSE);        // Set small icon
         
        //
        // Initialize member variables
        //
        m_extBgColor = getDefaultBgColor();     // Default background color
     
        // Clear data arrays to Chart::NoValue
        for (int i = 0; i < sampleSize; ++i)
            m_timeStamps[i] = m_dataSeriesA[i] = m_dataSeriesB[i] = m_dataSeriesC[i] = Chart::NoValue;
     
        // Set m_nextDataTime to the current time. It is used by the real time random number
        // generator so it knows what timestamp should be used for the next data point.
        SYSTEMTIME st;
        GetLocalTime(&st);
        m_nextDataTime = Chart::chartTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute,
            st.wSecond) + st.wMilliseconds / 1000.0;
     
        //
        // Initialize controls
        //
     
        // Set up the data acquisition mechanism. In this demo, we just use a timer to get a
        // sample every 250ms.
        SetTimer(DataRateTimer, DataInterval, 0);
     
        // The chart update rate (in ms)
        m_UpdatePeriod.SelectString(0, _T("1000"));
         
        // Load icons for the Run/Freeze buttons
        loadButtonIcon(IDC_RunPB, IDI_RunPB, 100, 20);
        loadButtonIcon(IDC_FreezePB, IDI_FreezePB, 100, 20);
     
        // Initially set the Run mode
        m_RunPB.SetCheck(1);
        OnRunPB();
     
        return TRUE;
    }
     
    // *** code automatically generated by VC++ MFC AppWizard ***
    // If you add a minimize button to your dialog, you will need the code below
    // to draw the icon.  For MFC applications using the document/view model,
    // this is automatically done for you by the framework.
    void CRealtimedemoDlg::OnPaint()
    {
        if (IsIconic())
        {
            CPaintDC dc(this); // device context for painting
     
            SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
     
            // Center icon in client rectangle
            int cxIcon = GetSystemMetrics(SM_CXICON);
            int cyIcon = GetSystemMetrics(SM_CYICON);
            CRect rect;
            GetClientRect(&rect);
            int x = (rect.Width() - cxIcon + 1) / 2;
            int y = (rect.Height() - cyIcon + 1) / 2;
     
            // Draw the icon
            dc.DrawIcon(x, y, m_hIcon);
        }
        else
        {
            CDialog::OnPaint();
        }
    }
     
    // *** code automatically generated by VC++ MFC AppWizard ***
    // The system calls this to obtain the cursor to display while the user drags
    // the minimized window.
    HCURSOR CRealtimedemoDlg::OnQueryDragIcon()
    {
        return (HCURSOR) m_hIcon;
    }
     
    //
    // User clicks on the Run pushbutton
    //
    void CRealtimedemoDlg::OnRunPB()
    {
        // Enable chart update timer
        CString s;
        m_UpdatePeriod.GetLBText(m_UpdatePeriod.GetCurSel(), s);
        SetTimer(ChartUpdateTimer, _tcstol(s, 0, 0), 0);
    }
     
    //
    // User clicks on the Freeze pushbutton
    //
    void CRealtimedemoDlg::OnFreezePB()
    {
        // Disable chart update timer
        KillTimer(ChartUpdateTimer);   
    }
     
    //
    // Handles timer events
    //
    void CRealtimedemoDlg::OnTimer(UINT_PTR nIDEvent)
    {
        switch (nIDEvent)
        {
        case DataRateTimer:
            // Is data acquisition timer - get a new data sample
            getData();
            break;
        case ChartUpdateTimer:
            // Is chart update timer - request chart update
            m_ChartViewer.updateViewPort(true, false);     
            break;
        }
         
        CDialog::OnTimer(nIDEvent);
    }
     
    //
    // View port changed event
    //
    void CRealtimedemoDlg::OnViewPortChanged()
    {
        drawChart(&m_ChartViewer);
    }
     
    //
    // User changes the chart update period
    //
    void CRealtimedemoDlg::OnSelchangeUpdatePeriod()
    {
        if (m_RunPB.GetCheck())
        {
            // Call freeze then run to use the new chart update period
            OnFreezePB();
            OnRunPB();
        }  
    }
     
    /////////////////////////////////////////////////////////////////////////////
    // CRealtimedemoDlg methods
     
    //
    // A utility to shift a new data value into a data array
    //
    static void shiftData(double *data, int len, double newValue)
    {
        memmove(data, data + 1, sizeof(*data) * (len - 1));
        data[len - 1] = newValue;
    }
     
    //
    // The data acquisition routine. In this demo, this is invoked every 250ms.
    //
    void CRealtimedemoDlg::getData()
    {
        // The current time in millisecond resolution
        SYSTEMTIME st;
        GetLocalTime(&st);
        double now = Chart::chartTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute,
            st.wSecond) + st.wMilliseconds / 1000.0;
         
        // This is our formula for the random number generator
        do
        {
            // Get a data sample
            double p = m_nextDataTime * 4;
            double dataA = 20 + cos(p * 129241) * 10 + 1 / (cos(p) * cos(p) + 0.01);
            double dataB = 150 + 100 * sin(p / 27.7) * sin(p / 10.1);
            double dataC = 150 + 100 * cos(p / 6.7) * cos(p / 11.9);
     
            // Shift the values into the arrays
            shiftData(m_dataSeriesA, sampleSize, dataA);
            shiftData(m_dataSeriesB, sampleSize, dataB);
            shiftData(m_dataSeriesC, sampleSize, dataC);
            shiftData(m_timeStamps, sampleSize, m_nextDataTime);
     
            m_nextDataTime += DataInterval / 1000.0;
        }
        while (m_nextDataTime < now);
     
        //
        // We provide some visual feedback to the latest numbers generated, so you can see the
        // data being generated.
        //
        char buffer[1024];
         
        sprintf(buffer, "%.2f", m_dataSeriesA[sampleSize - 1]);
        m_ValueA.SetWindowText(CString(buffer));
     
        sprintf(buffer, "%.2f", m_dataSeriesB[sampleSize - 1]);
        m_ValueB.SetWindowText(CString(buffer));
     
        sprintf(buffer, "%.2f", m_dataSeriesC[sampleSize - 1]);
        m_ValueC.SetWindowText(CString(buffer));
    }
     
    //
    // Draw the chart and display it in the given viewer
    //
    void CRealtimedemoDlg::drawChart(CChartViewer *viewer)
    {
        // Create an XYChart object 600 x 270 pixels in size, with light grey (f4f4f4)
        // background, black (000000) border, 1 pixel raised effect, and with a rounded frame.
        XYChart *c = new XYChart(600, 270, 0xf4f4f4, 0x000000, 1);
        c->setRoundedFrame(m_extBgColor);
         
        // Set the plotarea at (55, 62) and of size 520 x 175 pixels. Use white (ffffff)
        // background. Enable both horizontal and vertical grids by setting their colors to
        // grey (cccccc). Set clipping mode to clip the data lines to the plot area.
        c->setPlotArea(55, 62, 520, 175, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
        c->setClipping();
     
        // Add a title to the chart using 15 pts Times New Roman Bold Italic font, with a light
        // grey (dddddd) background, black (000000) border, and a glass like raised effect.
        c->addTitle("Field Intensity at Observation Satellite", "timesbi.ttf", 15
            )->setBackground(0xdddddd, 0x000000, Chart::glassEffect());
                 
        // Add a legend box at the top of the plot area with 9pts Arial Bold font. We set the
        // legend box to the same width as the plot area and use grid layout (as opposed to
        // flow or top/down layout). This distributes the 3 legend icons evenly on top of the
        // plot area.
        LegendBox *b = c->addLegend2(55, 33, 3, "arialbd.ttf", 9);
        b->setBackground(Chart::Transparent, Chart::Transparent);
        b->setWidth(520);
     
        // Configure the y-axis with a 10pts Arial Bold axis title
        c->yAxis()->setTitle("Intensity (V/m)", "arialbd.ttf", 10);
     
        // Configure the x-axis to auto-scale with at least 75 pixels between major tick and
        // 15  pixels between minor ticks. This shows more minor grid lines on the chart.
        c->xAxis()->setTickDensity(75, 15);
     
        // Set the axes width to 2 pixels
        c->xAxis()->setWidth(2);
        c->yAxis()->setWidth(2);
     
        // Now we add the data to the chart.
        double lastTime = m_timeStamps[sampleSize - 1];
        if (lastTime != Chart::NoValue)
        {
            // Set up the x-axis to show the time range in the data buffer
            c->xAxis()->setDateScale(lastTime - DataInterval * sampleSize / 1000, lastTime);
             
            // Set the x-axis label format
            c->xAxis()->setLabelFormat("{value|hh:nn:ss}");
     
            // Create a line layer to plot the lines
            LineLayer *layer = c->addLineLayer();
     
            // The x-coordinates are the timeStamps.
            layer->setXData(DoubleArray(m_timeStamps, sampleSize));
     
            // The 3 data series are used to draw 3 lines. Here we put the latest data values
            // as part of the data set name, so you can see them updated in the legend box.
            char buffer[1024];
     
            sprintf(buffer, "Alpha: <*bgColor=FFCCCC*> %.2f ", m_dataSeriesA[sampleSize - 1]);
            layer->addDataSet(DoubleArray(m_dataSeriesA, sampleSize), 0xff0000, buffer);
             
            sprintf(buffer, "Beta: <*bgColor=CCFFCC*> %.2f ", m_dataSeriesB[sampleSize - 1]);
            layer->addDataSet(DoubleArray(m_dataSeriesB, sampleSize), 0x00cc00, buffer);
             
            sprintf(buffer, "Gamma: <*bgColor=CCCCFF*> %.2f ", m_dataSeriesC[sampleSize - 1]);
            layer->addDataSet(DoubleArray(m_dataSeriesC, sampleSize), 0x0000ff, buffer);
        }
     
        // Set the chart image to the WinChartViewer
        viewer->setChart(c);
        delete c;
    }
     
    /////////////////////////////////////////////////////////////////////////////
    // General utilities
     
    //
    // Get the default background color
    //
    int CRealtimedemoDlg::getDefaultBgColor()
    {
        LOGBRUSH LogBrush;
        HBRUSH hBrush = (HBRUSH)SendMessage(WM_CTLCOLORDLG, (WPARAM)CClientDC(this).m_hDC,
            (LPARAM)m_hWnd);
        ::GetObject(hBrush, sizeof(LOGBRUSH), &LogBrush);
        int ret = LogBrush.lbColor;
        return ((ret & 0xff) << 16) | (ret & 0xff00) | ((ret & 0xff0000) >> 16);
    }
     
    //
    // Load an icon resource into a button
    //
    void CRealtimedemoDlg::loadButtonIcon(int buttonId, int iconId, int width, int height)
    {
        GetDlgItem(buttonId)->SendMessage(BM_SETIMAGE, IMAGE_ICON, (LPARAM)::LoadImage(
            AfxGetResourceHandle(), MAKEINTRESOURCE(iconId), IMAGE_ICON, width, height,
            LR_DEFAULTCOLOR)); 
    }

    欲了解chartdirector更多详细信息和下载可以参阅ChartDirector中文介绍+下载

    欢迎加群交流控件经验:301644590
  • 相关阅读:
    周末班:Python基础之面向对象基础
    二分查找
    各种推导式合集
    周末班:Python基础之函数进阶
    Python基础之迭代器和生成器
    Django中的信号
    周末班:Python基础之并发编程
    周末班:Python基础之网络编程
    Python基础之面对对象进阶
    Python基础之协程
  • 原文地址:https://www.cnblogs.com/flashcharts/p/2984607.html
Copyright © 2020-2023  润新知