在函数调用期间临时禁用窗口
Posted
技术标签:
【中文标题】在函数调用期间临时禁用窗口【英文标题】:Temporarily disable window during a function call 【发布时间】:2020-06-01 09:51:37 【问题描述】:考虑:
class Worker
public:
void DoWork();
;
class MainWindow : public QMainWindow
Q_OBJECT
private:
Worker MyWorker;
public:
void DoWork();
;
我希望我的MainWindow
的DoWork
函数的行为类似于:
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()
),而且异常没有被正确捕获。
【问题讨论】:
【参考方案1】:您可以使用QFutureWatcher
接收有关您的QFuture
完成的通知,而无需忙于等待呼叫processEvents
。
基本上您订阅信号QFutureWatcher<void>::finished
以了解操作何时完成。在关联的插槽中,您重新启用小部件。特别重要的是调用future.waitForFinished()
,即使计算已经完成,以强制转移在工作线程中抛出的任何QException,请参阅here。这看起来像是一种解决方法,但我不知道是否有更好的解决方案。
订阅后您调用setFuture()
函数开始您的doWork
操作。计算将在自己的线程中进行。
这样你应该可以为所欲为:
-
工作时窗口被禁用
窗口在工作时保持响应(可以移动和调整大小)
Worker::doWork
中的任何 QException
都可以方便地捕获
根据您的问题,这是一个最小的工作示例。它应该告诉你我的意思......
#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"
【讨论】:
异常必须从QException
继承的限制在我的情况下有点限制。无论如何,现在我非常清楚在 Qt 中这样做的正确方法是什么。
我完全同意你的看法。我也认为这在某些情况下是一个限制。也许有更好的方法来管理这个,但我找不到它们 =)以上是关于在函数调用期间临时禁用窗口的主要内容,如果未能解决你的问题,请参考以下文章