MFC實現Windows自帶的任務管理器性能使用記錄功能

1概述

最近在項目中需要顯示實時監控的折線圖,通過在網上搜索解決方案,發現了一個開源的基於MFC的類實現了Windows任務管理器性能使用記錄的顯示。在此基礎上進行修改。在這裏對該類進行詳細的分析,並且把其實現原理進行分析,通過對原理分析,進而通過修改源碼能夠隨心所欲的製作我們需要的使用記錄窗口。

這是該類原主頁,是codeproject上的,有興趣的可以參考下。這是Mehdi Mousavi 先給他的愛人的,外國人都是這麼浪漫~~O(_)O~

CHistogramCtrl, a windows 2000 like histogram control

下面對該類進行詳細分析

首先我們分析一下任務管理器中顯示CPU使用記錄的表現,綠色網格在不停的向左滾動,在每次滾動中刷新出新的數據。表面上看上去是顯示的曲線,但是實際上如果通過兩點連線的方式,把每兩個點之間的距離縮小,看上去非常像曲線。所以可以這樣理解,每次綠色網格向做滾動時,最右面會出現一個點,這時會從原來的點向新出現的點畫線。

分析完曲線形成的原理,下面想辦法實現綠色網格的滾動顯示。其實眼睛是可以被欺騙的,下面這種方法便可以實現看上去是滾動的效果。

看上圖,方法時通過複製紅色框住的部分然後向左移動一小段距離粘貼顯示,然後重繪右邊空出的一小條屏幕,這樣讓人感覺整個屏幕在向左不斷的滾動。當然這裏需要好好處理一下綠色線條如何重繪。對於複製的方法下面會詳細解釋。

2.實現方法

繼續前面的介紹該功能如何實現,其實全部功能都通過一個類來實現的。即爲CHistogramCtrl(原文的類),該類中主要封裝了一些設置函數然後就是實現的方法。

其中最重要的函數就是create函數和drawline函數,前者是窗口的創造者,而在後者中實現了滾動效果及折線的顯示。下面重點說一下這兩部分的實現。

Create函數

對於create函數部分大家只需要瞭解一下其實現的方法即可,實際使用中並不需要對其進行更改。

   該類是繼承了CWnd類的,他使用了父類CWndCreate方法創建了一個窗口,實際使用時,我們是通過創建一個靜態STATIC來傳到這個方法中進而在STATIC中創建出該窗口的。後面使用方法會詳細介紹如何使用。

Drawline函數

該類中添加了一個定時器句柄,在定時器中定時執行Drawline函數,從而實現滾動效果。

在介紹Drawline之前,首先是要初始網格,在Invalidate裏面進行的。

for(register i = m_rcClient.left - 1; i < m_rcClient.right; i += 40)

{//橫向寬度爲40個像素點

       m_pMemDC->MoveTo(i, m_rcClient.top);

       m_pMemDC->LineTo(i, m_rcClient.bottom);

}

 

for(register j = m_rcClient.top - 1; j < m_rcClient.bottom; j += 13)

{//縱向寬度爲13個像素點

       m_pMemDC->MoveTo(m_rcClient.left, j);

       m_pMemDC->LineTo(m_rcClient.right, j);

}

其中標紅的部分時控制的初始網格寬度,本人希望在後面對這個類進行更改,增加更多的外部接口,從而使用更加方便,即不用過多的更改源代碼。下面具體介紹Drawline函數的實現。

首先就是通過下面這段代碼實現前面提到的區域拷貝並複製。

       //該矩形爲每次定時器刷新 需要重繪的舉行,跟每次刷新的像素數有關,現在是每次刷新10個像素

       CRect bkRect(m_rcClient.right - 20, m_rcClient.top, m_rcClient.right, m_rcClient.bottom);

       CBrush bkBrush;

       bkBrush.CreateSolidBrush(m_crBackGround);

       m_pMemDC->FillRect(bkRect, &bkBrush);

       //獲取內存中DC,並將其向左錯位複製到顯示DC,其實倒數第三個參數就是其錯位的像素數,現在錯位10個像素

       //通過該機制使得屏幕實現向左推移的效果。

       m_pMemDC->BitBlt(0, 0, m_rcClient.Width(), m_rcClient.Height(), m_pMemDC, 20, 0, SRCCOPY);

其中m_rcClient就是去的整個顯示窗口的區域。這段代碼的重點就是CDC的一個成員函數BitBlt,該函數實現了從源設備上下文拷貝位圖到這個當前設備上下文。可以Google一下該函數,具體用法如下:

BOOL   BitBlt(   int   x,   int   y,   int   nWidth,   int   nHeight,   CDC*   pSrcDC,   int   xSrc,   int   ySrc,   DWORD   dwRop   );

