您好,登錄后才能下訂單哦!
本文小編為大家詳細(xì)介紹“Qt中怎么使用PaintEvent繪制實(shí)時(shí)波形圖”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“Qt中怎么使用PaintEvent繪制實(shí)時(shí)波形圖”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識(shí)吧。
數(shù)據(jù)來源依舊是硬件傳入的實(shí)時(shí)數(shù)據(jù),如下:
[0, 3, 5, 8, 10, 13, 15, 18, 20, 23, 25, 28, 26, 23, 20, 16, 13, 11, 9, 6, 4, 3, 0]
其實(shí)有些人看到這里會(huì)說,數(shù)據(jù)都有了直接畫出來不就可以了嗎?
在我們實(shí)際應(yīng)用過程中,這些硬件上傳的數(shù)據(jù)不是一次性傳出的,而是取決于你操作硬件的頻率以及事件決定的。所以說,想要一次性拿出一整條數(shù)據(jù)來繪制,這個(gè)時(shí)機(jī)已經(jīng)晚了。
這里使用了SetRealTimeDepthData(stDepthData stData);
意思是:設(shè)置實(shí)時(shí)深度數(shù)據(jù)值。
參數(shù)穿入的是一個(gè)結(jié)構(gòu)體,在使用這個(gè)函數(shù)之前我已經(jīng)將數(shù)據(jù)做了簡(jiǎn)單的處理,包括了深度方向設(shè)置。
比如:當(dāng)深度逐漸變大時(shí),深度方向div是正數(shù),當(dāng)深度逐漸減小時(shí),深度方向div是負(fù)數(shù)。
下面,我展示下我實(shí)際處理后的數(shù)據(jù)值
對(duì)于這些真實(shí)數(shù)據(jù)我設(shè)定了一個(gè)結(jié)構(gòu)體,用于存儲(chǔ)數(shù)據(jù)時(shí)間、深度方向以及具體深度值
struct DrawingEffectivePress { int nDiv; //深度方向 int nDepth; //深度值 DWORD dwTime; //記錄當(dāng)前真實(shí)數(shù)據(jù)的時(shí)間 DrawingEffectivePress():nDiv(0),nDepth(0),dwTime(0){} }
SetRealTimeDepthData具體實(shí)現(xiàn),如下:
void QDrawingWaveform::SetRealTimeDepthData(stDepthData stData) { std::lock_guard<std::mutex> lck(m_dataMutex); //加鎖進(jìn)行數(shù)據(jù)操作 DrawingEffectivePress stDepth; stDepth.nDiv = stData.nDiv>=0?1:-1; stDepth.nDepth = stData.nDepth; stDepth.dwTime = GetTickCont(); m_vetDepth.push_back(stDepth); }
代碼講解:
有上述圖片的真實(shí)數(shù)據(jù)來看,深度的方位是逐漸遞增的,那么在程序中我們采用了1和-1的方式表示,正方向時(shí)都是用1來表示,負(fù)方向時(shí)都是用-1來表示。
每有一條真實(shí)數(shù)據(jù)時(shí),都需要記錄當(dāng)前真實(shí)數(shù)據(jù)的具體時(shí)間,用于繪制實(shí)時(shí)的動(dòng)態(tài)走向。
設(shè)定定時(shí)器每40毫秒刷新一次頁(yè)面:
#define TimeInterval 40 //定時(shí)器時(shí)間間隔
定時(shí)器啟動(dòng) m_nTimerId = startTimer(TimeInterval);
這是我們繪制的一個(gè)重點(diǎn),也是比較麻煩的一部分了。
與硬件打過交道的友友們都知道,硬件數(shù)據(jù)的不穩(wěn)定性,有些時(shí)候看著數(shù)據(jù)的走向是朝下的,因?yàn)槭謩?dòng)操作緣故,偶爾會(huì)有一些浮動(dòng)的數(shù)據(jù),這些數(shù)據(jù)需要篩除,在傳入數(shù)據(jù)之前我已經(jīng)做了處理,這個(gè)問題在這篇文章中是不存在的。
使用m_vetDepth存儲(chǔ)了實(shí)際的深度數(shù)據(jù)值。
std::vector<DrawingEffectivePress> m_vetDepth;
什么叫做超時(shí)顯示數(shù)據(jù)?
根據(jù)文章一開篇的動(dòng)畫可以看出,波形圖一邊進(jìn)行繪制操作,一邊向左移動(dòng)。
在移動(dòng)過程中,肯定會(huì)移出左邊界,那么也就代表了當(dāng)前的圖形不需要展示。對(duì)此,我們就需要在每次更新數(shù)據(jù)時(shí),判斷有哪些數(shù)據(jù)是已經(jīng)超過顯示范圍的,需要進(jìn)行剔除了。
那么,到這里也就遇到了另外一個(gè)問題,我們剔除的數(shù)據(jù)是在m_vetDepth中存儲(chǔ)的數(shù)據(jù)嗎?
答案是的,但是為了邏輯簡(jiǎn)單操作,我們需要重新定義一個(gè)結(jié)構(gòu)體,當(dāng)前結(jié)構(gòu)體主要用來做已經(jīng)繪制成圖形的點(diǎn)的記錄。
std::vector<DrawingEffectivePress> m_vetEffectiveDepth;
在當(dāng)前容器中存儲(chǔ)的數(shù)據(jù)一定是具體特定標(biāo)識(shí)的,也就是波形圖的拐點(diǎn)數(shù)據(jù),一個(gè)完整波形的最低點(diǎn)以及最高點(diǎn)。
當(dāng)我們進(jìn)行實(shí)際繪圖時(shí),也是取m_vetEffectiveDepth中的數(shù)據(jù),保證了數(shù)據(jù)邏輯簡(jiǎn)單性。
現(xiàn)在,我們先來看一看剔除超時(shí)數(shù)據(jù)的實(shí)際代碼,如下:
void QDrawingWaveform::DeletingTimeoutData() { DWORD dwCurrentTime = GetTickCount(); //當(dāng)前時(shí)間 std::vector<DrawingEffectivePress>::iterator itvet = m_vetEffectivePress.begin(); for (itvet; itvet != m_vetEffectivePress.end();) { DrawingEffectivePress stPoint = *itvet; if ((dwCurrentTime - stPoint.dwPressTime) > m_nSingShowTime) { //超過界面展示范圍,剔除數(shù)據(jù) itvet = m_vetEffectivePress.erase(itvet++); } else itvet++; } }
代碼解析:實(shí)時(shí)獲取最新時(shí)間,每次都判斷存儲(chǔ)的硬件操作時(shí)間與設(shè)定的最大值(m_nSingShowTime)進(jìn)行比較。
當(dāng)超過設(shè)定的時(shí)間時(shí),說明圖形已經(jīng)消失在界面上了,就需要剔除數(shù)據(jù)。
上一步驟是剔除超時(shí)數(shù)據(jù),那么我們?cè)撊绾未鎯?chǔ)這些有效數(shù)據(jù)到m_vetEffectivePress
容器中呢?
思路:
1:默認(rèn)容器中存儲(chǔ)前兩條數(shù)據(jù)。
2:當(dāng)后續(xù)數(shù)據(jù)來時(shí),需要與上一條數(shù)據(jù)進(jìn)行判別。
2.1:如果方向一致,說明深度一直在遞增或者是遞減,直接替換最后一會(huì)有效數(shù)據(jù)的值即可。
2.2:如果方向不一致,說明深度值發(fā)生了變換,可能由向下變成了向上;也可能由向上變成了向下。不再做數(shù)據(jù)替換操作,而是插入一條新數(shù)據(jù)。
3:操作一條數(shù)據(jù)后,進(jìn)行數(shù)據(jù)刪除。
將上述思路轉(zhuǎn)變成代碼,如下:
std::vector<DrawingEffectivePress>::iterator itvet = m_vetPress.begin() for (itvet; itvet != m_vetPress.end(); ) { DrawingEffectivePress stPoint = *itvet; DrawingEffectivePress stPress; stPress.dwTime = stPoint.dwTime; stPress.nDiv = stPoint.nDiv; stPress.nDepth = stPoint.nDepth; int nsize = m_vetEffectivePress.size(); if (nsize < 2) { m_vetEffectivePress.push_back(stPress); } else { //如果當(dāng)前數(shù)據(jù)stPoint與m_vetEffectivePress的最后一位的拐點(diǎn)一致, //剔除掉m_vetEffectivePress的最后一位,存儲(chǔ)成最新數(shù)據(jù) if (m_vetEffectivePress[nsize - 1].nDiv == stPoint.nDiv) { //更新m_vetEffectivePress的最后一位 m_vetEffectivePress[nsize - 1] = stPress; } else { //存儲(chǔ)的最后一位的拐點(diǎn)與當(dāng)前數(shù)值不一致時(shí),直接存儲(chǔ) m_vetEffectivePress.push_back(stPress); } } itvet = m_vetPress.erase(itvet++); }
經(jīng)過上述數(shù)據(jù)處理后,我們可以直接在panitEvent中直接繪制出m_vetEffectivePress圖形了。
實(shí)際代碼效果如下:
QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); //抗鋸齒 QPolygon polygon; for (int i = 0; i < m_vetEffectivePress.size(); i++) { DrawingEffectivePress stPoint = m_vetEffectivePress[i]; int nX = this->CalcRealPointX(stPoint.dwPressTime); int nY = this->CalcRealPointY(stPoint.nDepth); polygon << QPoint(nX, nY); } painter.drawPolyline(polygon);
代碼解析:根據(jù)實(shí)際數(shù)據(jù)的操作時(shí)間以及具體的深度值,可以確定波形圖的x軸與y軸了。
根據(jù)時(shí)間的變化,就可以讓圖形看起來是一種動(dòng)起來的效果。
CalcRealPointX
實(shí)際處理
//TODO:計(jì)算,實(shí)際的x軸坐標(biāo) DWORD dwCurrent = GetTickCount(); int nRectRight = rect().right(); int nMoveOriginal = dwDepthTime - dwCurrent; double dMoveLen = nMoveOriginal / 8; int nX = nRectRight + dMoveLen; return nX;
代碼解析:實(shí)時(shí)獲取當(dāng)前繪制時(shí)間,并減去結(jié)構(gòu)體中存儲(chǔ)的深度時(shí)間,設(shè)置移動(dòng)長(zhǎng)度(dwMoveOriginal)。
因?yàn)橐蜃笃?,所以,每次用窗口的右?cè)區(qū)域減去就可以了。
注意:我這里使用的是"+",因?yàn)槲矣?jì)算得出的偏移長(zhǎng)度一定是一個(gè)負(fù)值。實(shí)時(shí)時(shí)間一定會(huì)比實(shí)際深度時(shí)間大,所以結(jié)果肯定是一個(gè)負(fù)值。
到這里,我們的實(shí)時(shí)圖形繪制算是完成了80%了,想要繪制出連續(xù)的實(shí)時(shí)深度值圖形就可以實(shí)現(xiàn)了。
為什么說是80%呢?
因?yàn)檫€有一個(gè)我們需要考慮的問題,當(dāng)不是連續(xù)數(shù)據(jù)獲取時(shí),使用QPolygon繪制圖形時(shí),就會(huì)出現(xiàn)以下效果:
當(dāng)我們不是連續(xù)繪制深度值時(shí),間隔一定時(shí)間后,再進(jìn)行繪圖時(shí),就會(huì)出現(xiàn)上述紅色區(qū)域框出來的詭異現(xiàn)象。
按照實(shí)際應(yīng)用的繪制效果就應(yīng)該如同文章剛開始的效果一樣,間隔一定時(shí)間后,再次繪制,應(yīng)該還是一條完整的波形數(shù)據(jù)。
QPolygon這個(gè)繪制類顯然使用上述代碼是不支持的,即使我們換成了DrawLine的方式,這個(gè)問題還是需要被解決的。
此時(shí),我們就需要做一個(gè)特殊處理,當(dāng)我們沒有實(shí)際深度值數(shù)據(jù)時(shí),需要實(shí)時(shí)知道當(dāng)前的繪制點(diǎn)在哪個(gè)位置,也就是說,在沒有真實(shí)數(shù)據(jù)來臨之前,我們需要每間隔一個(gè)繪圖數(shù)據(jù)刷新時(shí)間,需要繪制一條直線,而不是直接從上一個(gè)繪制點(diǎn)直接繪制波形圖。
讀到這里,這篇“Qt中怎么使用PaintEvent繪制實(shí)時(shí)波形圖”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。