有時候需要處理一些跟界面無關的但非常耗時的事情,這些事情跟界面在同一個線程中,由於時間太長,導致界面無法響應,處於“假死”狀態。例如:在應用程式中保存文件到硬碟上,從開始保存直到文件保存完畢,程式不響應用戶的任何操作,視窗也不會重新繪製,從而處於“無法響應”狀態,這是一個非常糟糕的體驗 。 在這種情... ...
有時候需要處理一些跟界面無關的但非常耗時的事情,這些事情跟界面在同一個線程中,由於時間太長,導致界面無法響應,處於“假死”狀態。例如:在應用程式中保存文件到硬碟上,從開始保存直到文件保存完畢,程式不響應用戶的任何操作,視窗也不會重新繪製,從而處於“無法響應”狀態,這是一個非常糟糕的體驗 。
在這種情況下,有一種方法是使用多線程,即在子線程中處理文件保存,主線程負責界面相關。
而如果不想使用多線程,最簡單的辦法就是在文件保存過程中頻繁調用QApplication::processEvents()。該函數的作用是讓程式處理那些還沒有處理的事件,然後再把使用權返回給調用者。
代碼如下:
bool MyApp::writeFile(const QString &filename) { QFile file(filename); ... QApplication::setOverrideCursor(Qt::WaitCursor); for(int r = 0; r != rowCount; ++r) { for(int c = 0; c != colCount; ++c) { out << table(r,c); qApp.processEvents(); } } QApplication::restoreOverrideCursor(); }
這樣一來,程式就能響應了。
但是,該方法有一個問題:可能正在保存文件的過程中,用戶不小心又單擊了保存,或不小心關閉了程式主視窗,這樣會產生意想不到的後果。
解決這個問題的最簡單的辦法是替換成:
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);//它可以忽略用戶的輸入(滑鼠和鍵盤事件)。
進一步的,如果想顯示一個帶有進度條的對話框,隨時顯示當前的進度狀態,可以使用QProgressDialog。
bool MyApp::writeFile(const QString &filename) { QFile file(filename); ... QApplication::setOverrideCursor(Qt::WaitCursor); QProgressDialog progress; progress.setWindowTitle(tableData->sNameCH); progress.setLabelText(QStringLiteral("數據保存中,請稍候...")); //progress.setCancelButton(0);//不顯示“取消”按鈕 progress.setCancelButtonText("取消"); progress.setRange(0,rowCount ); progress.setModal(true); //此處沒有調用show()來顯示,是因為QProgressDialog會自動決定是否顯示 //如果時間過短,就不會顯示。 for(int r = 0; r != rowCount; ++r) { progress.setValue(row); //如果用戶單擊了“取消”,就取消保存文件,並刪除該文件。 if(progress.wasCanceled) { file.remov(); return false; } for(int c = 0; c != colCount; ++c) { out << table(r,c); qApp.processEvents(); } } QApplication::restoreOverrideCursor(); }
顯示效果如下: