保持Qt GUI響應的幾種方法

来源:http://www.cnblogs.com/sfy5848/archive/2016/11/14/6061881.html
-Advertisement-
Play Games

QThread Qt Gui frozen 未響應 阻塞 processEvents ...


最開始使用Qt時就遇到過QT Gui失去響應的問題,我是用多線程的方式解決的,然而通常來說,多線程是會降低程式的運行速度。

之後,在使用QSqlQuery::execBatch()函數時,Qt Gui 又失去響應,雖然多線程可以解決,但是如果能用單線程很好解決的,最好不要用到多線程,因為多線程不僅容易拖慢程式的速度,編程及維護的難度也更大,能用簡單方法解決的,就不要用複雜的方法。

於是我再次搜索資料,期望在解決方案的選擇與解決步驟上,能夠得到一個全面而又細緻的總結。


Witold Wysota 的文章https://doc.qt.io/archives/qq/qq27-responsive-guis.html#performinglongoperations 總結的非常不錯。

Jason Lee的翻譯: http://blog.csdn.net/jasonblog/article/details/5568589

所以本文是在此文基礎上的部分翻譯、理解與二次總結。總之,有刪減,有補充,所以沒寫 '轉' 字。


一、問題的來源與分析

     首先,我們要知道 “為什麼Qt Gui 會停止響應?”。簡明扼要的說就是:長時間的密集處理或等待阻塞了Qt的事件迴圈,應用程式不能響應來自視窗系統的事件請求(《C++ Gui Qt4》 P135中有描述)。   那麼多長算長呢?一秒鐘算長,兩秒鐘太長。

     其次,“ 何種情形下會發生該問題? ”。可分為兩種情形:

     第一,長時間按順序執行的密集運算,全部計算結束後才能繼續執行,如快速傅立葉變換。

     第二,“ 觸發 ”了某項操作,該操作完成後才能進行“ 下一步 ”, 所以這裡描述的是非同步操作,如保存文件操作,伺服器等待連接、網路下載等。詳細見附註(1).

     私以為兩種情形並無明顯的概念上的區分,本質是一樣的,但兩種情形有不同的處理方法,特別是第二種情形, 在Qt框架下 ,用Qt的信號和槽機制往往可以解決阻塞問題,如QTcpServer::newConnection信號通知連接的到來,QIODevice::bytesWritten()與 QIODevice::readyRead()通知文件的讀寫,它們都是以非阻塞的形式實現相關功能的利器。 而第一種情形,不僅所有的事件迴圈停止了,信號和槽也暫時被忽視。我們將針對以上兩種情形尋找解決方案。

     最後,我們考慮是否可以把這個造成 Qt Gui 停止響應的罪魁禍首大卸八塊,即把他拆分成一個個小塊,如果可以拆分,那麼每塊之間是依賴還是獨立,如果獨立那問題好辦,放在不同的位置獨立運作,否則,我們只能同步的執行,而最差的結果是——根本無法拆分!!

     總之,考慮以上信息差異,執行不同的解決方案。


二、解決方案

Manual event processing(人工執行事件)

     保持事件迴圈有一種最基本的方法——讓程式去處理 懸掛事件好了 ,處理完了再回來繼續我的後續運算,要做到這一點,就要在我的運算代碼中間加上處理事件的代碼,這句代碼就是 QCoreApplication::processEvents();,只要該句代碼能夠周期性的被執行,就能保持Qt Gui的響應。

//代碼來源於上述鏈接所指向文章

for (int i = 3; i <= sqrt(x) && isPrime; i += 2) {
        label->setText(tr("Checking %1...").arg(i));
        if (x % i == 0)
            isPrime = false;
        QCoreApplication::processEvents();
        if (!pushButton->isChecked()) {
            label->setText(tr("Aborted"));
            return;
        } 

    }

部分翻譯(略)——可查看 Jason Lee的翻譯

     該方案除了 具有Witold Wysota文中 所提到缺點之外,《C++ Gui Qt4》P135中還提到,用戶可能會在應用程式還在執行某種操作時,或者關閉了主視窗,或者通過界面再次觸發相同操作,這樣就會產生不可預料的後果,如 一個保存文件對話框,用戶單擊save按鈕後,程式開始磁碟文件的寫入操作,該操作還未完成時,用戶再次單擊了關閉按鈕,或者再次單擊save按鈕。書中給出的解決辦法是將 qApp->processEvents()替換為qApp-> processEvents(QEventLoop::ExcludeUserInputEvents),以告訴Qt忽略滑鼠事件和鍵盤事件。

 

Using a Worker Thread(使用任務線程)

     翻譯(略)—— Jason Lee的翻譯

除了 Witold Wysota 文中所說的重新實現QThread類之外,還可以使用QObject::moveToThread(QThread *thread)函數,將

進行複雜運算的對象移入子線程中運行,前提是子線程不能夠有父對象,否則無法移入子線程。示例如下:

QThread *thread = new QThread(this); 
MyComputation *computation = new  MyComputation();//負責密集運算的對象 
computation->moveToThread(thread); 
connect(thread, SIGNAL(started()), computation ,SLOT( compute()) ); //compute()為computation的運算函數 
thread->start();

