Results 1 to 6 of 6

Thread: QTimer not working in new project

  1. #1
    Join Date
    Jun 2012
    Posts
    219
    Thanks
    28
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default QTimer not working in new project

    I'm having trouble getting QTimer to work in a console (no GUI) application.

    When I start the timer, it goes active, but never times out.

    When I tried using the static QTimer::singleShot method, I get a runtime error:

    QObject::startTimer: QTimer can only be used with threads started with QThread
    I've tried explicitly using Qt::QueuedConnection for the connection to the slot that tries to start the timer, but that makes no difference (and I think that's the default anyway).

    Maybe the signal that's connected to the slot that starts the timer is on a different thread than what instantiated the QObject. The signal is emitted from an interrupt handler registered by the wiringPiISR() function provided by the wiringPi package.

    Here's the code:

    powerControl.h
    Qt Code:
    1. #ifndef POWERCONTROL_H
    2. #define POWERCONTROL_H
    3. #define GPIO3 5
    4. #include <QObject>
    5. #include <QTimer>
    6. #include <QDebug>
    7.  
    8. class PowerControl : public QObject
    9. {
    10.  
    11. Q_OBJECT
    12. public:
    13. explicit PowerControl(QObject *parent = 0);
    14.  
    15. static PowerControl *getInstance() {return instance;}
    16.  
    17. static void handleEdgeBoth(void);
    18.  
    19. virtual void handleIrq(bool afterEdgeState);
    20. ~PowerControl();
    21.  
    22. private:
    23. static PowerControl *instance;
    24. QTimer debounceTimer;
    25. QTimer tapTimer;
    26. QTimer powerOffTimer;
    27. static unsigned long lastTime;
    28.  
    29. signals:
    30. void fallingEdge(void);
    31. void risingEdge(void);
    32. void buttonPushed(void);
    33. void tapDetected(void);
    34. void longPressDetected();
    35.  
    36. public slots:
    37. private slots:
    38. void edgeDetected(bool);
    39. void bounceFinished(void) {qDebug() << "Bounce timeout";}
    40. void tapTimeout(void) {qDebug() << "tap timeout";}
    41.  
    42.  
    43. #define DEBOUNCE_TIME 10
    44. };
    45.  
    46. #endif // POWERCONTROL_H
    To copy to clipboard, switch view to plain text mode 

    powerControl.cpp
    Qt Code:
    1. #include "powercontrol.h"
    2. #include "wiringPi.h"
    3. #include <QDebug>
    4.  
    5. PowerControl *PowerControl::instance;
    6. unsigned long PowerControl::lastTime = 0;
    7.  
    8. PowerControl::PowerControl(QObject *parent) : QObject(parent)
    9. {
    10. debounceTimer.setSingleShot(true);
    11. debounceTimer.setInterval(DEBOUNCE_TIME);
    12. tapTimer.setSingleShot(true);
    13. tapTimer.setInterval(1000);
    14. powerOffTimer.setSingleShot(true);
    15.  
    16. wiringPiISR(3,INT_EDGE_BOTH,handleEdgeBoth);
    17. //wiringPiISR(3,INT_EDGE_RISING,handleEdgeRisingInterrupt);
    18.  
    19. connect(&tapTimer,SIGNAL(timeout()),this,SLOT(tapTimeout(void)),Qt::QueuedConnection);
    20. instance=this;
    21. }
    22. void PowerControl::handleEdgeBoth()
    23. {
    24. PowerControl *pc = getInstance();
    25. unsigned long now = millis();
    26. unsigned long elapsedTime = now - lastTime;
    27. lastTime = now;
    28.  
    29.  
    30. qDebug() << "Elapsed time =" << elapsedTime;
    31.  
    32. if (elapsedTime < DEBOUNCE_TIME)
    33. {
    34.  
    35. qDebug() << "key bounce detected\n";
    36. return;
    37. }
    38. else
    39. emit pc->edgeDetected(digitalRead(3));
    40.  
    41.  
    42.  
    43. }
    44. void PowerControl::handleIrq(bool afterEdgeState)
    45. {
    46. if (afterEdgeState)
    47. qDebug() << "Rising Edge Detected";
    48. else
    49. qDebug() << "Falling Edge Detected";
    50. }
    51. void PowerControl::edgeDetected(bool edge)
    52. {
    53. if (edge)
    54. {
    55. qDebug() << "Rising Edge Detected";
    56. tapTimer.stop();
    57. }
    58. else
    59. {
    60. QTimer::singleShot(1000, this, SLOT(tapTimeout(void)));
    61.  
    62. tapTimer.start();
    63. qDebug() << "Falling Edge Detected, timer isActive = " << tapTimer.isActive();
    64. }
    65. }
    66.  
    67. PowerControl::~PowerControl()
    68. {
    69.  
    70. }
    To copy to clipboard, switch view to plain text mode 

    Hardware is Rpi Zero W, Qt 4.8 running on Stretch Lite

    Thanks!

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QTimer not working in new project

    First comment - if you want PowerControl to be a Singleton, you need to make the constructor and destructor private. Otherwise, anyone can create a new, separate instance of PowerControl simply by using operator new(). And anyone could delete your "instance" simply by calling operator delete() on it, leaving you with a dangling pointer to a deleted object.

    You haven't shown how you are using this class, but my hunch is that you are starting the timer before you have an event loop running (that is, before QApplication::exec() has been called in main()).
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  3. #3
    Join Date
    Jun 2012
    Posts
    219
    Thanks
    28
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer not working in new project

    Thanks for the reply!

    Yes, you are correct about the Singleton. It's ok to be a bit "sloppy" on this project , since it's so simple and I'm the only one who's ever going to work on it. That's my "excuse", but still, there's no reason not to do as you suggest!

    No, the timer doesn't get started before exec is called. It gets started in the "edgeDetected" slot, which gets called as a result of an signal being emitted in the interrupt handler. And, the handler only gets called when I push a hardware button.

    Here's the main.c (and that's ALL the code):

    #include <QCoreApplication>


    #include "powercontrol.h"
    #include "ledcontrol.h"
    int main(int argc, char *argv[])
    {
    QCoreApplication a(argc, argv);

    //QApplication a(argc, argv);

    a.setOrganizationName("Flying Nerd");
    a.setApplicationName("Ipad2GMA");

    wiringPiSetupSys();
    PowerControl pc;
    //LedControl *lc = new LedControl(1000,1000,2,true);


    return a.exec();
    }

  4. #4
    Join Date
    Mar 2009
    Location
    Brisbane, Australia
    Posts
    7,729
    Thanks
    13
    Thanked 1,610 Times in 1,537 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Wiki edits
    17

    Default Re: QTimer not working in new project

    If the interrupt fires (expected or unexpected) between line 16 and 19 while your constructor is executing then you can be starting the timer before the event loop is running. At that time, instance will be uninitialized to boot.

    Exactly what debugging output, if any, do you see?

  5. #5
    Join Date
    Jun 2012
    Posts
    219
    Thanks
    28
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer not working in new project

    "if the interrupt fires (expected or unexpected) between line 16 and 19"
    It doesn't. The exec statement is being executed before the timer is started.

    Again, the interrupt occurs only when a button is pushed. Which doesn't happen until LONG after the exec statement is executed.

    The qDebug statements in the posted code are what I'm using for debugging. When the interrupt handler executes, there are print statements (see posted code). If the timer actually runs, then the tapTimer slot will print using the QDebug (it doesn't).

    The qDebug statement in the edgeDetected slot:

    [code]qDebug() << "Falling Edge Detected, timer isActive = " << tapTimer.isActive(); [/quote]

    indicates the timer is active after it's started. I see this print every time the putton is pushed. And I see the
    Rising Edge Detected
    when the button is released.

    But, the tapTimer never expires (the connected slot never executes). And, I get the strange message when I use the static singleShot() method to start a different timer (just as a test).

    To be absolutley sure, I put a a qDebug() statement immediately preceding the a.exec() statement
    Qt Code:
    1. qDebug("exec statment");
    To copy to clipboard, switch view to plain text mode 
    in main.c. It prints and that output is the ONLY output that's seen until I push the button.

    I'm thinking this might be a build issue. Perhaps a run time library on Stretch (on the rpi) is incompatible with a binary I'm using on the build machine for linking. Seems unlikely that it would link fine but not run correctly, but that's the only guess I have right now.

    I'm going to try an even more trivial test case to see if QTimer will work at all in an executable built from my "tried and true" dev environment.


    Added after 27 minutes:


    I'm going to try an even more trivial test case to see if QTimer will work at all in an executable built from my "tried and true" dev environment.
    Actually, I'm not sure how to this in a console application. I think I need to have a QWidget to even detect a keybutton push.

    Maybe QTimer will only work in a QApplication with widgets, not in a QCoreApplication that only has a console?


    Added after 15 minutes:


    Actually, using the static QTimer::singleShot method in main.c works, even though it preceeds the exec(). Here's the corresponding main.c:
    Qt Code:
    1. #include <QCoreApplication>
    2.  
    3. #include "powercontrol.h"
    4. #include "ledcontrol.h"
    5.  
    6. int main(int argc, char *argv[])
    7. {
    8. QCoreApplication a(argc, argv);
    9.  
    10. //QApplication a(argc, argv);
    11.  
    12. a.setOrganizationName("Flying Nerd");
    13. a.setApplicationName("Ipad2GMA");
    14.  
    15. wiringPiSetupSys();
    16. PowerControl pc;
    17. //LedControl *lc = new LedControl(1000,1000,2,true);
    18.  
    19. QTimer::singleShot(1000, &pc, SLOT(tapTimeout(void)));
    20. qDebug("exec statment");
    21.  
    22. return a.exec();
    23. }
    To copy to clipboard, switch view to plain text mode 

    The tapTimeout slot executes about 1 second after starting the program -- the qDebug print is seen. Yet, I still get the message:

    QObject::startTimer: QTimer can only be used with threads started with QThread
    when the singleShot method is executed in the slot associated with the interrupt handler.

    It seems like that error message is telling me the slot is running in a thread that wasn't "started with QThread". But, I didn't explicitly start any thread. Probably, code in the wiringPi library that implements the wiringPiISR function sets up creation of a new thread for the registered interrupt handler.

    Maybe I have to change thread affinity of the slot that I connect to my signal handler? How would I do this?


    Added after 32 minutes:


    I added prints of currentThreadId and found that the slot is indeed running in a different thread than what I see from a currentThreadId print in main.c or in the constructor of the PowerControl object.

    I tried using moveToThread(QCoreApplication::instance()->thread()) in the constructor of PowerControl and also in the slot. I also passed the Thread * from main.c to the constructor and passed that to the moveToThread method instead of QCoreApplication::instance()->thread(). Still, the slot is running on a different thread.

    Why does the slot run in a different thread than the constructor? Why doesn't the moveToThread seem to work?


    Added after 15 minutes:


    Another thread with similar issue:

    http://www.qtcentre.org/threads/15701-QTimer-problem

    So, maybe QTimer can't be used if code in libraries like wiringPi use their own threading mechanisms?
    Last edited by davethomaspilot; 19th May 2018 at 15:25.

  6. #6
    Join Date
    Jun 2012
    Posts
    219
    Thanks
    28
    Thanked 3 Times in 3 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QTimer not working in new project

    SOLVED

    Ok, I researched moveToThread and found that it doesn't make all methods of the object run on the specified thread-- just slots invoked via signals connected with connectionType of Qt::QueuedConnection.

    I was calling the slot directly from the static handler. The I probably could have emitted a signal to invoke a slot from the handler, but instead I used invokeMethod with a Qt::QueuedConnection to invoke the edgeDetected slot.

    Now, the timer is working as expected!

Similar Threads

  1. I am working on a POS project
    By foxrider in forum Newbie
    Replies: 12
    Last Post: 21st April 2014, 15:48
  2. Replies: 7
    Last Post: 24th September 2012, 08:17
  3. Qtimer is not working....
    By k.qasempour in forum Newbie
    Replies: 3
    Last Post: 21st June 2012, 09:16
  4. QTimer - Why this is not working?
    By prasenjit in forum Newbie
    Replies: 2
    Last Post: 17th December 2009, 17:37

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.