c++ - 在函数调用期间暂时禁用窗口

标签 c++ qt qtconcurrent

考虑:

class Worker
{
public:
  void DoWork();
};

class MainWindow : public QMainWindow
{
Q_OBJECT

private:
  Worker MyWorker;

public:
  void DoWork();
};

我希望自己的DoWorkMainWindow函数表现为:
void MainWindow::DoWork()
{
  setEnabled(false);

  std::future<void> Resu = std::async(std::launch::async, &Worker::DoWork, &MyWorker);

  while (Resu.wait_for(100ms) == std::future_status::timeout)
    qApp->processEvents();

  try
    { Resu.get(); }
  catch (Except const &MyExcept)
    { QMessageBox::critical(this, "Error", MyExcept.What()); }

   setEnabled(true);
 }

这可以按我的意愿工作:(i)在执行工作时禁用了窗口,
(ii)窗口在工作时保持响应(可以移动和调整大小),以及(iii)任何
捕获在Except中的Worker::DoWork很方便。

但是,此解决方案依赖std::futureqApp->processEvents(),后者
不被this answer推荐。

如何在Qt中正确编写以上代码?

到目前为止,我已经尝试过:
void MainWindow::DoWork()
{
  setEnabled(false);

  QFuture<void> Resu = QtConcurrent::run(std::bind(&Worker::DoWork, &MyWorker));

  try
    { while (Resu.isRunning()) qApp->processEvents(); }
  catch (Except const &MyExcept)
    { QMessageBox::critical(this, "Error", MyExcept.What()); }

   setEnabled(true);
 }

并且我发现消耗整个线程的弊端(因为对qApp->processEvents()的调用太频繁了),而且没有正确捕获异常。

最佳答案

您可以使用 QFutureWatcher 来接收有关QFuture完成的通知,而不必忙于等待调用processEvents

基本上,您订阅信号QFutureWatcher<void>::finished即可知道操作何时完成。在关联的插槽中,您可以重新启用小部件。特别重要的是即使计算已经完成也要调用future.waitForFinished(),以强制传输在工作线程中抛出的任何QException,请参见here。看来这是一种解决方法,但我不知道是否有更好的解决方案。

订阅后,您可以调用setFuture()函数来启动doWork操作。计算将在其自己的线程中进行。

这样,您应该可以根据需要执行所有操作:

  • 执行工作时禁用窗口
  • 窗口在工作时保持响应(可以移动并调整大小)
  • 可以方便地捕获QException中抛出的任何Worker::doWork

  • 根据您的问题,这是一个最小的工作示例。它应该告诉你我的意思...
    #include <QApplication>
    #include <QException>
    #include <QMessageBox>
    #include <QPushButton>
    #include <QThread>
    #include <QtConcurrent>
    
    
    class Exception : public QException
    {
    public:
        Exception(const QString& message = "") : m_message {message} { }
    
        void raise() const override { throw *this; }
    
        Exception *clone() const override { return new Exception(*this); }
    
        QString message() { return m_message; }
    
    private:
        QString m_message;
    };
    
    
    class Worker
    {
    public:
        void doWork() {
            QThread::sleep(5);
            // throw Exception {"something bad happened..."};
            QThread::sleep(5);
        }
    };
    
    
    class Button : public QPushButton
    {
        Q_OBJECT
    
    public:
        Button(QWidget* parent = nullptr) : QPushButton {parent} {
            resize(400, 300);
            setText("Click me to wait!");
    
            connect(this, &QPushButton::clicked,
                    this, &Button::doWork);
        }
    
    public slots:
        void doWork() {
            setEnabled(false);
    
            auto future {QtConcurrent::run(&m_worker, &Worker::doWork)};
    
            auto watcher {new QFutureWatcher<void> {}};
            connect(watcher, &QFutureWatcher<void>::finished,
                    [=]() mutable {
                        try {
                            // Call this to force the transfer of any QException that was thrown in the worker thread
                            // (https://doc.qt.io/qt-5/qexception.html)
                            future.waitForFinished();
                        } catch (Exception& e) {
                            QMessageBox::critical(this, "Error", e.message());
                        }
                        setEnabled(true);
                    });
    
            watcher->setFuture(future);
        }
    
    private:
        Worker                  m_worker;
    };
    
    
    int main(int argc, char* argv[])
    {
        QApplication app {argc, argv};
    
        auto button {new Button {}};
        button->show();
    
        return app.exec();
    }
    
    #include "main.moc"
    
    

    关于c++ - 在函数调用期间暂时禁用窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62129606/

    相关文章:

    c++ 数组初始化

    c++ - 如何根据用户输入动态更改任何项目的位置?

    c++ - 如何使用 Visual Studio 2017 和 Qt 开发 GUI 应用程序?

    c++ - QtConcurrent映射和进度报告

    c# - 如何更改我的 C++ 代码以用作 C# 中的 DLL?

    c++ - 获取 C++ 中表达式的类型,包括引用

    c++ - 如何决定要打印的 QtMsgType 消息类型

    c++ - QtConcurrent 在 App 即将退出时等待完成

    c++ - 如何控制(即中止)QScriptEngine 的当前评估

    c++ - 如何替换boost spirit x3中的左递归?