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