отложить выполнение кода с помощью QTimer и Eventloop

Я хочу, чтобы моя программа ждала истечения времени ожидания QTimer для выполнения определенного метода. Функция выполняет некоторые вычисления в цикле, и после завершения этого цикла она должна дождаться истечения времени ожидания таймера и запуститься снова после завершения события таймера.

Вот текущее состояние кода, который генерирует поток и подключает таймер к методу generateData(). Этот код выполняется в конструкторе класса.

timer = new QTimer(0);
timer->setTimerType(Qt::PreciseTimer);
timer->setInterval(40); //25 frames per second


QThread *thread = new QThread(this);
moveToThread(thread);
timer->moveToThread(thread);

connect(thread, SIGNAL(started()), timer, SLOT(start()));
connect(timer, SIGNAL(timeout()), this, SLOT(timerEvent()));
connect(thread, SIGNAL(started()), this, SLOT(generateData()));
connect(this, SIGNAL(finished()), thread, SLOT(quit()));

thread->start();

метод, который должен ждать таймер после выполнения цикла for

void Class::generateData() {
    while (1) {
        calculation()
        //do some calculation, which takes around 3-5ms
        QEventLoop loop;
        connect(timer, SIGNAL(timeout()), &loop, SLOT(quit()));
        loop.exec();
    }
}

Кажется, цикл событий не мешает выполнению метода в это время. Есть ли другой способ сделать это?


person todayihateprogramming    schedule 28.06.2018    source источник
comment
предоставьте минимальный воспроизводимый пример, что такое object1 и object1->at(i)->test();?   -  person eyllanesc    schedule 28.06.2018
comment
Разве вы на самом деле не просто ищете QThread::msleep?   -  person Amfasis    schedule 28.06.2018
comment
Я обновил вопрос, но цикл расчета не имеет большого значения для вопроса.   -  person todayihateprogramming    schedule 28.06.2018
comment
Никогда не делай этого. Вы повторно входите в цикл событий. Просто не надо. Вам не нужно.   -  person Kuba hasn't forgotten Monica    schedule 01.07.2018


Ответы (2)


Ваш подход выглядит излишне сложным. Я бы сделал следующее:

void Class::generateData()
{
    // Do calculations.
    for (int i = 0; i<object1->size(); ++i)
    {
      object1->at(i)->test();
    }
    // Wait a little bit and do calculations again.
    QTimer::singleShot(40, this, SLOT(generateData()));
}

Обратите внимание, что я избавился от цикла while, потому что таймер рекурсивно вызывает одну и ту же функцию.

person vahancho    schedule 28.06.2018
comment
Проблема с этим подходом заключается в том, что я теряю точность времени, так как я жду завершения расчета, а затем запускаю таймер. Но я хотел бы выполнять вычисления во время работы таймера, чтобы событие по крайней мере почти точно вызывалось каждые 40 мс. - person todayihateprogramming; 28.06.2018
comment
в этом случае вы должны просто подключить generateData() к слоту timer.timeout() и, возможно, вызвать для вас moveToThread Class. Qt обрабатывает границы потоков за вас - person Amfasis; 28.06.2018
comment
Разве это не та же проблема, поскольку расчет запускается, когда таймер истекает? Поскольку расчет занимает до 5 мс, я не буду точен. - person todayihateprogramming; 28.06.2018
comment
@todayihateprogramming, вы можете измерить время расчета и настроить время ожидания, чтобы оно всегда было 40 мс. Если, скажем, расчет занимает 5 мс, вы установите QTimer::singleShot(40 - 5, this, SLOT(generateData()));, так что общее время все равно будет 40 мс. - person vahancho; 28.06.2018
comment
Как я также добавил к своему ответу, я думаю, что это не так, поскольку таймер фактически использует системный вызов, который будет срабатывать независимо от использования потока. - person Amfasis; 28.06.2018
comment
Расчет зависит от множества переменных и не является постоянным. Я надеялся, что будет способ просто подождать после завершения расчета, но while (timer.isactive()); не работает - person todayihateprogramming; 28.06.2018
comment
Я также видел реализации, в которых QElapsedTimer запускается в начале расчета, а затем измеряется в конце, затем это прошедшее время вычитается из 40 мс, и результат используется в QThread::usleep или QThread::msleep. - person Amfasis; 28.06.2018
comment
Возможно, стоит попробовать. Хорошая идея! - person todayihateprogramming; 28.06.2018

Сочетая подсказки, которые вы дали по другим ответам, я думаю, это то, что вы ищете:

Class::Class(QObject *parent)
{
    timer = new QTimer(0);
    timer->setTimerType(Qt::PreciseTimer);
    timer->setInterval(40); //25 frames per second

    QThread *thread = new QThread(this);
    moveToThread(thread);
    timer->moveToThread(thread);
    this->moveToThread(thread);

    connect(thread, SIGNAL(started()), timer, SLOT(start()));
    connect(timer, SIGNAL(timeout()), this, SLOT(generateData()));
    connect(this, SIGNAL(finished()), timer, SLOT(stop()));
    connect(this, SIGNAL(finished()), thread, SLOT(quit()));

    thread->start();
}

void Class::generateData()
{
    // Do calculations.
    for (int i = 0; i<object1->size(); ++i)
    {
        object1->at(i)->test();
    }
}

Каждый раз, когда таймер истекает, он запускает функцию generateData в потоке (поскольку вы переместили класс в этот поток). Таймер будет продолжать пульсировать с частотой 25 Гц, поскольку на самом деле это системный вызов (а не активное ожидание в этом потоке). В Windows это может быть недостаточно точно.

Обратите внимание, что вы не можете вызывать moveToThread для этого, если у него есть родители, см. QT docs Также обратите внимание, что класс должен быть производным от QObject, но я думаю, что это уже так, поскольку вы connecting

person Amfasis    schedule 28.06.2018
comment
QTimer может быть просто дочерним элементом класса, тогда перемещение класса также приведет к перемещению таймера (он перемещает всех дочерних элементов). Поток почти всегда не может быть перемещен сам в себя, и это то, что вы делаете, когда поток является дочерним элементом класса. Что-то еще должно владеть обоими. - person Kuba hasn't forgotten Monica; 01.07.2018
comment
В настоящее время ветка не является участником, но вы правы, это решение может иметь некоторые изменения! - person Amfasis; 01.07.2018