性能监控仪表的创建与实现

在许多监控应用程序中,如呼叫中心软件,监控CPU和内存的使用情况是非常有用的。希望创建一个类似于Windows任务管理器中性能仪表的控件,它应该具有相似的外观和感觉,特别是背景网格应该随着数据的变化而移动,以避免看起来静态。

为了实现这一目标,创建了一个派生自CView的类,用于显示性能仪表的绘制。由于所有的绘制代码都在DrawPerf(CDC& dc, CRect rect)类方法中,因此如果需要创建一个控件类,应该能够使用相同的代码。

为了减少闪烁,使用了一个位图DC。这里简单地使用了MFC特性包中包含的CMemDC类。如果使用的是旧版本的VC++,可以在这里发布的一些项目中查看无闪烁绘图。

以下是创建视图类的代码示例:

class CPerfMeterView : public CView { public: virtual void OnDraw(CDC* pDC); protected: 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; };

绘制性能仪表的实际代码并不困难。大部分代码涉及计算绘制的CRect。顶级绘制代码在DrawPerf中,它从OnDraw函数调用。背景颜色是系统的COLOR_BTNFACE。这个函数简单地调用DrawPerfLeft和DrawPerfRight,一旦它计算出每个仪表的宽度。

以下是绘制性能仪表的代码示例:

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)); if (rect.Width() < 100 || rect.Height() < 60) return; CString reading; int r, total; pDoc->GetMeterReading(reading, r, total); CRect lrect(rect); lrect.right = 100; DrawPerfLeft(dc, lrect, reading, r, total); CRect rrect(rect); rrect.left = 101; if (rrect.Width() < 100) return; DrawPerfRight(dc, rrect, total); }

左侧仪表包含一个标题,一个带有显示最大仪表值(total)和当前读数r的条形图的仪表盒,使用实心绿色笔或点状绿色笔。

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

以下是绘制右侧仪表的代码示例:

void CPerfMeterView::DrawPerfRight(CDC& dc, CRect rect, int total) { ... dc.DrawText("History", titlerect, DT_SINGLELINE | DT_TOP | DT_LEFT); ... 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: CArray 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++; } } 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); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485