Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Understaning when Qt's Event Loop processes events



  • So I created a test application to figure out more about how Qt's Event loops is working. So I have a class called testclass and this class objective is add a message to the que every 100ms. Then the class simulates sending a message outbound by simulating the wait process. For testing purpose I wait for a lone time 2 seconds. During that wait period I would expect the queue would be getting filled because every 100ms I should be adding a message but this does not happen msgs dont get added to the queue until my simulated wait is complete. So I am confused on when Qt processes its event loop.. How can a timers timeout slot get handled if something else is being processed. When does the event loop decide that its okay to process an event in between function calls? I am just misunderstanding how this process works and any help would be greatly appreciated.

    testclass.h

    #define TESTCLASS_H
    
    #include <QObject>
    #include <QTimer>
    #include <QSemaphore>
    #include <QQueue>
    class testclass : public QObject
    {
        Q_OBJECT
    public:
        QTimer secondMsgTimer;
        QQueue<QString> msgqueue;
        explicit testclass(QObject *parent = nullptr);
        void addMsgToSend(QString msg);
        void sendMsg();
        void wait();
    private slots:
        void secondMsgTimerHandler();
    public slots:
        void init();
    
    };
    
    #endif // TESTCLASS_H
    

    testclass.cpp

    #include "testclass.h"
    #include <QDebug>
    #include <QThread>
    #include <QCoreApplication>
    
    QSemaphore semaphore(1);
    int msgcount = 0;
    testclass::testclass(QObject *parent) : QObject(parent)
    {
     connect(&secondMsgTimer, SIGNAL(timeout()), SLOT(secondMsgTimerHandler()));
    }
    
    // adds msg to the que
    void testclass::addMsgToSend(QString msg)
    {
        msgqueue.enqueue(msg);
        sendMsg();
    
    }
    void testclass::sendMsg()
    {
        semaphore.acquire();
        QString msg = msgqueue.dequeue();
        //simulate sending a command and waiting for response. I want to be blocking here
        //because in my real code I need to wait for the results of the msg to continue
        qDebug() << "sending msg" << msg;
        wait();
        qDebug() << "got rsp for msg= " << msg;
        semaphore.release();
    }
    
    void testclass::wait()
    {
        //simulate waiting
        QThread::msleep(2000);
    }
    
    void testclass::secondMsgTimerHandler()
    {
        // add a msg to the que every 100ms but this does not happen..
        // this slot is only fired after the wait() function completes which is every 2 seconds
        // why is this? what am I doing wrong? why does the event loop not process events during that 2 seconds
        msgcount++;
        addMsgToSend(QString("This is msg %1").arg(msgcount));
    }
    
    // initilize test class once the event loop is running.
    void testclass::init()
    {
         secondMsgTimer.start(100);
    }
    
    

    main.cpp

    #include <QCoreApplication>
    #include <QTimer>
    #include "testclass.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        //creat test class
        testclass t(&a);
        // init the test class once the event loop is running
        QTimer::singleShot(0,&t,SLOT(init()));
        return a.exec();
    }
    
    
    
    
    
    


  • @pjorourke05

    QThread::msleep(2000);
    

    This puts the thread to sleep, so it does nothing during that time. Unless you use separate threads in your code --- which you do not --- you can't use this.

    //because in my real code I need to wait for the results of the msg to continue
    wait();

    Try not to organize things this way. When the response arrives, act on it then (signals & slots). If you realyy have to wait, look at QEventLoop::exec(), not sleep.



  • @JonB To clarify.. your stating I am blocking the event loop with my wait() function which inst allowing the timer's timeout signal to fire. What I should do is send the messages async then use a slot to handle when the results of the message are received Which means any logic that relies on the results of a message would go into that slot.



  • @pjorourke05
    Yes, sleep is sleep, and sleeping things (including threads) just don't do anything other than sleep :)

    Yes, Qt programming is set up for you to write to do stuff in slots which get called when signals are emitted. You probably ought slowly read through https://doc.qt.io/qt-5/signalsandslots.html, it's worth spending some time on this initially.

    How can a timers timeout slot get handled if something else is being processed. When does the event loop decide that its okay to process an event in between function calls?

    It doesn't work like this. The events/event loops are not being dealt with "between function calls". There are no interrupts going on in Qt. Think of it as the event loop is in charge and calling things:

    repeat
        process an event message in the queue, calling slots
    until something said to quit
    

    You can send your own event messages into the queue via emit of a signal, and the system is doing the same.

    If one of your slots takes a long time without exiting, the next message in the queue doesn't get processed.



  • @JonB Ok thanks.. that is where I was headed but didnt know if I was missing something because I am new to Qt.

    It seems like a good rule of thumb is to use signals and slots to connect the dots but if you have a long running operation than you need to use another thread.

    Also would this be a true statement. If you are currently executing a slot call it (slotA) and a timer times out which needs to invoke slotB. slotB will not be invoked until slotA is complete.. which means the way your application is designed could overlap execution with moment the timer's timeout slot (slotB) is suppose to execute meaning your timers timeout would not fire accurately. The way to avoid this is to use threads.



  • @pjorourke05
    Yes.

    A time expiring event is just an event like all the others. It generates a message into the queue like everything else does, and that only gets processed (your slot gets called) when it's reached in the conceptual event loop above. Don't think of it as anything "interrupting" the code that's running. Yes, slotB won't get executed until slotA has exited.

    If you have a computation-heavy operation, or something which really must block/wait, you might put these in a separate thread. In particular, a separate thread from the main one servicing the GUI/timers, so that those continue to respond to the user in real time.


Log in to reply