异步显示 Qt 对话框
Posted
技术标签:
【中文标题】异步显示 Qt 对话框【英文标题】:Show Qt dialog asynchronously 【发布时间】:2018-03-09 04:52:39 【问题描述】:我想在调用 show() 后立即显示 QT 对话框;无需等待函数结束。
void SomeFunction()
dialog_.reset(new MessageBoxProgression(this, SLOT(cancel()));
dialog_->show();// not displayed waits for longOperation() to finish
longOperation();
dialog_ 有一个进度条,需要异步显示和更新,但目前,dialog_ 直到 longOperation() 执行完成后才会显示。
编辑:可以这样做吗?
void SomeFunction()
dialog_.reset(new MessageBoxProgression(this, SLOT(cancel()));
dialog_->show();// not displayed waits for longOperation() to finish
QApplication::processEvents();
longOperation();
update(dialog_);
QApplication::processEvents();
longOperation2();
【问题讨论】:
使用:qApp->processEvents();
或者在另一个线程中运行longOperation()
,一般第二种最合适
How to make Qt work when main thread is busy?的可能重复
我看代码没有问题,你可以做
将长操作移动到不同的线程。 QApplication::processEvents
是一个可怕的想法,应该避免。
【参考方案1】:
主线程不应该做除了 gui 工作之外的任何事情。长时间运行的操作根本不属于主线程。如果你有一个长时间运行的操作,你应该异步执行它。
我们可以分解出一些常见的长操作特征:
class LongOperationBase : public QObject
Q_OBJECT
std::atomic_bool stop, running;
protected:
bool shouldRun() const return !stop;
virtual void compute() = 0;
public:
Q_SLOT void start()
stop = false;
emit started();
QtConcurrent::run([this]
if (running || stop) return;
running = true;
compute();
running = false;
);
LongOperationBase()
LongOperationBase(QProgressDialog *pd)
connectTo(pd);
bool isRunning() const return running;
Q_SLOT void cancel() stop = true; // thread-safe
Q_SIGNAL void started();
Q_SIGNAL void hasRange(int);
Q_SIGNAL void hasProgress(int);
void connectTo(QProgressDialog *pd)
using B = LongOperationBase;
connect(this, &B::started, pd, &QProgressDialog::show);
connect(this, &B::hasRange, pd, &QProgressDialog::setMaximum);
connect(this, &B::hasProgress, pd, &QProgressDialog::setValue);
connect(pd, &QProgressDialog::canceled, this, &B::cancel, Qt::DirectConnection);
;
假设您有以下长操作 - 输入和输出数据类型仅作为示例给出。每次循环迭代应该花费 1-10 毫秒,以最大限度地减少检查状态和发出进度的开销。您可以轻松地在每个M
迭代中执行这些检查。
struct LongOperation final : LongOperationBase
std::vector<int> input;
std::vector<double> output;
using LongOperationBase::LongOperationBase;
void compute() override
size_t const N = input.size();
size_t const M = 1;
emit hasRange(N);
for (size_t i = 0, j = 0; i < N; ++i, ++j)
// operate on input, generate output
...
if (j == (M-1))
emit hasProgress(i);
if (!shouldRun()) break;
j = 0;
;
然后,异步执行它:
class Window : public QWidget
Q_OBJECT
QProgressDialog m_progressthis;
LongOperation m_operation&m_progress;
...
public:
Window(QWidget * parent = ) : QWidget(parent)
void runLongOperation()
m_operation.start();
...
;
this answer 中给出了使用QFuture
系统的替代方法,但它还不足以简化进度指示。上述基于QObject
的方法是临时可行的解决方案。
【讨论】:
【参考方案2】:只需添加 QApplication::processEvents();
void SomeFunction()
dialog_.reset(new MessageBoxProgression(this, SLOT(cancel()));
dialog_->show();// not displayed waits for longOperation() to finish
QApplication::processEvents();
longOperation();
这样就可以解决问题了
【讨论】:
dialog_ 无响应,dialog_ 内的按钮无法点击。 @SatyamRaikar 如果不使用 QApplication::processEvents() 是否有响应? @SatyamRaikar 他们不会点击,因为事件循环被阻止了。QApplication::processEvents
会告诉它只处理一次事件,当它被调用时。无论如何,您都应该避免使用processEvents
,因为它会在您的应用程序中产生各种问题。将繁重的处理任务转移到不同的线程,这样您就不会再阻塞主事件循环了。以上是关于异步显示 Qt 对话框的主要内容,如果未能解决你的问题,请参考以下文章