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.

Rajeshwar
  • 11,179
  • 26
  • 86
  • 158

4 Answers4

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);
}
Linville
  • 3,613
  • 1
  • 27
  • 41
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.

mr NAE
  • 3,144
  • 1
  • 15
  • 35
TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • 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 ? – Rajeshwar Aug 14 '13 at 16:06
  • 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. – Linville Aug 14 '13 at 16:07
  • 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. – TheDarkKnight Aug 14 '13 at 16:08
  • @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. – TheDarkKnight Aug 14 '13 at 16:10
  • I dont want to use a mutex in this situation. Is there a simple mechanism for this – Rajeshwar Aug 14 '13 at 16:16
  • 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! – TheDarkKnight Aug 14 '13 at 16:20
  • I think if I use a QT::queuedconnection that would eliminate the need of a mutex. Do think i am right ? – Rajeshwar Aug 14 '13 at 16:34
  • @Rajeshwar 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. – Ashif Aug 14 '13 at 17:43
  • 1
    @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.Thanks – Rajeshwar Aug 14 '13 at 18:02
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
}
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.

segfault
  • 5,759
  • 9
  • 45
  • 66