2014-06-27 48 views
1

我有一個Qt GUI類preferencesWindow,很明顯,它負責處理用戶首選項。我有一些字段管理與數據庫服務器的連接。當一個字段離開時,dbsChanged()方法被調用。下面是一些代碼,我設法寫:在多線程操作中避免GUI凍結

void preferencesWindow::dbsChanged() { 
    QFuture<QStringList> loader = run(this, &preferencesWindow::get_databases); 
    QStringList databases = loader.result(); 

    if (databases.length()) { 
     this->ui.database->show(); 
     this->ui.nodb_label->hide(); 
     this->ui.database->clear(); 
     this->ui.database->addItems(databases); 
     this->ui.okButton->setDisabled(false); 
     this->ui.validationStatus->setPixmap(QPixmap(":/icon/tick.png")); 
    } else { 
     this->ui.database->hide(); 
     this->ui.nodb_label->show(); 
     this->ui.okButton->setDisabled(true); 
     this->ui.validationStatus->setPixmap(QPixmap(":/icon/error.png")); 
    } 
} 
QStringList preferencesWindow::get_databases() { 
    QSqlDatabase test_connection; 
    if (QSqlDatabase::contains("PREFEREMCES_LIVE_TEST_CONNECTION")) 
     test_connection = QSqlDatabase::database("PREFEREMCES_LIVE_TEST_CONNECTION"); 
    else test_connection = QSqlDatabase::addDatabase("QMYSQL", "PREFEREMCES_LIVE_TEST_CONNECTION"); 
    test_connection.setHostName(this->ui.serverAddress->text()); 
    test_connection.setUserName(this->ui.username->text()); 
    test_connection.setPassword(this->ui.password->text()); 
    test_connection.setDatabaseName(this->ui.database->currentText()); 
    test_connection.setPort(this->ui.serverPort->value()); 

    test_connection.open(); 
    qDebug() << "Error: " << test_connection.lastError(); 
    QSqlQuery show_databases = test_connection.exec("show databases"); 
    QStringList databases; 
    while (show_databases.next()) { 
     databases.append(show_databases.value(0).toString()); 
    } 
    QSqlDatabase::removeDatabase("PREFERENCES_LIVE_TEST_CONNECTION"); 
    return databases; 
} 

由於get_databases可能需要很長的時間,我覺得把在一個單獨的線程,你可以在這兩條線看:

QFuture<QStringList> loader = run(this, &preferencesWindow::get_databases); 
QStringList databases = loader.result(); 

可能解決這個問題。它運行在一個單獨的線程上,但它仍凍結了GUI(在工作時)。

我應該如何重寫這整個過程?我雖然一些解決方案,但我真的不知道他們的表現,我不想白白工作...

回答

1

您可以使用QFutureWatcher監視QFuture對象的這種地位,就像寫在文件中:

// Instantiate the objects and connect to the finished signal. 
MyClass myObject; 
QFutureWatcher<int> watcher; 
connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished())); 

// Start the computation. 
QFuture<int> future = QtConcurrent::run(...); 
watcher.setFuture(future); 
3

它凍結GUI因爲即使get_databases通話是在一個單獨的線程,你仍然等待導致凍結的結果。

我不知道如何在Qt中做到這一點,但正常的事情是打開一個對話框,說「請稍等」或用取消按鈕的東西,並讓工作線程發送一個信號給父母( GUI)線程完成後。

+0

這將導致更差的性能,因爲該過程可能需要從0.5秒至30秒 – Victor

+0

@Victor在某些情況下,這可能會導致性能下降*(例如,只需要0.5秒)。如果操作通常至少需要幾秒鐘,那麼這會如何導致更糟糕的表現?在這種情況下,設置一切(顯示對話框,開始踩踏)和拆除(發送信號,關閉對話框)的時間可以忽略不計。 –

+2

@維克多:約阿希姆是對的。期貨最好與QFutureWatcher結合使用,當你的未來完成時,它會向你的父線程發出信號。在當前代碼中使用未來並不合理。 – OnWhenReady

2

QFuture將等待,直到線程設置結果時,您的電話loader.result()。你必須等待那個值。

我想你可以存儲未來的對象作爲preferencesWindow的成員,併發送自己signal,當完成get_databases。所以你在這段等待時間內給你的申請時間來處理其他事件。