65.9K
CodeProject 正在变化。 阅读更多。
Home

呼叫中心软件中使用的性能计

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (15投票s)

2012年3月12日

CPOL

2分钟阅读

viewsIcon

32183

downloadIcon

1520

如何制作一个看起来像 Windows 任务管理器中的性能计。

引言

Windows 任务管理器有一个不错的性能指标控制,用于显示关于 CPU 和内存使用的当前和历史数据。这种控制在许多监控应用程序中都很有用,例如我们呼叫中心软件中使用的那个。我们用它来测量几个关键因素,比如登录的代理数量,当前活跃的呼叫数量,以及电话线路和代理的利用率。

我们想要类似于 Windows 任务管理器中的性能指标表。它应该具有相似的外观和感觉。特别是,背景网格应该随数据移动,这样它就不会看起来是静态的。

这里展示的就是最终的代码。

创建视图类

我们使用一个 CView 派生类来显示性能指标表的绘图。由于所有的绘图代码都在 DrawPerf(CDC& dc, CRect rect) 类方法中,所以如果您需要创建一个控件类,应该可以使用相同的代码。有关创建自定义控件类的更多信息,请查看 Chris Maunder 的文章创建自定义控件

class CPerfMeterView : public CView
{
...
public:
    virtual void OnDraw(CDC* pDC);
protected:
    // actual drawing code
    void DrawPerf(CDC& dc, CRect rect);
    void DrawPerfLeft(CDC& dc, CRect rect, const CString& reading, int r, int total);
    void DrawPerfRight(CDC& dc, CRect rect, int total);
    void DrawPerfDataLineChart(CDC& dc, CRect rect);
protected:
    CFont*    m_pFont;
    CPen    m_penDottedGreen;
    CPen    m_penSolidGreen;
    CPen    m_penSolidYellow;
    CPen    m_penSolidDarkGreen;
    int    m_leadingTick;
    UINT    m_perfTimerId;
...
};

为了减少闪烁,我们使用了一个位图 DC。这里我们简单地使用了 MFC 功能包中包含的 CMemDC 类。包含文件是 "afxcontrolbarutil.h"。如果您使用的是旧版本的 VC++,您可以查看这里发布的一些关于无闪烁绘图的项目。

void CPerfMeterView::OnDraw(CDC* dc)
{
        // calc sizes
    CRect rcClient;
    GetClientRect(&rcClient);
        // use a buffer to reduce flicker
    CMemDC memDC(*dc, this);
    memDC.GetDC().SetBkMode(TRANSPARENT);
    if (! m_pFont)
        m_pFont = GetHeightFont("Tahoma", 12, memDC.GetDC());
    CFont* pOldFont = memDC.GetDC().SelectObject(m_pFont);
        // the actual drawing
    DrawPerf(memDC.GetDC(), rcClient);
    memDC.GetDC().SelectObject(pOldFont);
}

绘制指标表

性能指标表的实际绘制并不困难。大多数代码都涉及计算绘图的 CRect。顶层的绘图代码在 DrawPerf 中,它是从 OnDraw 函数调用的。背景颜色是系统的 COLOR_BTNFACE。此函数在计算每个指标表的宽度后,简单地调用 DrawPerfLeftDrawPerfRight

void CPerfMeterView::DrawPerf(CDC& dc, CRect rect)
{
    CPerfMeterDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (! pDoc)
        return;
    if (! dc.IsPrinting())
        dc.FillSolidRect(rect, ::GetSysColor(COLOR_BTNFACE));
        // do not draw if it is too small  
    if (rect.Width() < 100 || rect.Height() < 60)
        return;
        // get the meter reading
    CString reading;
    int r, total;
    pDoc->GetMeterReading(reading, r, total);
        // draw the left hand side meter
    CRect lrect(rect);
    lrect.right = 100;
    DrawPerfLeft(dc, lrect, reading, r, total);
        // draw the right hand side historical data
    CRect rrect(rect);
    rrect.left = 101;
    if (rrect.Width() < 100)
        return;
    DrawPerfRight(dc, rrect, total);
}

