標籤:

Qt 重繪問題?

需要使用Qt來繪製一個實時曲線(類似於用matlab訓練神經網路產生的動態曲線,橫軸是迭代次數,縱軸是精度),我的思路是:使用一個成員變數vector&,每次迭代訓練得到的精度都push到這個vector中(下面的代碼用產生的隨機數演示),然後立即update重繪。也就是說我目前有2個點就繪製2個點的曲線圖,有3個點就繪製3個點的曲線圖。

可是現在產生的問題是,我每次得到一個精度並push到vector中,然後立即update,這個update根本沒有執行(我嘗試用repaint,也沒有效果)。最後的效果就是,所有的精度都push到vector後,才會調用paintEvent。

求大家幫忙看下原因,代碼如下:

drawer.h中有個成員變數 vector& vecdata,以下是drawer.cpp的代碼:

void Form::curveProcess() {
for (int i = 0; i &< 200; i++) { cout &<&< "haha" &<&< i &<&< endl; vecdata.push_back(rand()/(float)(RAND_MAX/100)); // 重繪無效,求解釋... update(); } } void Form::paintEvent(QPaintEvent *event) { // 省略繪製坐標軸的代碼.. // 以下是根據 vector&中的精度來繪製的曲線
// 繪製曲線
painter.setPen(Qt::red);
int datasize = vecdata.size();

for (int i = 0; i &< datasize - 1; i++) { painter.drawLine(width / (datasize - 1) * i, -height / 100 * vecdata[i], width / (datasize - 1) * (i + 1), -height / 100 * vecdata[i + 1]); } }

我在另外一個類中設置了點擊某個button,就會調用:

drawer *ff = new drawer();
ff-&>show();
ff-&>curveProcess();

最後的效果圖是200個精度的點一下子就出來了:

過程中並沒有執行update,求解釋..

有什麼辦法可以執行到這個update么?


首先啊,你說「我在另外一個類中設置了點擊某個button,就會調用」,那麼說明之後的3行代碼是在「點擊 button」事件的slot里。而這個slot又調用了 curveProcess , curveProcess 又開啟了一個 for 循環。整個過程都在這個事件里。Qt 是單線程的,在「點擊 button」事件處理結束之前,所有的"update"事件(也就是paintEvent)都處於排隊中的狀態。當整個 for 循環執行完畢之後,「點擊 button」事件執行完畢退出,才開始處理「update」事件。

正確的做法是在 for 循環裡面,每次 update 之後都把執行還給 Qt 主線程,讓它有機會處理排隊中的事件。


這個和窗口繪製的原理有關,需要從頭說起。

1、最基本的原理是:繪製是在主線程中完成的。主線程是什麼,可以理解為就是main()函數。你寫qt程序肯定知道一個基本用法,main()函數最後需要調用QApplication的exec()函數,這個exec()裡面是個死循環,每次循環調用一次QApplication的processEvents(),這個函數負責處理窗口事件、用戶消息,然後繪製窗口。就是說所有這些事都是在主線程中依次完成的,並不是同時進行,所以如果你在主線程中處理數據(比如響應按鈕事件),你這個事件處理函數不返回,就不會往下走到繪製這一步。

2、update和repaint。可能你已經知道,窗口系統為了提高性能,會盡量減少繪製次數和範圍,所以默認情況下,只有窗口內容顯示出來的時候才進行一次繪製,包括窗口被遮擋的部分重新顯現。update和repaint就是主動通知系統繪製窗口內容。區別在於,update只是在內部數據中做一個標記,要等到主線程進行到下一次繪製時才進行繪製,所以多次調用update是沒有意義的,只是重複設置繪製標記,並不會馬上進行繪製,而且同樣要等到你的代碼走完之後走到繪製時才能看到結果。repaint雖然可以立即調用到paintEvent,但是光有這個paintEvent還不夠,不跑完整個processEvents,缺少了界面響應,你的窗口就是個死的,看上去仍然不是實時顯示。

解決方法①(臨時): 在每次update之後都調用一次qApp-&>processEvents()。這種方法的問題在於,processEvents裡面除了繪製還有事件處理等很多內容,提前調用會打亂計劃好的處理順序,這在事件處理複雜的程序中容易導致bug,要小心使用。而且不能控制動畫的速度,因為你不能在主線程裡面sleep,那樣會卡死整個界面。

解決方法②(推薦):拆分for循環,放到timer處理函數裡面,timer設置一定的間隔,每隔一段時間調用這個timer函數,函數push一次然後調用update,就可以看到變化過程了。如果把間隔設為0,理論上效果和方法①一樣。(請查timerEvent的用法)

解決方法③(較麻煩,執行效率低):在子線程裡面處理數據,通過信號調用主線程的update。如果數據處理時間太長的話就只能用這種辦法了。

具體代碼就不寫了。


Qt自帶的實例里就有一個用Qt Quick和OpenGL繪製曲線的例子,你可以參考


隨便百度下都有查得到,用update肯定是會出現你那種情況啦。update又不是立即重繪。

不是沒執行update,是執行了update但是沒有調用paintEvent而已。

用repaint沒有調用paintEvent才比較值得研究,現有這些信息我是看不出來為什麼不行。


我最近也遇到了,update為什麼不行,其他人說了,我就說我怎麼實現顯示出繪製這個過程的吧。使用了一個QImage,paintevent中去drawImage這個QImage對象,所有的繪製都都是對QImage進行操作,然後調用update。手機碼的,代碼回頭貼。

更新一下:

課堂作業: HeartUnchange/DLA_model · GitHub

(:好吧,我承認c++沒學好,,

你要的繪製部分在DLA_model/widget.h at master · HeartUnchange/DLA_model · GitHub

的這個class中,

class nn : public QWidget

精簡版?再次更新時發,我先去交作業.


推薦閱讀:

如何評價基於 C++ 17 的框架 MCF?
protobuf 變長64位無符號整數 為什麼最多需要消耗10位元組而不是9位元組?
C++ 為什麼 Lambda 的引用捕獲 const 變數會失敗?
C++ type_traits里哪些東西通過標準語言沒有可能實現而必須尋求編譯器內建支持?
看完 C++ 課本能否直接上手 Qt?

TAG:QtC開發框架 | C |