2
\$\begingroup\$

I needed a timer that fires in intervals for a given duration when e.g. a button is pressed. The button can be pressed several times thus I thought it would be easiest to create a new timer for each button press and the timer destroys itself when its duration passed. I wrote this...

Interface:

// timerhandler.h
class TimerHandler {
    public:
        virtual void processTimer()=0; // callback
        void startNewTimer(int interval,int duration);
        virtual ~TimerHandler(){}
};

//timerhandler.cpp
#include "timerhandler.h"
#include "mytimer.h"
void TimerHandler::startNewTimer(int interval,int duration){
    new MyTimer(this,interval,duration);
}

Usage: MainWindow inherits TimerHandler

//MainWindow.cpp
void MainWindow::on_pushButton_start_clicked() { this->startNewTimer(100,2000); }
void MainWindow::processTimer() { std::cout << "TIMER" << std::endl; }

Implementation:

//mytimer.h
#include <QObject>
#include <QTimer>
class TimerHandler;
class MyTimer : public QObject {
        Q_OBJECT
    public:
        friend class TimerHandler;
    private:
        TimerHandler* th;
        QTimer* timer;
        MyTimer(){};
        MyTimer(TimerHandler* th,int interval,int duration);
        ~MyTimer();
    public slots:
        void commitSuicide();
        void on_timer_fired();
};

//mytimer.cpp
MyTimer::MyTimer(TimerHandler* th,int interval,int duration) : th(th) {
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(on_timer_fired()));
    timer->start(interval);
    QTimer::singleShot(duration,this,SLOT(commitSuicide()));
}
MyTimer::~MyTimer(){ delete timer; }
void MyTimer::commitSuicide(){ delete this; }
void MyTimer::on_timer_fired(){ th->processTimer(); }

...it works, but I wonder if there is an easier way (less code) and it is horribly inflexible. I am a bit stuck when I want to change the code to allow different callbacks (that take different parameters).

I first tried a virtual slot as interface. I think it would work somehow, but when TimerHandler inherits QOject, then my MainWindow inherits QObject twice and I could not get it running.

Also if there is anything else that I can/should fix please let me know.

\$\endgroup\$
3
\$\begingroup\$

That's a lot of code indeed, and for all that code it is not very flexible: the "slot" name is fixed, you force multiple inheritance on your users, so they can have only one such type of timer - what if I want one to call processThing() and another to do processOtherThing?

You should use the signal/slot mechanism to your advantage - that's what it is there for, and get rid of your interface and class entirely: a single (global/static) function is enough.

Consider something like this (QTimer::singleShot itself is a good example of how you should be doing it):

void limited_timer(int interval, int duration, QObject *target, const char *slot)
{
  QTimer *timer = new QTimer;
  QObject::connect(timer, SIGNAL(timeout()), target, slot);
  QTimer::singleShot(duration, timer, SLOT(deleteLater()));
  timer->start(interval);
}

Usage (in one of your main window's methods):

limited_timer(100, 2000, this, SLOT(processTimer()));
\$\endgroup\$
  • \$\begingroup\$ Actually this is what I wanted, but I didnt how to pass the slot ;) \$\endgroup\$ – formerlyknownas_463035818 Nov 29 '15 at 16:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.