1. 程式人生 > >MFC中繪製動態曲線

MFC中繪製動態曲線

https://blog.csdn.net/zang141588761/article/details/50536788

在工控監測領域,經常需要動態繪製曲線,觀察曲線的變化趨勢,繪製波形圖,繪製頻譜等。在前面4講中介紹了MFC經常用的TeeChart控制元件和Hight-Speed
Chart
 Ctrl,這兩個都是MFC繪圖控制元件的經典(另外,在Qt中還有QwtPlotQCustomPlot兩大神器)。許多人問如何繪製動態變化的曲線,為此專門寫下這篇文章。


C++ GUI 繪圖控制元件目錄

MFC(VC)

Qt


















對於任何繪圖控制元件,都可以實現動態繪圖,其原則是:控制元件只負責繪圖,若想曲線動,就讓資料動,就像看電影一樣,電影是由一幀一幀的靜態圖片組合起來的,在一定速度上重新整理,靜態圖片就能動起來;和電影的原理一樣,繪圖控制元件能顯示靜態的曲線,想要它動起來,就讓它頻在一定時間重新整理就可以了

這就是動態繪圖的實現原理。

實現動態曲線需要以下兩個準備:

  1. 計時器Timer
  2. 陣列左移

基於Timer的繪圖

任何介面庫都會有Timer這個實現,在MFC中時OnTimer訊息,在Qt中是QTimer類,那種原理基本都一樣,下面將以MFC(VC)為例進行說明。

Timer是訊息級別最低的訊息,它會保證其它級別高的訊息優先執行,因此,就算資料大量重新整理,也不會影響主執行緒的其它訊息。

MFC生成OnTimer訊息,訊息響應函式如下:

[cpp]  view plain copy 在CODE上檢視程式碼片 派生到我的程式碼片
  1. void CTeeChartDlg::OnTimer(UINT_PTR nIDEvent)  
  2. {  
  3.     // TODO: 在此新增訊息處理程式程式碼或呼叫預設值  
  4.     CDialogEx::OnTimer(nIDEvent);  
  5. }  

繪圖的實現就在這個訊息響應函式裡

如果讓定時器設定為1秒觸發,每一秒把舊資料去除,繪製新資料,就能看到不停變換的波形;對於趨勢圖,假如每秒有一個新資料,那麼就在定長陣列中,把陣列所有資料整體左移,同時陣列末端加入新資料。程式碼如下:

[cpp]  view plain copy 在CODE上檢視程式碼片 派生到我的程式碼片
  1. ///   
  2. /// \brief 左移陣列  
  3. /// \param ptr 陣列指標  
  4. /// \param data 新數值  
  5. ///  
  6. void LeftMoveArray(double* ptr,size_t length,double data)  
  7. {  
  8.     for (size_t i=1;i<length;++i)  
  9.     {  
  10.         ptr[i-1] = ptr[i];  
  11.     }  
  12.     ptr[length-1] = data;  
  13. }  
此函式把整個陣列左移,然後新資料放置在陣列最末端(右端)。

這樣,陣列就實現“向左運動”,把左移後的陣列繪製,就能在繪圖控制元件上發現其變化。

下面開始實現動態繪圖(這裡演示TeeChart的方法,附件裡有HightSpeed-Chart CChartCtrl的方法):

[cpp]  view plain copy 在CODE上檢視程式碼片 派生到我的程式碼片
  1. void CTeeChartDlg::OnBnClickedButtonRuning()  
  2. {  
  3.     KillTimer(0);  
  4.     ZeroMemory(&m_TeeChartArray,sizeof(double)*m_c_arrayLength);  
  5.     for (size_t i=0;i<m_c_arrayLength;++i)  
  6.     {  
  7.         m_X[i] = i;  
  8.     }  
  9.     m_count = m_c_arrayLength;  
  10.     CSeries chart_T = (CSeries)m_Chart.Series(0);  
  11.     chart_T.Clear();  
  12.     m_pLineSerie->ClearSerie();  
  13.     SetTimer(0,1000,NULL);    
  14. }  
函式中幾個成員變數的定義是:

[cpp]  view plain copy 在CODE上檢視程式碼片 派生到我的程式碼片
  1. double m_TeeChartArray[2096];  
  2. double m_X[2096];  
  3. unsigned int m_count;  
  4. const size_t m_c_arrayLength = 2096;  

m_TeeChartArray是需要繪製的陣列的Y值,m_X是對應的x值,m_count是計數器,每繪製一次,個數加1,主要用於x軸

在timer中的實現如下:

[cpp]  view plain copy 在CODE上檢視程式碼片 派生到我的程式碼片
  1. void CTeeChartDlg::OnTimer(UINT_PTR nIDEvent)  
  2. {  
  3.     // TODO: 在此新增訊息處理程式程式碼和/或呼叫預設值  
  4.     if(0 == nIDEvent)  
  5.     {  
  6.         ++m_count;  
  7.         drawMoving();  
  8.     }  
  9.     CDialogEx::OnTimer(nIDEvent);  
  10. }  

drawMoving函式用於繪圖,timer設定為1秒觸發一次,這時就能看到每秒的變化,如果資料是以1秒為重新整理週期,每一秒有個新資料,只需要把舊的資料向左移,新資料放到陣列最右端,再在繪圖控制元件上把此圖形畫出來即可看的像動一樣。

drawMoving函式的實現如下:

[cpp]  view plain copy 在CODE上檢視程式碼片 派生到我的程式碼片
  1. void CTeeChartDlg::drawMoving()  
  2. {  
  3.     CSeries chart_T = (CSeries)m_Chart.Series(0);  
  4.     chart_T.Clear();  
  5.     m_pLineSerie->ClearSerie();  
  6.     LeftMoveArray(m_TeeChartArray,m_c_arrayLength,randf(0,10));  
  7.     LeftMoveArray(m_X,m_c_arrayLength,m_count);  
  8.     DrawLine_TeeChart(m_X,m_TeeChartArray,m_c_arrayLength);  
  9. }  

前面說過timer是優先順序最低的訊息,如果想曲線動的流暢,可以把時鐘設定為0ms,如

[cpp]  view plain copy 在CODE上檢視程式碼片 派生到我的程式碼片
  1. SetTimer(0,0,NULL);   
這時會在保證介面流暢的前提下,以最高頻率重新整理。這樣看到的圖形會非常流暢。

上面介紹的就是動態繪製曲線的思路和方法,附件中有用TeeChart實現和HightSpeedChart實現的例子,考慮到可能有些人沒有安裝TeeChart,專門把TeeChart分離出來了一個原始碼,只有HightSpeedChart,不需要安裝任何控制元件。


demo1:

MFC下TeeChart和HightSpeedChart動態繪製曲線圖-VS2010

demo2(不用安裝任何控制元件):

MFC動態繪製曲線圖-HightSpeedChart實現