Search code examples
c++qtqtimer

QTimer isActive returns true, but remainingTime returns -1


A timer is defined and started in the beginning:

QTimer *teleTimer;
teleTimer = new QTimer();
QObject::connect(teleTimer, SIGNAL(timeout()), this, SLOT(somefunction1())); 
teleTimer->start(200);

Then it is stopped somewhere and another function somefunction2() is called. After that function finished, the timer is started again:

if (teleTimer->isActive())
{
    qDebug() << teleTimer->remainingTime();
    teleTimer->stop();
    delete teleTimer;
}
teleTimer = new QTimer();
QObject::connect(teleTimer, SIGNAL(timeout()), this, SLOT(somefunction3())); 
teleTimer->start(200);

However, teleTimer->isActive() returns true, while teleTimer->remainingTime() returns -1, then the application is crashed:

Thread 2 Crashed:: QThread
0   org.qt-project.QtCore           0x000000010ed4be3b QObject::killTimer(int) + 27
1   org.qt-project.QtCore           0x000000010ed5a9b9 QTimer::stop() + 25
2   com.yourcompany.QTGCS           0x000000010dffa609 TelemetrySerialWorker::setTelemetryMode(int) + 745 (telemetryserialworker.cpp:114)

So how to fix this? Thanks.

Update: This problem is solved. Thanks for all the replies. I will try to post a question in a good format next time. Thanks.


Solution

  • Generally speaking, if you're invoking functions in a sequence, it's simplest to keep the sequence in a table, and use single-shot timers. Recreating timers or connections is expensive.

    #include <QtCore>
    #include <functional>
    #include <initializer_list>
    
    class Sequencer : public QObject {
    public:
      using base_class = QObject;
      struct Element {
        int delay = 0;
        std::function<void()> functor;
      };
      explicit Sequencer(QObject *parent = {}) : QObject(parent) {}
      // takes care of initializer list and other compatible initializers
      Sequencer(QVector<Element> init, QObject *parent = {}) : 
        QObject(parent), m_sequence(std::move(init)) {}
      void start(int index = 0) {
        m_it = m_sequence.begin() + index;
        m_timer.start(m_it->delay, this);
      }
      int stop() {
        m_timer.stop();
        return m_it - m_sequence.begin();
      }
    protected:
      void timerEvent(QTimerEvent *event) override {
        if (m_timer.timerId() != event->timerId())
          return base_class::timerEvent(event);
        m_it->functor();
        m_it++;
        if (m_it != m_sequence.end())
          m_timer.start(m_it->delay, this);
        else
          m_timer.stop();
      }
    private:
      QVector<Element> m_sequence;
      QVector<Element>::const_iterator m_it = m_sequence.begin();     
      QBasicTimer m_timer;
    };
    

    You'd use it like this, for example:

    class MyClass { // can be QObject, but doesn't have to be
      Sequence m_seq{
        {200, [=]{ function1(); }},   // after 200ms delay
        {0,   [=]{ function2(); }},   // after no delay
        {500, [=]{ function3(); }},   // after 500ms delay
        {0,   [=]{ loop(); }}};       // and right away loop the sequence
      void function1();
      void function2();
      void function3();
      void loop() { m_seq.start(); }
    public:
      MyClass() {
        m_seq.start();
      }
    };