中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Qt中怎么使用PaintEvent繪制實時波形圖

發布時間:2022-06-07 09:39:49 來源:億速云 閱讀:822 作者:zzz 欄目:開發技術

本文小編為大家詳細介紹“Qt中怎么使用PaintEvent繪制實時波形圖”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Qt中怎么使用PaintEvent繪制實時波形圖”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

數據來源依舊是硬件傳入的實時數據,如下:

[0, 3, 5, 8, 10, 13, 15, 18, 20, 23, 25, 28, 26, 23, 20, 16, 13, 11, 9, 6, 4, 3, 0]

其實有些人看到這里會說,數據都有了直接畫出來不就可以了嗎?

在我們實際應用過程中,這些硬件上傳的數據不是一次性傳出的,而是取決于你操作硬件的頻率以及事件決定的。所以說,想要一次性拿出一整條數據來繪制,這個時機已經晚了。

繪制思路

1:接收硬件傳入的數據

這里使用了SetRealTimeDepthData(stDepthData stData);意思是:設置實時深度數據值。

參數穿入的是一個結構體,在使用這個函數之前我已經將數據做了簡單的處理,包括了深度方向設置。

比如:當深度逐漸變大時,深度方向div是正數,當深度逐漸減小時,深度方向div是負數。

下面,我展示下我實際處理后的數據值

Qt中怎么使用PaintEvent繪制實時波形圖

對于這些真實數據我設定了一個結構體,用于存儲數據時間、深度方向以及具體深度值

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來表示。

每有一條真實數據時,都需要記錄當前真實數據的具體時間,用于繪制實時的動態走向。

2:定時器動態刷新頁面

設定定時器每40毫秒刷新一次頁面:

#define TimeInterval 40 //定時器時間間隔

定時器啟動 m_nTimerId = startTimer(TimeInterval);

3:真實數據處理

這是我們繪制的一個重點,也是比較麻煩的一部分了。

與硬件打過交道的友友們都知道,硬件數據的不穩定性,有些時候看著數據的走向是朝下的,因為手動操作緣故,偶爾會有一些浮動的數據,這些數據需要篩除,在傳入數據之前我已經做了處理,這個問題在這篇文章中是不存在的。

使用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++);
}

4:圖形繪制

經過上述數據處理后,我們可以直接在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繪制圖形時,就會出現以下效果:

Qt中怎么使用PaintEvent繪制實時波形圖

當我們不是連續繪制深度值時,間隔一定時間后,再進行繪圖時,就會出現上述紅色區域框出來的詭異現象。

按照實際應用的繪制效果就應該如同文章剛開始的效果一樣,間隔一定時間后,再次繪制,應該還是一條完整的波形數據。

QPolygon這個繪制類顯然使用上述代碼是不支持的,即使我們換成了DrawLine的方式,這個問題還是需要被解決的。

此時,我們就需要做一個特殊處理,當我們沒有實際深度值數據時,需要實時知道當前的繪制點在哪個位置,也就是說,在沒有真實數據來臨之前,我們需要每間隔一個繪圖數據刷新時間,需要繪制一條直線,而不是直接從上一個繪制點直接繪制波形圖。

讀到這里,這篇“Qt中怎么使用PaintEvent繪制實時波形圖”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

浮梁县| 闽清县| 朝阳区| 凤庆县| 承德县| 商城县| 普定县| 安平县| 盐城市| 云霄县| 巴中市| 霸州市| 开远市| 永顺县| 英山县| 新宾| 陇西县| 秭归县| 金阳县| 施秉县| 华容县| 巍山| 蒲江县| 鄂伦春自治旗| 长宁区| 台中县| 巧家县| 论坛| 南澳县| 武功县| 和田县| 张家川| 如皋市| 横峰县| 大邑县| 韶山市| 崇仁县| 灌云县| 平湖市| 翼城县| 环江|