返回值:函數成功,返回非零值,否則爲0

參數:   x   指定目標矩形左上角的邏輯x座標。    

y   指定目標矩形左上角的邏輯y座標。    

nWidth   指定目標矩形和源位圖的寬度(邏輯單位)。    

nHeight   指定目標矩形和源位圖的高度(邏輯單位)。    

pSrcDC   指向CDC對象的指針,標識待拷貝位圖的設備上下文。如果dwRop指定不包括源的光柵操作,則它必須爲NULL    

xSrc   指定源位圖左上角的邏輯X座標。    

ySrc   指定源位圖左上角的邏輯Y座標。    

dwRop   指定要執行的光柵操作。光柵操作代碼定義GDC如何合併輸出操作中的顏色,包括當前畫刷、可能的源位圖和目標位圖。

通過函數參數的介紹可知,程序中BitBlt函數中的倒數第三個參數20正式每次向左移動的像素數。至此實現了拷貝,下面就是剩下那部分區域的重繪了。

重繪也很簡單,無非分兩部分,一部分是畫網格,另一部分就是畫我們所需要的折線。

畫網格:這一部分畫橫線很簡單,跟前面一致就可以了,但是畫豎線需要計算一下在那個位置畫,因爲必須保證畫出來後和前面拼接後仍然保持網格狀,我認爲這是本程序最難處理的部分。代碼如下:

       //m_nFirstLinePos 初始爲寬度,每次要減去想做錯位的像素數,

       m_nFirstLinePos -= 20;

       if(m_nFirstLinePos < 0)

              m_nFirstLinePos += 40;  

//畫新刷出來的豎線

       int nX = m_rcClient.right - ((m_rcClient.right - m_nFirstLinePos) % 40) - 1;

       m_pMemDC->MoveTo(nX, m_rcClient.top);

       m_pMemDC->LineTo(nX, m_rcClient.bottom);

這一部分我是通過反覆畫圖,嘗試才最終明白原理,原來左面網格是對齊的,通過移動後,會出現左右分別有不完成的網格,而中間是完整的網格,而左邊不完整的部分是可以計算的,就是用網格的寬度減去錯位的寬度。這時用總的長度減去左半部分不足一個網格的寬度後,再對網格寬度取模,剩下的部分就是右邊不足一個網格寬度,用總的減去它,就是最右邊那條線應該畫的位置,如果在應該重繪的區域內就畫出來,如果不在就不畫。-1是爲了應付如果正好在邊框處,這樣也可以顯示。

畫折線:這部分比較簡單了,代碼如下:

       for(int k=0; k<m_nLineNum ;k++)

       {

              m_pMemDC->SelectObject(m_colorPenAll[k]);

              //要減去 錯位的像素

              m_pMemDC->MoveTo(m_rcClient.right - 25 - 20, m_yPreviousPosAll[k]);

              m_pMemDC->LineTo(m_rcClient.right - 25, m_yPosAll[k]);

              m_yPreviousPosAll[k] = m_yPosAll[k];

       }

原類只實現了一條折線,本類能夠實現多條線,其實就是通過設置一下線的數目,然後用一個數組存儲各個位置。

3補充說明一些問題

原文中提到使用了一個CList存儲位置,是爲了如果在我們定時器刷新顯示的過程中有多個數據傳到我們的位置數據結構中,我們會全部保存下來,然後在下次刷新顯示時,取所有數據的平均值進行顯示。

本類沒有使用,當然這也是根據實際需要進行的,我需要的是最新值,不關心個別值,而且也可以通過控制刷新頻率減小誤差。

4使用方法

原文對使用方法有了很詳細的註解,在此不對設置函數進行解釋,只對如何將該類顯示在我們靜態控件中。

使用起來非常簡單。

1。在對話框中加入一個靜態控件,命名爲IDC_STATIC_HISTOGRAM,然後在對話框初始化函數中(如果沒有自己添加句柄)定義

CHistogramCtrl  m_ctrlHistogram;

然後添加如下代碼即可顯示

CRect rect;

// 獲取靜態控件的舉行

GetDlgItem(IDC_STATIC_HISTOGRAM)->GetWindowRect(rect);

// 將該矩形轉化爲客戶窗口

ScreenToClient(rect);

// 調用Create函數,並將上面的矩形傳入

m_ctrlHistogram.Create(WS_VISIBLE | WS_CHILD

      | WS_TABSTOP, rect, this, IDC_STATIC_HISTOGRAM);

這是就會在靜態框中顯示滾動的網格了

當然如果你要想顯示折線,需要定時的使用SetPos()設置每個時刻的位置,形成折線。我的類中重載了該方法,可以設置多條線的位置。

 

CHistogramCtrl就介紹這麼多,等有空後再把該類好好修改下,傳上來供大家參考。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章