PDA

View Full Version : How to use multiple instances of a class containing QTimer?



nhungr
20th July 2012, 11:11
Hello,
I have written two classes, one of which uses QTimer. My first class creates a QVector of my second class and in each instance of the second class, a QTimer is started in order to increment a counter periodically until it reaches a maximum. In other words, I have 5 timers running each in their respective class instance. I have no compile or execution errors but the timer's slot never seems to be called. I guess this means that the timeout signal of each qtimer is not called either... why? Are the QTimers out of scope before the timeout signal can be sent? I don't see why, as all instances of the classes remain active throughout... I have seen the comment on using QTimer in multiple threads here (http://doc.qt.nokia.com/4.7-snapshot/qtimer.html#details), except that I'm not using threads. Does anyone have any idea where I'm going wrong? Thanks very much in advance. My simplified code is as follows:

.h of my first class:


#include <QVector>

class MyFirstClass {
public:
void Initialize();
void Run();
QVector<QSharedPointer<MySecondClass> > mMySecondClass;
}

.cpp of my first class:


void MyFirstClass::Initialize() {
mMySecondClass.resize(5); // Create 5 instances of second class
for (i = 0; i<mMySecondClass.size(); ++i) { // Initialize all 5 instances
mMySecondClass[i] = QSharedPointer<MySecondClass>(new MySecondClass);
mMySecondClass[i]->Initialize();
}
}

void MyFirstClass::Run() {
for (i = 0; i<mMySecondClass.size(); ++i) { // Call Run for all 5 instances of second class
mMySecondClass[i]->Run();
}
}

.h of my second class:


#include <QTimer>

class MySecondClass: public QObject {
Q_OBJECT
public:
void Initialize();
void Run();

protected slots:
void TimerSlot(); // slot connected to timeout of QTimer

protected:
QTimer *mMyTimer;
int mCounter;
}

.cpp of my second class:


void MySecondClass::Initialize() {
mMyTimer= new QTimer();
}

void MySecondClass::Run() {
mCounter = 0; // initialize counter
connect(mMyTimer, SIGNAL(timeout()), this, SLOT(TimerSlot())); // connect timer signal to slot
mMyTimer->start(5); // start timer
}

void MySecondClass::TimerSlot() {
mCounter += 1; // increment counter
if (mCounter > 10) { // stop timer when counter reaches 10
mMyTimer->stop();
}
}

Example implementation:


MyFirstClass foo;
foo.Initialize();
foo.Run();

wysota
20th July 2012, 11:29
QVector::resize() will not create instances of your class. It will create instances of QSharedPointer.

nhungr
20th July 2012, 11:47
Oops yes, you're right, I have to call new. I forgot a line in the code above. I've added it as line 4 in the code above for for the .cpp of my first class. The problem remains though... Any ideas?
Thanks.

wysota
20th July 2012, 11:55
Are you able to prepare a minimal compilable example reproducing the problem? By the way, your code is leaking memory.

high_flyer
20th July 2012, 16:15
Where do you initialize your 'mCounter'?
And how do you know if your slot has been called or not?

yeye_olive
20th July 2012, 16:52
Let me add a couple of questions:
- QTimer needs an event loop to be running for events to be delivered; can you confirm that an event loop is running at some point between the initialization of the MyFirstClass instance (allocation, Initialize() and Run()) and its destruction?
- On a side note: why do you use Initialize() methods instead of constructors, thus running the risk of using objects in an uninitialized state?

nhungr
23rd July 2012, 12:53
Thanks for the useful hints! The problem did indeed come from the fact that I didn't have an events loop running... which means my qtimer::connect events didn't get processed. So I need to make a call to QCoreApplication::exec(), as shown here (http://thisthread.blogspot.fr/2011/04/qcoreapplication-says-hello.html)for example. Although it works now, it's made me run into another conceptual problem: is there any way of interrupting the qtimers prematurely (by pressing a key on the keyboard for example) since the application now requires the exec routine to terminate before moving on?

amleto
23rd July 2012, 13:11
yes, there is a way to stop a timer
QTimer#stop

yeye_olive
23rd July 2012, 14:01
Now that your application has an event loop, you indeed need to call QCoreApplication::exit() or QCoreApplication::quit() for the program to exit. If I understand correctly, currently your application runs in a console, sets up some timers, some functions are called when they time out, and at some point QCoreApplication::quit() is called. Now you would also like your application to exit immediately when a certain key is pressed without waiting for the above scenario to complete. Is that correct?

If it is the case, then you do not need to stop the timers. What you need is a way to handle some event (key press, or termination request from the OS) and make sure that QCoreApplication::quit() gets called. This is not trivial (and unfortunately platform-specific), but it may not be necessary. In its current state, your program is immediately killed if you hit Ctrl+C as it (currently) ignores the termination request. Of course this ends the program brutally but it is OK if no cleanup is required. It is definitely something to avoid if e.g. your program may be in the middle of writing to some files. So the main question is: is it acceptable if your program gets killed without having the chance to run some cleanup code?

nhungr
24th July 2012, 10:49
Yes, that's exactly what I need. However, instead of exiting the application entirely, I want it just to exit the events loop and then continue with the rest of the code in my main loop. In other words. is there a way of polling inside the Qtimer slot whether a key has been pressed and stopping the timer in consequence? Once the last timer has been stopped, quit the events loop and go on with the rest of the code?
Perhaps my approach of using qtimers is not a good approach? Is it worthwhile looking into using QThreads instead?
Thanks very much.

amleto
24th July 2012, 12:37
to deal with key press events you would normally have a gui and be using widgets. Otherwise you are either dealing with non-standard c++ headers or doing things like peek (http://www.cplusplus.com/reference/iostream/istream/peek/)or cin.get.


I don't know if QCoreApplication::winEventFilter could help you whilst not using any gui to handle key presses

wysota
24th July 2012, 12:39
Usually it's possible to monitor stdin using QSocketNotifier but it requires to disable buffering for stdin.