繪圖控件講解——繪制動(dòng)態(tài)曲線
在工控監(jiān)測(cè)領(lǐng)域,經(jīng)常需要?jiǎng)討B(tài)繪制曲線,觀察曲線的變化趨勢(shì),繪制波形圖,繪制頻譜等。在前面4講中介紹了MFC經(jīng)常用的TeeChart控件和Hight-Speed Chart?Ctrl,這兩個(gè)都是MFC繪圖控件的經(jīng)典(另外,在Qt中還有QwtPlot和QCustomPlot兩大神器)。許多人問如何繪制動(dòng)態(tài)變化的曲線,為此專門寫下這篇文章。
C++ GUI 繪圖控件目錄
MFC(VC)
VS2010 使用TeeChart繪圖控件 - 之一 - 控件和類的導(dǎo)入VS2010 使用TeeChart繪圖控件 - 之二 - 繪制圖形(折線圖,柱狀圖)TeeChart繪圖控件 - 之三 - 提高繪圖的效率MFC下好用的高速繪圖控件-(Hight-Speed Charting)繪制動(dòng)態(tài)曲線
Qt
qt超強(qiáng)精美繪圖控件 - QCustomPlot一覽qt超強(qiáng)繪圖控件qwt - 安裝及配置
對(duì)于任何繪圖控件,都可以實(shí)現(xiàn)動(dòng)態(tài)繪圖,其原則是:控件只負(fù)責(zé)繪圖,若想曲線動(dòng),就讓數(shù)據(jù)動(dòng),就像看電影一樣,電影是由一幀一幀的靜態(tài)圖片組合起來的,在一定速度上刷新,靜態(tài)圖片就能動(dòng)起來;和電影的原理一樣,繪圖控件能顯示靜態(tài)的曲線,想要它動(dòng)起來,就讓它頻在一定時(shí)間刷新就可以了。
這就是動(dòng)態(tài)繪圖的實(shí)現(xiàn)原理。
實(shí)現(xiàn)動(dòng)態(tài)曲線需要以下兩個(gè)準(zhǔn)備:
計(jì)時(shí)器Timer數(shù)組左移
基于Timer的繪圖
任何界面庫都會(huì)有Timer這個(gè)實(shí)現(xiàn),在MFC中時(shí)OnTimer消息,在Qt中是QTimer類,那種原理基本都一樣,下面將以MFC(VC)為例進(jìn)行說明。
Timer是消息級(jí)別最低的消息,它會(huì)保證其它級(jí)別高的消息優(yōu)先執(zhí)行,因此,就算數(shù)據(jù)大量刷新,也不會(huì)影響主線程的其它消息。
MFC生成OnTimer消息,消息響應(yīng)函數(shù)如下:
void?CTeeChartDlg::OnTimer(UINT_PTR?nIDEvent) { //?TODO:?在此添加消息處理程序代碼或調(diào)用默認(rèn)值 CDialogEx::OnTimer(nIDEvent); }
繪圖的實(shí)現(xiàn)就在這個(gè)消息響應(yīng)函數(shù)里
如果讓定時(shí)器設(shè)定為1秒觸發(fā),每一秒把舊數(shù)據(jù)去除,繪制新數(shù)據(jù),就能看到不停變換的波形;對(duì)于趨勢(shì)圖,假如每秒有一個(gè)新數(shù)據(jù),那么就在定長(zhǎng)數(shù)組中,把數(shù)組所有數(shù)據(jù)整體左移,同時(shí)數(shù)組末端加入新數(shù)據(jù)。代碼如下:
///? ///?brief?左移數(shù)組 ///?param?ptr?數(shù)組指針 ///?param?data?新數(shù)值 /// void?LeftMoveArray(double*?ptr,size_t?length,double?data) { for?(size_t?i=1;i<length;++i) { ptr[i-1]?=?ptr[i]; } ptr[length-1]?=?data; }
此函數(shù)把整個(gè)數(shù)組左移,然后新數(shù)據(jù)放置在數(shù)組最末端(右端)。
這樣,數(shù)組就實(shí)現(xiàn)“向左運(yùn)動(dòng)”,把左移后的數(shù)組繪制,就能在繪圖控件上發(fā)現(xiàn)其變化。
下面開始實(shí)現(xiàn)動(dòng)態(tài)繪圖(這里演示TeeChart的方法,附件里有HightSpeed-Chart CChartCtrl的方法):
void?CTeeChartDlg::OnBnClickedButtonRuning() { KillTimer(0); ZeroMemory(&m_TeeChartArray,sizeof(double)*m_c_arrayLength); for?(size_t?i=0;iClearSerie(); SetTimer(0,1000,NULL); }
函數(shù)中幾個(gè)成員變量的定義是:
double?m_TeeChartArray[2096]; double?m_X[2096]; unsigned?int?m_count; const?size_t?m_c_arrayLength?=?2096;
m_TeeChartArray是需要繪制的數(shù)組的Y值,m_X是對(duì)應(yīng)的x值,m_count是計(jì)數(shù)器,每繪制一次,個(gè)數(shù)加1,主要用于x軸
在timer中的實(shí)現(xiàn)如下:
void?CTeeChartDlg::OnTimer(UINT_PTR?nIDEvent) { //?TODO:?在此添加消息處理程序代碼和/或調(diào)用默認(rèn)值 if(0?==?nIDEvent) { ++m_count; drawMoving(); } CDialogEx::OnTimer(nIDEvent); }
drawMoving函數(shù)用于繪圖,timer設(shè)定為1秒觸發(fā)一次,這時(shí)就能看到每秒的變化,如果數(shù)據(jù)是以1秒為刷新周期,每一秒有個(gè)新數(shù)據(jù),只需要把舊的數(shù)據(jù)向左移,新數(shù)據(jù)放到數(shù)組最右端,再在繪圖控件上把此圖形畫出來即可看的像動(dòng)一樣。
drawMoving函數(shù)的實(shí)現(xiàn)如下:
void?CTeeChartDlg::drawMoving() { CSeries?chart_T?=?(CSeries)m_Chart.Series(0); chart_T.Clear(); m_pLineSerie->ClearSerie(); LeftMoveArray(m_TeeChartArray,m_c_arrayLength,randf(0,10)); LeftMoveArray(m_X,m_c_arrayLength,m_count); DrawLine_TeeChart(m_X,m_TeeChartArray,m_c_arrayLength); }
前面說過timer是優(yōu)先級(jí)最低的消息,如果想曲線動(dòng)的流暢,可以把時(shí)鐘設(shè)置為0ms,如
SetTimer(0,0,NULL);
這時(shí)會(huì)在保證界面流暢的前提下,以最高頻率刷新。這樣看到的圖形會(huì)非常流暢。
上面介紹的就是動(dòng)態(tài)繪制曲線的思路和方法,附件中有用TeeChart實(shí)現(xiàn)和HightSpeedChart實(shí)現(xiàn)的例子,考慮到可能有些人沒有安裝TeeChart,專門把TeeChart分離出來了一個(gè)源碼,只有HightSpeedChart,不需要安裝任何控件。
demo1:
MFC下TeeChart和HightSpeedChart動(dòng)態(tài)繪制曲線圖-VS2010
demo2(不用安裝任何控件):
MFC動(dòng)態(tài)繪制曲線圖-HightSpeedChart實(shí)現(xiàn)