<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
上一篇文章講述瞭如何使用控制元件進行波形圖繪製,雖然很方便,但是也有一些無法避免的問題,比如說:動態繪製圖形時,想要流暢的進行波動,就必須按照特定的時間實時更換資料。
接來下,我們採用在paintEvent中繪製的方式進行實時波形圖繪製,首先,我們先展示下顯示效果吧!
資料來源依舊是硬體傳入的實時資料,如下:
[0, 3, 5, 8, 10, 13, 15, 18, 20, 23, 25, 28, 26, 23, 20, 16, 13, 11, 9, 6, 4, 3, 0]
其實有些人看到這裡會說,資料都有了直接畫出來不就可以了嗎?
在我們實際應用過程中,這些硬體上傳的資料不是一次性傳出的,而是取決於你操作硬體的頻率以及事件決定的。所以說,想要一次性拿出一整條資料來繪製,這個時機已經晚了。
那麼,我們該如何使用paintEvent實時繪製出波形圖呢?接下來就來講解下我的思路吧,如果覺得我的思路比較繁瑣,大家也可以提出來,我也學習下,彌補下自己的不足。
這裡使用了SetRealTimeDepthData(stDepthData stData);
意思是:設定實時深度資料值。
引數穿入的是一個結構體,在使用這個函數之前我已經將資料做了簡單的處理,包括了深度方向設定。
比如:當深度逐漸變大時,深度方向div是正數,當深度逐漸減小時,深度方向div是負數。
下面,我展示下我實際處理後的資料值
對於這些真實資料我設定了一個結構體,用於儲存資料時間、深度方向以及具體深度值
struct DrawingEffectivePress { int nDiv; //深度方向 int nDepth; //深度值 DWORD dwTime; //記錄當前真實資料的時間 DrawingEffectivePress():nDiv(0),nDepth(0),dwTime(0){} }
SetRealTimeDepthData具體實現,如下:
void QDrawingWaveform::SetRealTimeDepthData(stDepthData stData) { std::lock_guard<std::mutex> lck(m_dataMutex); //加鎖進行資料操作 DrawingEffectivePress stDepth; stDepth.nDiv = stData.nDiv>=0?1:-1; stDepth.nDepth = stData.nDepth; stDepth.dwTime = GetTickCont(); m_vetDepth.push_back(stDepth); }
程式碼講解:
有上述圖片的真實資料來看,深度的方位是逐漸遞增的,那麼在程式中我們採用了1和-1的方式表示,正方向時都是用1來表示,負方向時都是用-1來表示。
每有一條真實資料時,都需要記錄當前真實資料的具體時間,用於繪製實時的動態走向。
設定定時器每40毫秒重新整理一次頁面:
#define TimeInterval 40 //定時器時間間隔
定時器啟動 m_nTimerId = startTimer(TimeInterval);
這是我們繪製的一個重點,也是比較麻煩的一部分了。
與硬體打過交道的友友們都知道,硬體資料的不穩定性,有些時候看著資料的走向是朝下的,因為手動操作緣故,偶爾會有一些浮動的資料,這些資料需要篩除,在傳入資料之前我已經做了處理,這個問題在這篇文章中是不存在的。
使用m_vetDepth儲存了實際的深度資料值。
std::vector<DrawingEffectivePress> m_vetDepth;
什麼叫做超時顯示資料?
根據文章一開篇的動畫可以看出,波形圖一邊進行繪製操作,一邊向左移動。
在移動過程中,肯定會移出左邊界,那麼也就代表了當前的圖形不需要展示。對此,我們就需要在每次更新資料時,判斷有哪些資料是已經超過顯示範圍的,需要進行剔除了。
那麼,到這裡也就遇到了另外一個問題,我們剔除的資料是在m_vetDepth中儲存的資料嗎?
答案是的,但是為了邏輯簡單操作,我們需要重新定義一個結構體,當前結構體主要用來做已經繪製成圖形的點的記錄。
std::vector<DrawingEffectivePress> m_vetEffectiveDepth;
在當前容器中儲存的資料一定是具體特定標識的,也就是波形圖的拐點資料,一個完整波形的最低點以及最高點。
當我們進行實際繪圖時,也是取m_vetEffectiveDepth中的資料,保證了資料邏輯簡單性。
現在,我們先來看一看剔除超時資料的實際程式碼,如下:
void QDrawingWaveform::DeletingTimeoutData() { DWORD dwCurrentTime = GetTickCount(); //當前時間 std::vector<DrawingEffectivePress>::iterator itvet = m_vetEffectivePress.begin(); for (itvet; itvet != m_vetEffectivePress.end();) { DrawingEffectivePress stPoint = *itvet; if ((dwCurrentTime - stPoint.dwPressTime) > m_nSingShowTime) { //超過介面展示範圍,剔除資料 itvet = m_vetEffectivePress.erase(itvet++); } else itvet++; } }
程式碼解析:實時獲取最新時間,每次都判斷儲存的硬體操作時間與設定的最大值(m_nSingShowTime)進行比較。
當超過設定的時間時,說明圖形已經消失在介面上了,就需要剔除資料。
上一步驟是剔除超時資料,那麼我們該如何儲存這些有效資料到m_vetEffectivePress
容器中呢?
思路:
1:預設容器中儲存前兩條資料。
2:當後續資料來時,需要與上一條資料進行判別。
2.1:如果方向一致,說明深度一直在遞增或者是遞減,直接替換最後一會有效資料的值即可。
2.2:如果方向不一致,說明深度值發生了變換,可能由向下變成了向上;也可能由向上變成了向下。不再做資料替換操作,而是插入一條新資料。
3:操作一條資料後,進行資料刪除。
將上述思路轉變成程式碼,如下:
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 { //如果當前資料stPoint與m_vetEffectivePress的最後一位的拐點一致, //剔除掉m_vetEffectivePress的最後一位,儲存成最新資料 if (m_vetEffectivePress[nsize - 1].nDiv == stPoint.nDiv) { //更新m_vetEffectivePress的最後一位 m_vetEffectivePress[nsize - 1] = stPress; } else { //儲存的最後一位的拐點與當前數值不一致時,直接儲存 m_vetEffectivePress.push_back(stPress); } } itvet = m_vetPress.erase(itvet++); }
經過上述資料處理後,我們可以直接在panitEvent中直接繪製出m_vetEffectivePress圖形了。
實際程式碼效果如下:
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);
程式碼解析:根據實際資料的操作時間以及具體的深度值,可以確定波形圖的x軸與y軸了。
根據時間的變化,就可以讓圖形看起來是一種動起來的效果。
CalcRealPointX
實際處理
//TODO:計算,實際的x軸座標 DWORD dwCurrent = GetTickCount(); int nRectRight = rect().right(); int nMoveOriginal = dwDepthTime - dwCurrent; double dMoveLen = nMoveOriginal / 8; int nX = nRectRight + dMoveLen; return nX;
程式碼解析:實時獲取當前繪製時間,並減去結構體中儲存的深度時間,設定移動長度(dwMoveOriginal)。
因為要向左偏移,所以,每次用視窗的右側區域減去就可以了。
注意:我這裡使用的是"+",因為我計算得出的偏移長度一定是一個負值。實時時間一定會比實際深度時間大,所以結果肯定是一個負值。
到這裡,我們的實時圖形繪製算是完成了80%了,想要繪製出連續的實時深度值圖形就可以實現了。
為什麼說是80%呢?
因為還有一個我們需要考慮的問題,當不是連續資料獲取時,使用QPolygon繪製圖形時,就會出現以下效果:
當我們不是連續繪製深度值時,間隔一定時間後,再進行繪圖時,就會出現上述紅色區域框出來的詭異現象。
按照實際應用的繪製效果就應該如同文章剛開始的效果一樣,間隔一定時間後,再次繪製,應該還是一條完整的波形資料。
QPolygon這個繪製類顯然使用上述程式碼是不支援的,即使我們換成了DrawLine的方式,這個問題還是需要被解決的。
此時,我們就需要做一個特殊處理,當我們沒有實際深度值資料時,需要實時知道當前的繪製點在哪個位置,也就是說,在沒有真實資料來臨之前,我們需要每間隔一個繪圖資料重新整理時間,需要繪製一條直線,而不是直接從上一個繪製點直接繪製波形圖。
到此這篇關於Qt中PaintEvent繪製實時波形圖的實現範例的文章就介紹到這了,更多相關Qt PaintEvent繪製實時波形圖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45