Reputation: 473
I want to show QT dialog as soon as show() is called; without waiting for the function to end.
void SomeFunction()
{
dialog_.reset(new MessageBoxProgression(this, SLOT(cancel()));
dialog_->show();// not displayed waits for longOperation() to finish
longOperation();
}
dialog_ is having a progress bar which needs to be shown and updated asynchronously, but currently, dialog_ is not show until longOperation() has finished executing.
EDIT: Can this be done?
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();
}
Upvotes: 0
Views: 1164
Reputation: 98425
The main thread should not be doing anything besides gui work. Long-running operations do not belong in the main thread at all. If you have a long-running operation, you should execute it asynchronously.
We can factor out some common long-operation features:
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);
}
};
Suppose that you have the following long operation - the input and output data types are given only as an example. Each loop iteration should take 1-10ms to minimize the overhead of checking the status and emitting progress. You can easily perform these checks every M
iterations.
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;
}
}
}
};
Then, to execute it asynchronously:
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();
}
...
};
An alternative approach using the QFuture
system is given in this answer, but it's not complete enough to streamline the progress indications. The above QObject
-based approach is a workable solution in the interim.
Upvotes: 1
Reputation: 942
Just add QApplication::processEvents();
void SomeFunction()
{
dialog_.reset(new MessageBoxProgression(this, SLOT(cancel()));
dialog_->show();// not displayed waits for longOperation() to finish
QApplication::processEvents();
longOperation();
}
this will solve the problem
Upvotes: 1