how to add a 1 second delay using Qtimer
Asked Answered
N

4

17

I currently have a method which is as follows

void SomeMethod(int a)
{

     //Delay for one sec.
     timer->start(1000);

     //After one sec
     SomeOtherFunction(a);
}

This method is actually a slot that is attached to a signal. I would like to add a delay of one sec using Qtimer.However I am not sure on how to accomplish this. Since the timer triggers a signal when its finished and the signal would need to be attached to another method that does not take in any parameters. Any suggestion on how I could accomplish this task.?

Update : The signal will be called multiple times in a second and the delay will be for a second. My issue here is passing a parameter to the slot attached to timeout() signal of a timer. My last approach would be to store the value in a memeber variable of a class and then use a mutex to protect it from being changed while the variable is being used .however I am looking for simpler methods here.

Nevanevada answered 14/8, 2013 at 15:51 Comment(2)
Do you expect to handle signals more often than the delay period?Wassyngton
@Wassyngton Yes My delay would be one second and the signals might be called multiple times in a secondNevanevada
U
1

I'm a bit confused by the way you phrase your question, but if you're asking how to get the timer's timeout() signal to call a function with a parameter, then you can create a separate slot to receive the timeout and then call the function you want. Something like this: -

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass(QObject *parent);

public slots:

    void TimerHandlerFunction();
    void SomeMethod(int a);

private:
    int m_a;
    QTimer m_timer;
};

Implementation: -

MyClass::MyClass(QObject *parent) : QObject(parent)
{
    // Connect the timer's timeout to our TimerHandlerFunction()
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(TimerHandlerFunction()));
}

void MyClass::SomeMethod(int a)
{
    m_a = a; // Store the value to pass later

    m_timer.setSingleShot(true); // If you only want it to fire once
    m_timer.start(1000);
}

void MyClass::TimerHandlerFunction()
{
    SomeOtherFunction(m_a);
}

Note that the QObject class actually has a timer that you can use by calling startTimer(), so you don't actually need to use a separate QTimer object here. It is included here to try to keep the example code close to the question.

Unpopular answered 14/8, 2013 at 16:2 Comment(9)
I see you are using a member variable of a class and storing the parameter. However this is going to be multithreaded. So will this way be safe ?Nevanevada
I would just caveat this solution that if SomeMethod(..) is called quicker than once a second, the value of m_a will get squashed before it can be used.Wassyngton
It can be, depending on how you use it. If you are setting and getting the member variable in different threads, then ensure you use QMutex to lock it before setting / getting. If you're passing the value between threads, then doing this via the signal slot mechanism will ensure it's thread safe.Unpopular
@Linville, very true, though without knowing the full requirements, it may not matter. If that is a problem, then the cached variable could be a QList of integers which get processed each time the TimerHandlerFunction is called.Unpopular
I dont want to use a mutex in this situation. Is there a simple mechanism for thisNevanevada
What's your reason for not using a QMutex? All you do is create a QMutex object and call mutex.lock before you set a value and mutex.unlock afterwards? That's about as simple as it gets!Unpopular
I think if I use a QT::queuedconnection that would eliminate the need of a mutex. Do think i am right ?Nevanevada
@Nevanevada asnwer is good, in addition to that, i am curios to know that. What makes you to confirm this answer is good, even your commented many queries unanswered?. I think, you misunderstood qtimer as multithreaded. Please excuse if i am wrong, and correct me.Cassel
@ashif It seems apparently that Qtimer would either require a mutex wrapped around the variable or a queued connection. I am simply using a thread now passing it the value directly. Either way the answer presented the options I had.ThanksNevanevada
W
54

Actually, there is a much more elegant solution to your question that doesn't require member variables or queues. With Qt 5.4 and C++11 you can run a Lambda expression right from the QTimer::singleShot(..) method! If you are using Qt 5.0 - 5.3 you can use the connect method to connect the QTimer's timeout signal to a Lambda expression that will call the method that needs to be delayed with the appropriate parameter.

Edit: With the Qt 5.4 release it's just one line of code!

Qt 5.4 (and later)