左侧指标表包含一个标题、一个带条形图的计量框,显示计量值的最大值 (tota) 和当前读数 r,使用实心绿色画笔或虚线绿色画笔。

void CPerfMeterView::DrawPerfLeft(CDC& dc, CRect rect, const CString& reading, int r, int total)
{
 CPerfMeterDoc* pDoc = GetDocument();
 // draw title
 CRect titlerect(rect);
 titlerect.left += 10;
 titlerect.top += 4;
 titlerect.bottom += 16;
 dc.DrawText("Title", titlerect, DT_SINGLELINE | DT_TOP | DT_LEFT);
 
 // draw meter outer box
 ... 
 // draw meter reading text
 ... 
 // draw meter line graph
 ...
 CPen* pOldPen = dc.SelectObject(&m_penDottedGreen);
 draw_bars(dc, rect, middle, totalbars - rbars, 1);
 dc.SelectObject(&m_penSolidGreen);
 draw_bars(dc, rect, middle, rbars, 0);
 dc.SelectObject(pOldPen);
}

右侧指标表用于绘制历史数据。它包含一个标题和一个折线图。这里我们使用一个成员变量 m_leadingTick 来跟踪每次绘图时背景网格的移动量。网格硬编码为 12 像素,每次移动是网格单元的 1/6。

void CPerfMeterView::DrawPerfRight(CDC& dc, CRect rect, int total)
{
 ...
 dc.DrawText("History", titlerect, DT_SINGLELINE | DT_TOP | DT_LEFT);
 ...
 // draw grid
 CPen* pOldPen = dc.SelectObject(&m_penSolidDarkGreen);
 int delta = 12;
 for (int i = rect.bottom - delta; i > rect.top; i = i - delta) {
  dc.MoveTo(rect.left, i);
  dc.LineTo(rect.right, i);
 }
 for (int i = rect.right - (m_leadingTick * delta/6); i > rect.left; i = i - delta) {
  if (i == rect.right)
   continue;
  dc.MoveTo(i, rect.top);
  dc.LineTo(i, rect.bottom);
 }
 DrawPerfDataLineChart(dc, rect);
 dc.SelectObject(pOldPen);
}

获取数据

视图类调用文档类以获取当前测量数据和历史数据。对于这个示例,实际数据是随机生成的。视图类使用以下三个方法。

class CPerfMeterDoc : public CDocument
{
...
    void UpdatePerfData();
    void GetMeterReading(CString& reading, int& r, int& total);
    BOOL GetPerfDataNext(int index, double& r);
...
protected:
        // sample data
    CArray<int>    m_perfs;
};

GetMeterReading 方法用于绘制左侧指标表。GetPerfDataNext 方法用于绘制历史折线图。

void CPerfMeterView::DrawPerfDataLineChart(CDC& dc, CRect rect)
{
 ...
 while (drawIndex > rect.left) {
  double r;
  if (! pDoc->GetPerfDataNext(dataIndex, r))
   break;
  int v = rect.bottom - int (rect.Height() * r / 100);
   
  if (lastr < 0)
   dc.MoveTo(drawIndex, v);
  else
   dc.LineTo(drawIndex, v);
  dc.LineTo(drawIndex - dataUnit, v);
  lastr = r;
  drawIndex -= dataUnit;
  dataIndex++;
 }
}

定时器

定时器用于刷新数据和指标表。当前定时器的流逝时间为 1 秒。在每个定时器计时时,m_leadingTick 被更新,并且图形失效以进行重绘。

void CPerfMeterView::OnTimer(UINT_PTR nIDEvent)
{
 if (nIDEvent == m_perfTimerId) {
  if (m_leadingTick++ >= 6)
   m_leadingTick = 0;
  CPerfMeterDoc* pDoc = GetDocument();
  if (pDoc)
   pDoc->UpdatePerfData();
  Invalidate();
  return;
 }
 CView::OnTimer(nIDEvent);
}
© . All rights reserved.