一、CHistogramCtrl类介绍
这是CodeProject网站的一个优秀的类,能够在静态文本控件上实现曲线图的绘制,源代码网址如下:CHistogramCtrl, a windows 2000 like histogram control。源代码放到VS2013环境中只需要做一点修改就可以使用。这个类的具体代码如下:
HistogramCtrl.h
#if !defined(AFX_HISTOGRAMCTRL_H_H)
#define AFX_HISTOGRAMCTRL_H_H
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000 定义编译器版本
// HistogramCtrl.h : header file
//
#include "AfxTempl.h"
/
// CHistogramCtrl window
class CHistogramCtrl : public CWnd
{
// Construction
public:
CHistogramCtrl();
// Attributes
public:
typedef enum SPEED{ LOW_SPEED = 3000, NORMAL_SPEED = 1000, HIGH_SPEED = 500, IDLE = 0 };
// Operations
public:
BOOL Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, UINT uID);
void SetRange(UINT uLower, UINT uUpper);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CHistogramCtrl)
//}}AFX_VIRTUAL
// Implementation
public:
BOOL SetGridsColor(COLORREF cr);
BOOL SetBkColor(COLORREF cr);
void SetPen(int nWidth, COLORREF crColor);
CHistogramCtrl::SPEED SetSpeed(CHistogramCtrl::SPEED uSpeed);
void SetPos(UINT uPos);
virtual ~CHistogramCtrl();
// Generated message map functions
protected:
//{{AFX_MSG(CHistogramCtrl)
afx_msg void OnPaint();
afx_msg void OnTimer(UINT nIDEvent);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
enum SPEED m_uSpeed;
UINT m_yPos;
UINT m_yPreviousPos;
UINT m_uOffset;
void DrawLine();
BOOL InvalidateCtrl();
UINT GetAverage();
CDC *m_pMemDC;
UINT m_uLower;
UINT m_uUpper;
CRect m_rcClient;
CPen m_colorPen;
int m_nFirstLinePos;
COLORREF m_crBackGround;
COLORREF m_crGrids;
CList<UINT, UINT&> *m_pList;
};
/
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_HISTOGRAMCTRL_H_H)
HistogramCtrl.cpp
/*
Author:
Mehdi Mousavi
Email:
mehdi_mousavi@hotmail.com
webmaster@modemmania.com
Date of release:
14th of April 2001
Description:
The initial idea of doing this code has been put into my mind when my love asked me
to go to a clinic in order to check out my heart. You know, doctors have got some
sort of plotters that plots the information obtained from the heart on a piece of
paper as a histogram. The idea has been dropped into my mind there!
Dedicated to:
My love, Cindy
Copyright & disclaimer:
Permission is granted to distribute *unmodified* version of this source code.
You are entitled to use CHistogramCtrl for both commercial and/or educational
purposes as far as you keep these comments intact. Please feel free to post
any additions to the author.
*/
#include "stdafx.h"
#include "MyTaskManager.h"
#include "HistogramCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CHistogramCtrl
CHistogramCtrl::CHistogramCtrl()
{
m_nFirstLinePos = 13;
m_pMemDC = NULL;
m_pList = NULL;
m_uSpeed = IDLE;
m_crBackGround = RGB(0, 0, 0);
m_crGrids = RGB(0, 130, 66);
SetPen(1, RGB(0, 255, 0));
SetRange(1, 100);
}
CHistogramCtrl::~CHistogramCtrl()
{
if(m_pMemDC)
delete m_pMemDC;
if(m_pList)
delete m_pList;
}
BEGIN_MESSAGE_MAP(CHistogramCtrl, CWnd)
//{{AFX_MSG_MAP(CHistogramCtrl)
ON_WM_PAINT()
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CHistogramCtrl message handlers
BOOL CHistogramCtrl::Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, UINT uID)
{
//Postcondition:
// Creates a window within the "rect" region of the client screen
// Returns TRUE if the function creates the control successfully
// or FALSE if it fails.
BOOL bRet = CWnd::CreateEx(WS_EX_CLIENTEDGE,
AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW),
NULL,
dwStyle,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
pParentWnd->GetSafeHwnd(),
(HMENU)uID);
if(!bRet)
return FALSE;
m_pMemDC = new CDC;
if(!m_pMemDC)
return FALSE;
m_pList = new CList<UINT, UINT&>;
if(!m_pList)
return FALSE;
GetClientRect(m_rcClient);
if(!InvalidateCtrl())
return FALSE;
SetSpeed(CHistogramCtrl::NORMAL_SPEED);
return TRUE;
}
void CHistogramCtrl::SetRange(UINT uLower, UINT uUpper)
{
ASSERT(uLower && uUpper && uLower < uUpper);
//Postcondition:
// Sets the upper and lower (limits) range
m_uUpper = uUpper - uLower + 1;
m_uLower = 1;
m_uOffset = uLower - 1;
}
BOOL CHistogramCtrl::InvalidateCtrl()
{
//Postcondition:初始化网格
// Paints the entire client area of the control
// Returns TRUE if it's done successfuly or FALSE if it fails
CClientDC dc(this);
if(m_pMemDC->GetSafeHdc())
return FALSE;
if(!m_pMemDC->CreateCompatibleDC(&dc))
return FALSE;
CBitmap bmp;
if(!bmp.CreateCompatibleBitmap(&dc, m_rcClient.Width(), m_rcClient.Height()))
return FALSE;
if(!m_pMemDC->SelectObject(bmp))
return FALSE;
//Set the background color of the control
CBrush bkBrush;
if(!bkBrush.CreateSolidBrush(m_crBackGround))
return FALSE;
m_pMemDC->FillRect(m_rcClient, &bkBrush);
//Select a specified pen to the device context to draw background lines
CPen bkLinesPen;
if(!bkLinesPen.CreatePen(PS_SOLID, 1, m_crGrids))
return FALSE;
if(!m_pMemDC->SelectObject(bkLinesPen))
return FALSE;
//Draw background lines
for(register int i = m_rcClient.left - 1; i < m_rcClient.right; i += 13)
{//画竖直线
m_pMemDC->MoveTo(i, m_rcClient.top);
m_pMemDC->LineTo(i, m_rcClient.bottom);
}
for(register int j = m_rcClient.top - 1; j < m_rcClient.bottom; j += 13)
{//画水平线
m_pMemDC->MoveTo(m_rcClient.left, j);
m_pMemDC->LineTo(m_rcClient.right, j);
}
m_yPreviousPos = m_yPos = m_rcClient.bottom + 1;
InvalidateRect(m_rcClient);
return TRUE;
}
void CHistogramCtrl::OnPaint()
{
CPaintDC dc(this);
if(m_pMemDC->GetSafeHdc())
dc.BitBlt(0, 0, m_rcClient.Width(), m_rcClient.Height(), m_pMemDC, 0, 0, SRCCOPY);
}
void CHistogramCtrl::DrawLine()
{
//Postcondition:
// Draws the histogram within the client area of the control
if(!m_pMemDC->GetSafeHdc())
return;
//定时器刷新需要重绘的矩形区域,每次左移3个像素
CRect bkRect(m_rcClient.right - 3, m_rcClient.top, m_rcClient.right, m_rcClient.bottom);
CBrush bkBrush;
bkBrush.CreateSolidBrush(m_crBackGround);
m_pMemDC->FillRect(bkRect, &bkBrush);
//获取内存中DC,并将其向左错位复制到显示DC,错位3个像素
m_pMemDC->BitBlt(0, 0, m_rcClient.Width(), m_rcClient.Height(), m_pMemDC, 3, 0, SRCCOPY);
CPen myPen;
myPen.CreatePen(PS_SOLID, 1, m_crGrids);
m_pMemDC->SelectObject(myPen);
//画右边空出来的网格,计算左边第一条竖线位置
m_nFirstLinePos -= 3;
if(m_nFirstLinePos < 0)
m_nFirstLinePos += 13;
//计算右边要画的竖线位置
int nX = m_rcClient.right - ((m_rcClient.right - m_nFirstLinePos) % 13) - 1;
m_pMemDC->MoveTo(nX, m_rcClient.top);
m_pMemDC->LineTo(nX, m_rcClient.bottom);
for(register int j = m_rcClient.top - 1; j < m_rcClient.bottom; j += 13)
{
m_pMemDC->MoveTo(bkRect.left - 3, j);
m_pMemDC->LineTo(bkRect.right, j);
}
UINT uAverage = GetAverage();
TRACE("uAverage: %u\r\n", uAverage);
if(uAverage)
m_yPos = m_rcClient.bottom - (m_rcClient.bottom * uAverage/ m_uUpper);
if(m_yPreviousPos == (unsigned)m_rcClient.bottom + 1)
m_yPreviousPos = m_yPos;
m_pMemDC->SelectObject(m_colorPen);
m_pMemDC->MoveTo(m_rcClient.right - 11 - 3, m_yPreviousPos);
m_pMemDC->LineTo(m_rcClient.right - 11, m_yPos);
m_yPreviousPos = m_yPos;
Invalidate();
}
void CHistogramCtrl::SetPos(UINT uPos)
{
uPos -= m_uOffset;
ASSERT(uPos <= m_uUpper && uPos >= m_uLower);
//Postcondition:
// Adds the specified point to a list, so that we
// would be able to draw the histogram within the
// specified intervals.
m_pList->AddHead(uPos);
}
void CHistogramCtrl::OnTimer(UINT nIDEvent)
{
DrawLine();
}
CHistogramCtrl::SPEED CHistogramCtrl::SetSpeed(enum SPEED uSpeed)
{
enum SPEED oldSpeed = m_uSpeed;
m_uSpeed = uSpeed;
KillTimer(0);
if(uSpeed != CHistogramCtrl::IDLE)
SetTimer(0, m_uSpeed, NULL);
return oldSpeed;
}
void CHistogramCtrl::SetPen(int nWidth, COLORREF crColor)
{
m_colorPen.DeleteObject();
m_colorPen.CreatePen(PS_SOLID, nWidth, crColor);
}
BOOL CHistogramCtrl::SetBkColor(COLORREF cr)
{
BOOL bRet = FALSE;
enum SPEED oldSpeed = SetSpeed(CHistogramCtrl::IDLE);
m_crBackGround = cr;
CClientDC dc(this);
int oldPos = m_nFirstLinePos;
m_nFirstLinePos = 13;
if(m_pMemDC->DeleteDC())
if(InvalidateCtrl())
bRet = TRUE;
if(!bRet)
m_nFirstLinePos = oldPos;
SetSpeed(oldSpeed);
return bRet;
}
BOOL CHistogramCtrl::SetGridsColor(COLORREF cr)
{
BOOL bRet = FALSE;
enum SPEED oldSpeed = SetSpeed(CHistogramCtrl::IDLE);
m_crGrids = cr;
CClientDC dc(this);
int oldPos = m_nFirstLinePos;
m_nFirstLinePos = 13;
if(m_pMemDC->DeleteDC())
if(InvalidateCtrl())
bRet = TRUE;
if(!bRet)
m_nFirstLinePos = oldPos;
SetSpeed(oldSpeed);
return bRet;
}
UINT CHistogramCtrl::GetAverage()
{
UINT uCounter = 0, uSum = 0;
while(!m_pList->IsEmpty())
{
uSum += m_pList->RemoveTail();
uCounter++;
}
return uCounter ? uSum / uCounter : 0;
}
做出来的控件如下:
二、获取系统CPU使用率和物理内存使用率
1. 新建一个MFC应用程序,项目名称为MyTaskMan,保留确定按钮,如下图所示
2. 添加四个静态文本,设置用于显示曲线的两个静态文本控件Border为TRUE,设置用于显示CPU的控件ID为IDC_CPUCurve,用于显示物理内存的控件ID为IDC_MemoryCurve,如下图所示
3. 添加CHistogramCtrl类
在解决方案资源管理器选项卡中,项目名称右键添加现有项,将HistogramCtrl.cpp和HistogramCtrl.h,添加到项目中。修改HistogramCtrl.cpp中的头文件
在对话框类中添加两个CHistogramCtrl类对象,然后在MyTaskManDlg.h : 头文件中添加文件头#include "HistogramCtrl.h"
在CMyTaskManDlg类初始化函数OnInitDialog()中添加如下代码,同样要添加文件头#include "HistogramCtrl.h",在静态文本控件中显示绘图控件:
CRect rect;
GetDlgItem(IDC_CPUCurve)->GetWindowRect(rect);
ScreenToClient(rect);
m_ShowCPU.Create(WS_VISIBLE | WS_CHILD | WS_TABSTOP, rect, this, 1000);
m_ShowCPU.SetRange(1, 100);
m_ShowCPU.SetSpeed(CHistogramCtrl::HIGH_SPEED);
//m_ShowCPU.SetGridsColor(RGB(0, 0, 0));
//m_ShowCPU.SetBkColor(RGB(255, 255, 255));
//m_ShowCPU.SetPen(1, RGB(0, 255, 0));
CRect rect2;
GetDlgItem(IDC_MemoryCurve)->GetWindowRect(rect2);
ScreenToClient(rect2);
m_ShowMemory.Create(WS_VISIBLE | WS_CHILD | WS_TABSTOP, rect2, this, 1000);
m_ShowMemory.SetRange(1, 100);
m_ShowMemory.SetSpeed(CHistogramCtrl::HIGH_SPEED);
//m_ShowMemory.SetGridsColor(RGB(0, 0, 0));
//m_ShowMemory.SetBkColor(RGB(255, 255, 255));
m_ShowMemory.SetPen(2, RGB(255, 0, 0));
运行结果如下:
4. 添加自定义函数获取系统CPU使用率和物理内存使用率
获取CPU使用率函数:
void CMyTaskManDlg::GetCpuState(int& nStr)
{
static LONGLONG nFree{}, nNt{}, nUser{};
static LONGLONG nFreet{}, nNtt{}, nUsert{};
static DOUBLE nHave{};
GetSystemTimes((PFILETIME)&nFree, (PFILETIME)&nNt, (PFILETIME)&nUser);
nHave = (DOUBLE)(nFree - nFreet) / (DOUBLE)(nNt - nNtt + nUser - nUsert);
nStr = DWORD(100.0 - nHave * 100);
nFreet = nFree;
nNtt = nNt;
nUsert = nUser;
}
获取物理内存使用率:
DWORD CMyTaskManDlg::GetMemState()
{
MEMORYSTATUSEX nStatex;
nStatex.dwLength = sizeof(nStatex);
GlobalMemoryStatusEx(&nStatex);
DOUBLE nAll = (DOUBLE)nStatex.ullTotalPhys / 1048576;
DOUBLE nHave = (DOUBLE)nStatex.ullAvailPhys / 1048576;
return (DWORD)((nAll - nHave) / nAll * 100);
}
5. 设置定时器,每隔一段时间获取系统参数并显示
在初始化函数OnInitDialog()中开启定时器
SetTimer(0, 250, NULL); //设置定时器每250ms产生一个随机数
void CMyTaskManDlg::OnTimer(UINT_PTR nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
int nRandom = 0;
DWORD nNum = GetMemState();
GetCpuState(nRandom);
m_ShowCPU.SetPos(nRandom);
m_ShowMemory.SetPos(nNum);
CDialogEx::OnTimer(nIDEvent);
}
6. 最终运行结果如下: