Я пытаюсь сделать визуализацию поиска максимального значения массива. Идея в том, чтобы показать позицию указателя в каждой итерации. Разметка сетки построена следующим образом: первая строка содержит десять меток, показывающих индекс каждого элемента массива, во второй строке — десять правок строки со значениями массива, а в третьей — десять меток, которые могут иметь текст «» (без указателя) или «^» (указатель на это значение). Это выглядит как
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 также не подходит, потому что мне нужна задержка не перед обработкой слота, а во время него.
Может ли кто-нибудь, кто сталкивался с такой проблемой, привести пример правильного использования функций, которые я упомянул, или дать эквивалентный пример, который может работать в этом случае?
Блокировка и 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 с
Я просто предлагаю вам подождать оставшееся время после завершения расчета.
// 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("^");
Насколько я понял QTimer :: singleShot также не подходит, потому что я
нужна задержка не до обработки слота, а во время него.
QTimer::singleShot
может подойдет, если вместо вашего for
В цикле у вас есть отложенный вызов (с использованием таймера), выполняющий функцию с индексом в качестве параметра и содержащую тело цикла. Затем эта функция может вызывать ifself рекурсивно с увеличенным индексом.