需註意的是,將 computation 對象移入子線程後, 依舊不可直接調用  computation 對象 compute()函,應該調用線程對象的start()函數,發出started()信號觸發 computation 對象的運算操作,否則依舊會阻塞主線程。

 

Waiting in a Local Event Loop(在本地事件迴圈中等待)

翻譯(略)—— Jason Lee的翻譯

註解:如文章開頭所說,“等待非同步事件完成”,也就是說這種方法是針對非同步事件而設計的,非同步事件執行過程中會不斷發送信號,我們根據該信號決定程式接下來的行為,包括人工執行事件。而  Manual event processing 適用於順序執行的操作 。

 

Solving a Problem Step by Step(分步驟解決問題)

翻譯(略)

如前文所說,如果一個複雜操作可以拆分為獨立的子操作,那麼拆分應該是最好的解決辦法。至於如何拆分,可以通過閱讀《重構》這本書來學習。

 

Parallel Programming
翻譯(略)


三、總結

     前面我提到過,我是用queryBatch()函數導致了Qt Gui無法響應的,最後我選擇了 Using a Worker Thread這種方法。queryBatch()是一個操作資料庫的批處理函數,非常便利,但它是順序執行的,我無法用非同步方式來處理它,函數內部是不可見的,也無法人工執行事件或者拆分,最後只能使用子線程來執行它了,這就是為便利所付出的代價吧。不同情況有不同的解決方案,認清自己的問題很重要。


四、附註

(1) “ 觸發 ”了某項操作,該操作完成後才能進行“ 下一步 ”,但實際上對於“觸發”這個行為本身而言,它的職責已經完成了,而

“下一步”指的是某個功能執行中的“下一步”,當然也可能是編程語境下的“ 下一步 ”,即下一條語句, 所以這裡描述的是非同步操作,如保存文件操作,伺服器等待連接、網路下載等。

     舉個慄子, 創建了QTcpServer對象並調用listen() 函數監聽連接,這時如果調用QTcpServer::waitForNewConnection(...)函數,就會阻塞程式的運行,直到連接到來函數才能返回,進而執行下一句。這裡,listen()函數“ 觸發 ”了監聽行為, “ 下一步 ”與網路連接相關的動作要等連接到來後才能執行,而 QTcpServer::waitForNewConnection(...)則作為我們是否執行下一步的判斷標準,只不過這裡用的是阻塞的方式;對於非同步操作,也可以用非阻塞的方式解決,Qt的信號和槽機制就能很好的解決,如我們可以接收newConnection()信號判斷連接是否到來,而不必將程式阻塞在那。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 解釋:首先瀏覽器發送一個請求、給/StrutsPrepareAndExecuteFilter調用doFilter()方法、創建/StrutsActionProxy執行execute()方法、有一個引用/DefaultActionInvocation調invoke()方法、接著去調用其他攔截器的方法、 ...
  • 在討論這個問題之前,我們需要先區分unicode和UTF。 1. unicode :統一的字元編號,僅僅提供字元與編號間映射。符號數量在不斷增加,已超百萬。詳細:[https://zh.wikipedia.org/zh cn/Unicode] 1. UTF :unicode轉換格式 (unicode ...
  • [PHP];;;;;;;;;;;;;;;;;;;; About php.ini ;;;;;;;;;;;;;;;;;;;;; PHP's initialization file, generally called php.ini, is responsible for; configuring man ...
  • Bean的裝配: 在spring容器內拼湊bean叫做裝配。裝 配bean的時候,需要告訴容器哪些bean 以及容器如何使用依賴註入將它們配合在一起。 上下文定義文件的根元素是<beans>.<beans>有多個<bean>子元素。每個<bean>元素定義了一個 bean如何被裝配到spring容器 ...
  • 首先導入所需的jar包,項目目錄結構如下: 之後需要配置一下web.xml文件,內容如下: 然後配置applicationContext.xml: 下麵開始建立實體類User.java: 然後是控制層代碼: 這裡的value即訪問路徑,而return的"hello"通過applicationCont ...
  • 對比學習Python與Cstr1 = 'hello python 2'# 字元串i = 3.1415926535print(str1)print("hello python\n")# print 自帶換行符print('%s'%str1)print(str1)print("你好,%s OK %d" ... ...
  • 【轉載自】:http://blog.csdn.net/qq7342272/article/details/7940741 java運行時異常是可能在java虛擬機正常工作時拋出的異常。 java提供了兩種異常機制。一種是運行時異常(RuntimeExepction),一種是檢查式異常(checked ...
  • 多態成員變數的特點: 運行結果: 圖解: java中,子類的實例對象在堆記憶體中的存放應如圖所示,既有子類的成員變數,又有父類的成員變數; 同一對象可以有多態,當父類變數指向時,從A入口進入,可以訪問父類的所有成員變數;當子類變數指向時,從B入口進入,可以訪問子類的所有成員變數和父類非同名變數; ja ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...