Как я могу отложить процесс в Qt

Я пытаюсь сделать визуализацию поиска максимального значения массива. Идея в том, чтобы показать позицию указателя в каждой итерации. Разметка сетки построена следующим образом: первая строка содержит десять меток, показывающих индекс каждого элемента массива, во второй строке — десять правок строки со значениями массива, а в третьей — десять меток, которые могут иметь текст «» (без указателя) или «^» (указатель на это значение). Это выглядит как

0 1 2 3 4 5 6 7 8 9 — ряд индексов

12 23 34 54 78 1 32 26 55 26 — элементы

_ _ _ ^ _ _ _ _ _ _ — строка указателей

При поиске максимума указатель должен переходить к следующему элементу после каждой итерации. А для комфортного восприятия необходимо подождать секунду во время каждой итерации цикла.
Код слота:

void Widget::slotFindMax()
{
int max = a[0];
pointer[0]->setText("^");
maxValue->setText(QString::number(max));
for (int i = 1; i < 10; i++)
{
pointer[i - 1]->setText(" ");
QThread::sleep(1);
pointer[i]->setText("^");
if (a[i] > max)
{
max = a[i];
maxValue->setText(QString::number(max));
}
}
pointer[9]->setText(" ");
}

Я вижу, что QThread :: sleep () не работает здесь правильно. Насколько я понял, QTimer :: singleShot также не подходит, потому что мне нужна задержка не перед обработкой слота, а во время него.
Может ли кто-нибудь, кто сталкивался с такой проблемой, привести пример правильного использования функций, которые я упомянул, или дать эквивалентный пример, который может работать в этом случае?

1

Решение

Блокировка и GUI не смешиваются. Пишите в асинхронном стиле, используйте C ++ 11:

class Widget : ... {
// use QVector, std::vector or std::array for pointer and a!
QTimer m_animationTimer { this };
...
};

void Widget::slotFindMax()
{
m_animationTimer.start(1000);
int i = 0;
int max = std::numeric_limits<int>::min();
auto loop = [this, i, max]() mutable {
if (i > 0) pointer[i-1]->setText(" ");
if (i == pointer.size()) {
m_animationTimer.disconnect();
return m_animationTimer.stop();
}
pointer[i]->setText("^");
if (a[i] > max) {
max = a[i];
maxValue->setText(QString::number(max));
}
i ++;
});
loop(); // run the first iteration
connect(&m_animationTimer, &QTimer::timeout, loop);
}

loop функтор захватывает изменяемые значения i а также max по значению и выполняется один раз сразу, затем один раз в секунду до полного pointer массив был перебран. Затем он останавливает таймер. Функтор вызывается из QObject::event метод основания QObjectи вызов приходит как QTimerEvent обрабатывается из цикла событий. Поскольку ваш код не блокирует цикл обработки событий, он остается отзывчивым.

В C ++ 98 / Qt 4 вы должны поместить функтор в слот QObjectкласс и т.д. Это было бы более многословно, но логика в конечном итоге была бы той же.

1

Другие решения

Подводя итог, если я правильно понял ваш вопрос:

  • Сохранить начальное время
  • Запустить расчет
  • Если расчет занял более 1 с, просто перейдите к следующей итерации
  • Если прошедшее время меньше 1 с, подождите до 1 с

Я просто предлагаю вам подождать оставшееся время после завершения расчета.

// Get initial time
auto t1 = QDateTime::currentMSecsSinceEpoch();

// Make a complex operation
if (a[i] > max)
{
max = a[i];
maxValue->setText(QString::number(max));
}

// Get final time
auto t2 = QDateTime::currentMSecsSinceEpoch();

// Wait until the end of the 1s if required, or directly continue
if (t2-t1 < 1000) QThread::msleep(1000-(t2-t1));

// Update the pointer
if (i>0) pointer[i - 1]->setText(" ");
pointer[i]->setText("^");
0

Насколько я понял QTimer :: singleShot также не подходит, потому что я
нужна задержка не до обработки слота, а во время него.

QTimer::singleShot может подойдет, если вместо вашего for В цикле у вас есть отложенный вызов (с использованием таймера), выполняющий функцию с индексом в качестве параметра и содержащую тело цикла. Затем эта функция может вызывать ifself рекурсивно с увеличенным индексом.

0
По вопросам рекламы [email protected]