c++ - Qt - MainWindow 在运行时卡住

标签 c++ qt

我试图从数据库中获取大量数据,但在运行主窗口时卡住。

我在 Windows 下工作并根据此 link 5秒后Windows自动将程序设置为挂起状态。

有什么方法可以防止卡住吗?

代码如下:

void MainWindow::on_getDataButtonClicked()
{
    ui->centralWidget->setEnabled(false);
    QApplication::setOverrideCursor(Qt::WaitCursor);
    try
    {
        Client client(user, password);

        std::future<map<string, map<string, string> > > fut =
                  std::async(std::launch::async, &Client::get_data, &client);

        // While not all data has been retrieved, set message to the status bar.
        while (fut.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
        {
            ui->statusBar->showMessage("Getting data.");
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            ui->statusBar->showMessage("Getting data..");
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            ui->statusBar->showMessage("Getting data...");
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
        }

        map<string, map<string, string> > exported_strings = std::move(fut.get());

        ui->statusBar->showMessage("%All data has been retrieved!");
    }
    catch (std::string& s)
    {
        QMessageBox::critical(this, "Error", QString::fromStdString(s));
    }
    catch (std::exception& e)
    {
        QMessageBox::critical(this, "Error", QString(e.what()));
    }
    catch (...)
    {
        QMessageBox::critical(this, "Error", "An unknown error has occurred.");
    }
    ui->centralWidget->setEnabled(true);
    QApplication::restoreOverrideCursor();
}

附带说明一下,调试时主窗口不会卡住。

最佳答案

如果您正在等待并在 while 循环中阻塞 GUI 线程,那么进行异步工作是没有意义的。您需要摆脱 while 循环。

您可以使用 QtConcurrent::run 而不是 std::async,并使用 QFutureWatcher 来获得异步通知,而不会阻塞,当异步任务已经完成。

// https://github.com/KubaO/stackoverflown/tree/master/questions/async-sane-39396761
#include <QtWidgets>
#include <QtConcurrent>
#include <map>
#include <string>

struct Client {
    using result_type = std::map<std::string, std::map<std::string, std::string>>;
    result_type get_data() {
        QThread::sleep(5); // pretend to do some work
        return result_type();
    }
};

class MainWindow : public QMainWindow {
    Q_OBJECT
    Client::result_type exported_strings;
    QWidget centralWidget;
    QVBoxLayout layout{&centralWidget};
    QPushButton getDataButton{"Get Data"};
    QStatusBar statusBar;
    QTimer statusTimer;
    QString statusMessage;

    void setBusyStatus(const QString & status) {
        centralWidget.setEnabled(false);
        QApplication::setOverrideCursor(Qt::WaitCursor);
        statusMessage = status;
        statusTimer.start(0);
    }
    void setNormalStatus(const QString & status) {
        centralWidget.setEnabled(true);
        QApplication::restoreOverrideCursor();
        statusBar.showMessage(status);
        statusTimer.stop();
    }
    Q_SLOT void on_getDataButtonClicked();
public:
    MainWindow() {
        setStatusBar(&statusBar);
        setCentralWidget(&centralWidget);
        layout.addWidget(&getDataButton);
        int n = 0;
        connect(&statusTimer, &QTimer::timeout, [=]() mutable {
            statusBar.showMessage(QStringLiteral("%1%2").arg(statusMessage).arg(QString{n+1, QChar{'.'}}));
            n = (n+1)%3;
            statusTimer.start(500);
        });
        connect(&getDataButton, &QPushButton::clicked, this, &MainWindow::on_getDataButtonClicked);
    }
};

void MainWindow::on_getDataButtonClicked()
{
    auto future = QtConcurrent::run([=]{
        Client client;
        return client.get_data();
    });
    auto watcher = new QFutureWatcher<Client::result_type>{this};
    connect(watcher, &QFutureWatcher<Client::result_type>::finished, this, [=]{
        exported_strings = std::move(watcher->result());
        watcher->deleteLater();
        setNormalStatus("All data has been retrieved!");
    });
    watcher->setFuture(future);
    setBusyStatus("Getting data");
}

int main(int argc, char ** argv) {
    QApplication app{argc, argv};
    MainWindow w;
    w.show();
    return app.exec();
}
#include "main.moc"

或者,您可以从异步代码中发出一个信号,让您保留 std::async 的使用,如果您愿意的话:

#include <QtWidgets>
#include <future>
#include <map>
#include <string>

struct Client {
    using result_type = std::map<std::string, std::map<std::string, std::string>>;
    result_type get_data() {
        QThread::sleep(5); // pretend to do some work
        return result_type();
    }
};

class MainWindow : public QMainWindow {
    Q_OBJECT
    Client::result_type exported_strings;
    QWidget centralWidget;
    QVBoxLayout layout{&centralWidget};
    QPushButton getDataButton{"Get Data"};
    QStatusBar statusBar;
    QTimer statusTimer;
    QString statusMessage;

    std::future<Client::result_type> resultFuture;

    void setBusyStatus(const QString & status) {
        centralWidget.setEnabled(false);
        QApplication::setOverrideCursor(Qt::WaitCursor);
        statusMessage = status;
        statusTimer.start(0);
    }
    void setNormalStatus(const QString & status) {
        centralWidget.setEnabled(true);
        QApplication::restoreOverrideCursor();
        statusBar.showMessage(status);
        statusTimer.stop();
    }
    Q_SLOT void on_getDataButtonClicked();
    Q_SIGNAL void hasResult();
public:
    MainWindow() {
        setStatusBar(&statusBar);
        setCentralWidget(&centralWidget);
        layout.addWidget(&getDataButton);
        int n = 0;
        connect(&statusTimer, &QTimer::timeout, [=]() mutable {
            statusBar.showMessage(QStringLiteral("%1%2").arg(statusMessage).arg(QString{n+1, QChar{'.'}}));
            n = (n+1)%3;
            statusTimer.start(500);
        });
        connect(&getDataButton, &QPushButton::clicked, this, &MainWindow::on_getDataButtonClicked);
    }
};

void MainWindow::on_getDataButtonClicked()
{
    connect(this, &MainWindow::hasResult, this, [this](){
        exported_strings = std::move(resultFuture.get());
        setNormalStatus("All data has been retrieved!");
    }, Qt::UniqueConnection);

    resultFuture = std::async(std::launch::async, [this]{
        Client client;
        auto result = client.get_data();
        emit hasResult();
        return result;
    });
    setBusyStatus("Getting data");
}

int main(int argc, char ** argv) {
    QApplication app{argc, argv};
    MainWindow w;
    w.show();
    return app.exec();
}
#include "main.moc"

关于c++ - Qt - MainWindow 在运行时卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39396761/

相关文章:

qt - 在 CentOS 5.6 x64 上构建和安装 Qt-4.7.3 错误

c++ - 如何判断 g++ 和 clang 可用的 C++ 版本

c++ - 使用 Qt 进行音频信号处理 GUI 开发,如何?

c++ - 快速锁定大量读取且偶尔可能从另一个线程更改的变量

C++ std::async 调用类成员方法看不到变量变化

javascript - 使用表格 API 和 XMLHttpRequest 将请求正文发送到 google 表格

c++ - Qt 5.7 如何在运行时检查变量 QT_QPA_PLATFORM 的值?

QTableWidget。发出 cellChanged 信号

c++ - 当构造函数抛出异常时删除运算符段错误

c++ - 在供其他程序使用的头文件中,我可以只声明模板吗?