void MyClass::SomeMethod(int a) {
  QTimer::singleShot(1000, []() { SomeOtherFunction(a); } );
}

Qt 5.0 - 5.3

void MyClass::SomeMethod(int a) {
  QTimer *timer = new QTimer(this);
  timer->setSingleShot(true);

  connect(timer, &QTimer::timeout, [=]() {
    SomeOtherFunction(a);
    timer->deleteLater();
  } );

  timer->start(1000);
}
Wassyngton answered 14/8, 2013 at 18:11 Comment(4)
This is quite nice. Too bad you can't use lambda expression with static QTimer::singleShot().Regulate
@Roku, agreed! It's QTBUG-26406. Hopefully they'll address it soon!Wassyngton
Also I am not using C++11Nevanevada
Qt 5.4 will support QTimer::singleShot() and lambdas.Wassyngton
U
1

I'm a bit confused by the way you phrase your question, but if you're asking how to get the timer's timeout() signal to call a function with a parameter, then you can create a separate slot to receive the timeout and then call the function you want. Something like this: -

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass(QObject *parent);

public slots:

    void TimerHandlerFunction();
    void SomeMethod(int a);

private:
    int m_a;
    QTimer m_timer;
};

Implementation: -

MyClass::MyClass(QObject *parent) : QObject(parent)
{
    // Connect the timer's timeout to our TimerHandlerFunction()
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(TimerHandlerFunction()));
}

void MyClass::SomeMethod(int a)
{
    m_a = a; // Store the value to pass later

    m_timer.setSingleShot(true); // If you only want it to fire once
    m_timer.start(1000);
}

void MyClass::TimerHandlerFunction()
{
    SomeOtherFunction(m_a);
}

Note that the QObject class actually has a timer that you can use by calling startTimer(), so you don't actually need to use a separate QTimer object here. It is included here to try to keep the example code close to the question.

Unpopular answered 14/8, 2013 at 16:2 Comment(9)
I see you are using a member variable of a class and storing the parameter. However this is going to be multithreaded. So will this way be safe ?Nevanevada
I would just caveat this solution that if SomeMethod(..) is called quicker than once a second, the value of m_a will get squashed before it can be used.Wassyngton
It can be, depending on how you use it. If you are setting and getting the member variable in different threads, then ensure you use QMutex to lock it before setting / getting. If you're passing the value between threads, then doing this via the signal slot mechanism will ensure it's thread safe.Unpopular
@Linville, very true, though without knowing the full requirements, it may not matter. If that is a problem, then the cached variable could be a QList of integers which get processed each time the TimerHandlerFunction is called.Unpopular
I dont want to use a mutex in this situation. Is there a simple mechanism for thisNevanevada
What's your reason for not using a QMutex? All you do is create a QMutex object and call mutex.lock before you set a value and mutex.unlock afterwards? That's about as simple as it gets!Unpopular
I think if I use a QT::queuedconnection that would eliminate the need of a mutex. Do think i am right ?Nevanevada
@Nevanevada asnwer is good, in addition to that, i am curios to know that. What makes you to confirm this answer is good, even your commented many queries unanswered?. I think, you misunderstood qtimer as multithreaded. Please excuse if i am wrong, and correct me.Cassel
@ashif It seems apparently that Qtimer would either require a mutex wrapped around the variable or a queued connection. I am simply using a thread now passing it the value directly. Either way the answer presented the options I had.ThanksNevanevada
R
1

If you are calling SomeMethod multiple times per second and the delay is always constant, you could put the parameter a to a QQueue and create a single shot timer for calling SomeOtherFunction, which gets the parameter from the QQueue.

void SomeClass::SomeMethod(int a)
{
    queue.enqueue(a);
    QTimer::singleShot(1000, this, SLOT(SomeOtherFunction()));
}

void SomeClass::SomeOtherFunction()
{
    int a = queue.dequeue();
    // do something with a
}
Regulate answered 14/8, 2013 at 16:42 Comment(0)
J
0

That doesn't work because QTimer::start is not blocking.

You should start the timer with QTimer::singleShot and connect it to a slot which will get executed after the QTimer times out.

Jacintojack answered 14/8, 2013 at 19:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.