考虑:
class Worker
{
public:
void DoWork();
};
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
Worker MyWorker;
public:
void DoWork();
};
我希望自己的
DoWork
的MainWindow
函数表现为: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::future
和qApp->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/