Я пытаюсь использовать QProgressDialog
предоставить пользователю некоторую информацию о ходе выполнения длинной задачи, одновременно позволяя ему отменить эту задачу.
В основном у меня есть QDialog
с кнопкой Compute
, При нажатии на него вызывается трудоемкий метод для члена моего QDialog
родитель. Этот метод принимает обратный вызов, чтобы сообщить вызывающей стороне о ходе работы.
Проблема заключается в том, что диалогу прохождения требуется некоторое время, прежде чем он появится, и не учитывает сразу щелчок по его Cancel
кнопка.
Ясно, что в моем коде есть сбой, но я не привык к Qt, и я пробовал много вещей. Мне наверное нужна отдельная тема.
Выдержка из моего кода:
void MyDialog::callbackFunction(int value, void * objPtr) {
((QProgressDialog *)(objPtr))->setValue(value);
QCoreApplication::processEvents();
}
void MyDialog::on_mComputeBtn_clicked()
{
Compute();
}
void MyDialog::Compute()
{
QProgressDialog progressDialog("Optimization in progress...", "Cancel", 0, 100, this);
progressDialog.setMinimumDuration(500); // 500 ms
progressDialog.setWindowModality(Qt::WindowModal);
progressDialog.setValue(0);
connect(&progressDialog, SIGNAL(canceled()), this, SLOT(Cancel()));
QCoreApplication::processEvents();
parentMember->LongComputation(callbackFunction);
// probably useless
progressDialog.reset();
progressDialog.hide();
QCoreApplication::processEvents();
}
Диалог не появляется сразу, потому что вы установили минимальную продолжительность 500 мс. Установите значение 0, чтобы диалоговое окно отображалось сразу после изменения хода выполнения, или вызовите функцию показа вручную.
Чтобы заставить кнопку отмены работать, переместите ваши длинные вычисления в другой поток (например, используйте QThread или std :: async) и позвольте вашему основному циклу событий продолжить свое выполнение.
На самом деле есть много проблем с вашим кодом, но эти два пункта должны указать вам правильное направление. По моему опыту, каждый ручной вызов processEvents — это большой запах кода.
То, что вы делаете, это пытаетесь использовать модальную парадигму QProgressdialog, не позволяя запускать насос основного события, а также вы устанавливаете тайм-аут на 0,5 с, минимальная продолжительность обеспечит паузу. Возможно, немодальный вариант больше подходит для вашего случая.
если процесс получил отдельные шаги, вы можете сделать это без отдельного потока
class MyTask : public QObject
{
Q_OBJECT
public:
explicit MyTask(QObject *parent = 0);
signals:
public slots:
void perform();
void cancel();
private:
int steps;
QProgressDialog *pd;
QTimer *t;
};
...
void MyDialog::on_mComputeBtn_clicked()
{
myTask = new MyTask;
}
...MyTask::MyTask(QObject *parent) :
QObject(parent), steps(0)
{
pd = new QProgressDialog("Task in progress.", "Cancel", 0, 100000);
connect(pd, SIGNAL(canceled()), this, SLOT(cancel()));
t = new QTimer(this);
connect(t, SIGNAL(timeout()), this, SLOT(perform()));
t->start(0);
}
void MyTask::perform()
{
pd->setValue(steps);
//... perform one percent of the operation
steps++;
if (steps > pd->maximum())
t->stop();
}
void MyTask::cancel()
{
t->stop();
//... cleanup
}
QThread пример существует здесь: Qt 5: обновлять QProgressBar во время работы QThread через сигнал