异步显示Qt对话框

时间:2018-03-09 04:52:39

标签: c++ qt c++11

我想在调用show()后立即显示QT对话框;无需等待功能结束。

void SomeFunction()
{
dialog_.reset(new MessageBoxProgression(this, SLOT(cancel()));
dialog_->show();// not displayed waits for longOperation() to finish
longOperation();
}

dialog_有一个进度条,需要异步显示和更新,但是目前,在longOperation()执行完毕之前,dialog_才会显示。

编辑:可以这样做吗?

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();
}

2 个答案:

答案 0 :(得分:1)

只需添加QApplication :: processEvents();

void SomeFunction()
{
    dialog_.reset(new MessageBoxProgression(this, SLOT(cancel()));
    dialog_->show();// not displayed waits for longOperation() to finish
    QApplication::processEvents(); 
    longOperation();
}

这将解决问题

答案 1 :(得分: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-10ms来最小化检查状态和发出进度的开销。您可以在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_progress{this};
  LongOperation m_operation{&m_progress};
  ...
public:
  Window(QWidget * parent = {}) : QWidget(parent) {}
  void runLongOperation() {
    m_operation.start();
  }
  ...
};

使用QFuture系统的替代方法在this answer中给出,但它不够完整以简化进度指示。上述基于QObject的方法在过渡期间是一个可行的解决方案。