實例操作 Qt 多線程 后臺創(chuàng)建縮略圖
本文介紹的是實例操作 Qt 多線程 后臺創(chuàng)建縮略圖,不多說,更多資料參考本文末尾,先來看本文內(nèi)容。
起因是在qtcn上看到有人說創(chuàng)建縮略圖的API執(zhí)行很慢,并且阻塞了gui的正常運轉(zhuǎn),需要改成多線程來實現(xiàn),但他不清楚如何實現(xiàn),希望能有個例子。 這位同學的問題揭示了Qt里最需要用到多線程的一個用例,那就是防止一切阻塞GUI線程的操作。 Qt GUI編程有個最基本但很多人不熟悉的原則是所有的操作都不能是阻塞的, 同時所有的操作都不能占用很多CPU, 這個原則是針對GUI線程來說的, 但后者對子線程也同樣適用(這一點我們后邊繼續(xù)八)。
熟悉Qt的人都應該有所了解,Qt的圖形處理是基于事件循環(huán)的,這一點和大多數(shù)的GUI工具庫一致。事件循環(huán)簡單來說就是個死循環(huán),里面讀取系統(tǒng)外設的事件,然后見招拆招。 Qt里的Events, Signals,Timer都在事件循環(huán)里分發(fā),所以,如果阻塞了事件循環(huán)用腳趾頭想想也能知道會有多么嚴重的后果,最明顯的就是界面不刷新,也不能和用戶交互,所以這就要求我們在寫程序的時候不管是事件處理函數(shù)還是槽函數(shù)里都不能進行占用CPU時間的操作,特別要杜絕阻塞性的操作如串口數(shù)據(jù)讀取、杜絕長時間的高CPU占用率的操作如死循環(huán)和大數(shù)據(jù)量的計算等。 However,“人在河邊走, 哪能不濕鞋”呢, 總有需要用到這些操作的時候,Qt也提供了解決方案,那就是多線程。
Qt的多線程類有很多,改天開帖詳細介紹,這里只八最核心的QThread類。 該類的用法是Posix多線程的用法的簡化版, 只需要派生一個QThread的子類,實現(xiàn)其中的run虛函數(shù)就大功告成, 用的時候創(chuàng)建該類的實例,調(diào)用它的start方法,理論上非常簡單。 筆者個人覺得寫多線程的程序首先要去惡補一下多線程的知識, Qt說到底只是一個工具,它的API包裝得再好也得用的人懂得如何去使用才能發(fā)揮最好的作用。 Qt的文檔中關于多線程的編程文章挺多, 另外還列出了一系列的推薦讀物, 想把多線程程序?qū)懞玫娜艘欢ㄒ础?/p>
前面啰嗦了這么多無非是想告訴大家,寫多線程的程序單靠Qt的知識是不夠的(更別說有些人還沒用過Qt呢),多線程的知識更加重要。 而且單靠一兩篇簡單的文章是學不明白的,你看人家多線程編程能寫一大章書呢!好了,強調(diào)一下背景知識的重要性,下面要提供一個最簡單的Qt多線程的例子程序,它的功能是打開多個圖片文件之后創(chuàng)建線程執(zhí)行圖片縮放的操作,創(chuàng)建圖片的縮略圖,完成后子線程會把縮略圖傳給GUI線程,由GUI線程創(chuàng)建Label控件來顯示縮略圖。各位看官注意了,這個例子很簡單,和多線程相關的部分其實很少,沒有用到Mutex也沒涉及并發(fā)等等概念, 單一的演示如何防止大數(shù)據(jù)量的計算阻塞GUI線程。
例子的源碼見附件,這里先列出一些關鍵的流程和代碼, 方便大家理解:
第一步:打開圖片文件
用QFileDialog::getOpenFileNames獲得圖片文件名的StringList
第二步:遍歷List創(chuàng)建線程
- void MainWin::createThumbnail(const QString& filename)
- {
- QThread* thread = new ThumbnailThread(filename, 10 - waitseconds);
- connect(thread, SIGNAL(thumbnailFinished(QImage)), this, SLOT(addThumbnail(QImage)));
- connect(thread, SIGNAL(thumbnailFailed(const QString)), this, SLOT(showError(const QString)));
- connect(thread, SIGNAL(finished()), this, SLOT(deleteThread()));
- thread->start();
- }
finished是QThread類自帶的信號,在線程結(jié)束執(zhí)行時發(fā)出; 其他兩個信號是自定義信號, 用于在GUI線程和子線程之間傳遞數(shù)據(jù)。
第三步:子線程內(nèi)創(chuàng)建縮略圖
使用了QImage的scaled方法做圖片縮放。
- void ThumbnailThread::run()
- {
- if( bigpm.isNull())
- {
- emit thumbnailFailed(pmfilename);
- }
- else
- {
- smallpm = bigpm.scaled(TN_WIDTH, TN_HEIGHT, Qt::KeepAspectRatio);
- emit thumbnailFinished(smallpm);
- }
- }
第四步:GUI線程處理thumbnailFinished信號
為每個圖片創(chuàng)建一個Label, 將之加到預先定義好的GridLayout中
- void MainWin::addThumbnail(QImage smallpm)
- {
- static int i = 0;
- static int j = 0;
- QLabel* label = new QLabel;
- label->setPixmap(QPixmap::fromImage(smallpm));
- QGridLayout* gl = qobject_cast(previewwidget->layout());
- gl->addWidget(label, j, i);
- label->show();
- qWarning() << "Label:" <isVisible();
- i++;
- if( i > previewwidget->width() / smallpm.width())
- {
- i = 0;
- j ++;
- }
- }
關鍵點提示:
Qt的signal可跨線程傳遞, 但要注意slot執(zhí)行的線程。 connect時可以在第五個參數(shù)的位置指定連接的屬性(這里用了默認值), 如DirectConnection表示在connect函數(shù)執(zhí)行的線程里執(zhí)行slot, QueuedConnection表示slot執(zhí)行在接受者所在的線程,(還有其他選項參看文檔)本例中的slot都執(zhí)行在GUI線程中。
在收到finished信號后要清除執(zhí)行完畢的線程, 為了防止刪除后對線程實例的訪問這里用了個deleteLater方法 — 在大家都不再訪問thread實例時再刪除。
Qt中所有和GUI相關的操作都要放在GUI線程里執(zhí)行,包括所有Widget的創(chuàng)建和訪問,QPixmap的類也算GUI的類, 所以本例中只能用QImage來處理圖片。
這個例子其實用一個子線程就夠了,給每張圖片創(chuàng)建一個線程有點浪費。例子里有些為了測試需要加的代碼可能會影響閱讀(比如那個waitseconds),請大家自動忽略。 比如為了更好的看到效果,給線程里加了個sleep延緩處理圖片的速度…
原文鏈接:http://www.cuteqt.com/blog/?p=547
小結(jié):關于實例操作 Qt 多線程 后臺創(chuàng)建縮略圖 的內(nèi)容介紹完了,希望本文對你有所幫助!更多關于多線程的內(nèi)容請參考編輯推薦。