Results 1 to 7 of 7

Thread: QTimer within QThread

  1. #1
    Join Date
    Aug 2009
    Posts
    50
    Thanks
    9
    Qt products
    Qt4
    Platforms
    Windows

    Default QTimer within QThread

    I have an application where I need to have both an event loop and an explicit forever {} loop running within a QThread. An object which is created inside the forever {} loop (but only once, of course) contains a QTimer which is supposed to call one of the object's own methods when it times out.

    That's probably as clear as mud, so here is some code:

    Qt Code:
    1. class updater : public QObject
    2. {
    3. Q_OBJECT
    4. ..
    5. void startSomething()
    6. {
    7. ...
    8. if (!updateTimer)
    9. {
    10. updateTimer = new QTimer(this);
    11. updateTimer -> setSingleShot(true);
    12. updateTimer -> setInterval(UPDATE_INTERVAL);
    13. connect(updateTimer, SIGNAL(timeout()),
    14. this, SLOT(doSomething()), Qt::DirectConnection);
    15. }
    16. updateTimer -> start();
    17. }
    18.  
    19. public slots:
    20. void doSomething()
    21. {
    22. qDebug() << "Doing something";
    23. ...
    24. }
    25.  
    26. private:
    27. QTimer *updateTimer;
    28.  
    29. }
    30.  
    31. class worker : public QThread
    32. {
    33. Q_OBJECT
    34.  
    35. ...
    36.  
    37. private slots:
    38. void mainLoop()
    39. {
    40. forever
    41. {
    42. ...
    43. if (flagSet)
    44. {
    45. ud = new updater();
    46. unsetFlag();
    47. }
    48. ....
    49.  
    50. if (anotherFlagSet)
    51. {
    52. ud -> startSomething();
    53. unsetTheOtherFlag();
    54. }
    55. }
    56. }
    57.  
    58. private:
    59. void run()
    60. {
    61. {
    62. QTimer trigger;
    63. connect(&trigger, SIGNAL(timeout()),
    64. this, SLOT(mainServerLoop()), Qt::DirectConnection);
    65. trigger.setSingleShot(true);
    66. trigger.setInterval(0);
    67. trigger.start();
    68.  
    69. exec();
    70. }
    71.  
    72. updater *ud;
    73. }
    To copy to clipboard, switch view to plain text mode 

    The reason for such an obtuse structure is that the 'worker' QThread needs to control its own queue of requests for things it should work on, as the main thread sometimes sends it requests which, if not executed within a certain time, become obsolete and need to be deleted from the queue before new requests are sent. I realise that, were it not for this, I could just send requests to it via queued signals and everything would be much simpler.

    Anyway, the reason for having the exec() command in the run() routine, as well as the forever{} loop, is to provide an event loop so that, if I understand correctly, the QTimer which belongs to the 'ud' object (of class 'updater') can work properly.

    My problem, however, is that the QTimer which belongs to the 'ud' object does not, in fact, work properly; at least not all the time. The very first time that 'startSomething' is called, it works just as it is supposed to, but on subsequent occasions it doesn't seem to work at all. I have tried commenting-out the "updateTimer -> setSingleShot(true);" line, and putting "updateTimer -> stop()" into 'doSomething()' instead, and that makes no difference.

    Can anyone help me to see what may be going wrong?

    Thank you,
    Stephen.

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTimer within QThread

    You don't need the forever loop. You can always substitute it with a timer with a 0 timeout. I can't help you more based on what you posted because your code doesn't make much sense and it's hard to guess what you really wanted.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  3. #3
    Join Date
    Aug 2009
    Posts
    50
    Thanks
    9
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer within QThread

    Thank you for taking a look at this; unfortunately the actual code is intrinsically complex and I guess I haven't been successful in simplifying it for the question.

    I don't quite understand how the forever{} loop can be substituted by a timer with a 0 timeout; can you point me to an example of this? I know about the trick of using a zero-second timer to manage GUI responsiveness in a single-thread application, but I expect you mean something different.

    I'm using the forever{} loop because when I wrote this code I followed the Mandelbrot example in the Qt documentation, which is quite close to what I'm actually doing here. All I'm really trying to do is to add a single-shot QTimer within the rendering thread, so that the image updates itself after a certain interval. To be really precise, what I'm trying to do is to add a single short QTimer within the calculation thread which causes the image to update itself a certain time after the user stops interacting with the GUI; in other words, if the user does something with the GUI while the QTimer is still counting, the QTimer is meant to reset itself back to zero and start counting again.

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTimer within QThread

    Quote Originally Posted by Eos Pengwern View Post
    I don't quite understand how the forever{} loop can be substituted by a timer with a 0 timeout; can you point me to an example of this? I know about the trick of using a zero-second timer to manage GUI responsiveness in a single-thread application, but I expect you mean something different.
    It's quite the same. The only difference is you want your "worker thread" to be "responsive" and not your "GUI thread".

    The code you posted can be changed to:
    Qt Code:
    1. class Loop : public QObject {
    2. Q_OBJECT
    3. public slots:
    4. void loopIteration() {
    5. ...
    6. if (flagSet)
    7. {
    8. ud = new updater();
    9. unsetFlag();
    10. }
    11. ....
    12.  
    13. if (anotherFlagSet)
    14. {
    15. ud -> startSomething();
    16. unsetTheOtherFlag();
    17. }
    18.  
    19. }
    20. };
    21.  
    22. QThread thread;
    23. thread.start();
    24. QTimer timer;
    25. Loop loop;
    26. loop.moveToThread(&thread);
    27. connect(&timer, SIGNAL(timeout()), &loop, SLOT(loopIteration());
    28. timer.start(0);
    29. timer.moveToThread(&thread); // optional
    30.  
    31. ServerLoop server;
    32. server.moveToThread(&thread);
    33. QTimer intervalTimer;
    34. connect(&intervalTimer, ..., &server, ...);
    35. intervalTimer.start(INTERVAL);
    36. intervalTimer.mvoeToThread(&thread); // optional
    To copy to clipboard, switch view to plain text mode 

    But your code can be simplfied even more because your forever loop does nothing unless some flag is set. So you can implement slots for setting the flags that will do the task when they are requested.

    Qt Code:
    1. class DoItAll : public QObject {
    2. Q_OBJECT
    3. private slots:
    4. void createUpdater() {
    5. ud = new updater;
    6. }
    7. void executeStartSomething() {
    8. if(!ud) return;
    9. ud->startSomething();
    10. }
    11. private:
    12. ...
    13. };
    14.  
    15. QThread thread;
    16. thread.start();
    17. DoItAll inASimpleWay;
    18. connect(..., ..., &inASimpleWay, SLOT(createUpdater()));
    19. connect(..., ..., &inASimpleWay, SLOT(startSomething()));
    20. inASimpleWay.moveToThread(&thread);
    To copy to clipboard, switch view to plain text mode 

    and that's it. Instead of the two connect() statements you can use QMetaObject::invokeMethod() to trigger each of the methods on demand. It can even be a public method of the class itself:
    Qt Code:
    1. void DoItAll::raiseUpdaterFlag() {
    2. QMetaObject::invokeMethod(this, "createUpdater", Qt::QueuedConnection);
    3. }
    To copy to clipboard, switch view to plain text mode 

    Then you can call it from any thread and it will work as expected.
    Qt Code:
    1. inASimpleWay.raiseUpdaterFlag();
    To copy to clipboard, switch view to plain text mode 
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  5. #5
    Join Date
    Aug 2009
    Posts
    50
    Thanks
    9
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer within QThread

    I think I'm slowly beginning to catch on... As it happens, I'm about to grab a few hours' sleep and then catch an 11-hour flight, but if my battery holds out I'll have a close look at this during the flight and, hopefully, home in on the solution.

    Thank you again for your help.

  6. #6
    Join Date
    Aug 2009
    Posts
    50
    Thanks
    9
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer within QThread

    Fortunately my seat had a socket (ANA are good!), and by the time I had refactored my class without the forever{} loop, according to your second suggestion (using QMetaObject::invokeMethod as well) I'd got as far as St Petersburg (en route from Tokyo to London).

    Anyway, it works now; the QTimer events within the thread are behaving just as they should.

    I found two difficulties with QMetaObject::invokeMethod which added considerably to the size of the task:
    - I found that the methods invoked did in fact need to be declared as actual slots: it wouldn't work if they were ordinary public methods.
    - most of my methods took pointers as arguments, and QMetaObject::invokeMethod didn't seem to like that. I had to change things around to use const references instead. I also had be to be really careful to declare my custom structures with qRegisterMetaType, just like I would for a queued signal-slot connection.

    Now, though, everything is working, so thank you very much.

  7. #7
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QTimer within QThread

    Quote Originally Posted by Eos Pengwern View Post
    - I found that the methods invoked did in fact need to be declared as actual slots: it wouldn't work if they were ordinary public methods.
    Yes. Or they have to be accompanied by the Q_INVOKABLE macro.

    - most of my methods took pointers as arguments, and QMetaObject::invokeMethod didn't seem to like that. I had to change things around to use const references instead. I also had be to be really careful to declare my custom structures with qRegisterMetaType, just like I would for a queued signal-slot connection.
    If you wanted all that to work across threads, you'd have to eventually do it anyway.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


Similar Threads

  1. qthread / qtimer
    By Galen in forum Qt Programming
    Replies: 5
    Last Post: 17th April 2010, 22:57
  2. QThread and QTimer
    By sivrisinek in forum Qt Programming
    Replies: 4
    Last Post: 30th April 2009, 16:41
  3. QThread & QTimer
    By hosseinyounesi in forum Qt Programming
    Replies: 5
    Last Post: 13th April 2009, 08:22
  4. QTimer and QThread
    By TheKedge in forum Qt Programming
    Replies: 4
    Last Post: 21st September 2006, 14:52
  5. Qthread n QTimer Problem
    By quickNitin in forum Qt Programming
    Replies: 5
    Last Post: 8th June 2006, 14:12

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.