避免多线程操作上的GUI冻结

时间:2014-06-27 07:59:00

标签: c++ multithreading qt user-interface

我有一个Qt GUI类preferencesWindow,显然负责处理用户首选项。我有一些字段来管理与数据库服务器的连接。保留字段时,将调用dbsChanged()方法。下面是我设法编写的一些代码:

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(工作时)。

我应该如何重写整个过程?我虽然有一些解决方案,但我不确定他们的表现,而且我不想无用地工作......

3 个答案:

答案 0 :(得分:3)

它会冻结GUI,因为即使get_databases调用位于单独的线程中,您仍然会等待导致冻结的结果。

我不知道如何在Qt中做到这一点,但通常情况下会打开一个对话框,说'&34;请等待&#34;或者带有取消按钮的东西,并让工作线程在完成时向父(GUI)线程发送信号。

答案 1 :(得分:2)

QFuture将等到线程在您致电loader.result()时设置结果。您必须稍后等待该值。

我猜您可以将未来对象存储为preferencesWindow的成员,并在完成signal时向自己发送get_databases。因此,您可以在等待时间内为应用程序提供时间来处理其他事件。

答案 2 :(得分: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);