简体   繁体   English

避免GUI冻结多线程操作

[英]Avoiding GUI freezing on multithreaded operations

I have a Qt GUI class preferencesWindow that, obviously, is responsible for handling the user preferences. 我有一个Qt GUI类preferencesWindow ,显然,它负责处理用户的首选项。 I have some fields that manage the connection to a database server. 我有一些用于管理与数据库服务器的连接的字段。 When a field is left, dbsChanged() method is called. 保留字段时,将dbsChanged()方法。 Below is some code I managed to write: 以下是我设法编写的一些代码:

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;
}

Since get_databases can take a long time, I thought that putting in on a separate thread as you can see in these two lines: 由于get_databases可能要花费很长时间,因此我认为请放在这两行中可以看到的单独线程中:

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

could solve the problem. 可以解决问题。 It runs on a separate thread, but it still freezes the GUI (while working). 它在单独的线程上运行,但是仍然冻结GUI(工作时)。

How should I rewrite this entire process? 我应该如何重写整个过程? I though of some solutions, but I am not really sure about their performance, and I don't want to work uselessly... 我虽然有一些解决方案,但我对它们的性能并不十分确定,也不想无用地工作...

It freezes the GUI because even though the get_databases call is in a separate thread, you still wait for the results which causes the freeze. 它冻结了GUI,因为即使get_databases调用位于单独的线程中,您仍然要等待导致冻结的结果。

I don't know how to do it in Qt, but the normal thing would be to open a dialog saying "please wait" or something with a cancel button, and have the worker thread send a signal to the parent (GUI) thread when done. 我不知道如何在Qt中执行此操作,但是正常的情况是打开一个对话框,显示“请稍候”或带有取消按钮的内容,并在以下情况下让工作线程向父(GUI)线程发送信号:完成。

The QFuture will wait until the thread sets the result when your call loader.result() . 当您调用loader.result()时,QFuture将等待线程设置结果。 You have to wait for that value later. 您必须稍后等待该值。

I guess you could store the future object as member of preferencesWindow and send yourself a signal , when finishing get_databases . 我想您可以在完成get_databases时将将来的对象存储为preferencesWindow成员,并向自己发送signal So you give your application time to process other events during this wait time. 因此,您可以在这段等待时间内给应用程序时间来处理其他事件。

You can use QFutureWatcher to monitor that status of the QFuture object, like written in the documentation: 您可以使用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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM