Qt event loop and use of QEventLoop

catalogue

1, Event loop for Qt

2, QCoreApplication main event loop

3, Start of event cycle

4, processEvents

5, QEventLoop class

6, Nesting of event loop and Simulation of synchronous call by QEventLoop

1. Get data synchronously

2. Main thread waiting

3. The dialog box pops up

1, Event loop for Qt

As a cross platform UI framework, Qt's event loop implementation principle is to encapsulate event loops of different platforms and provide a unified abstract interface. Qt has done similar work, as well as many open source libraries such as glfw and SDL.

2, QCoreApplication main event loop

In general Qt programs, there is a QCoreApplication/QGuiApplication/QApplication in the main function, and exec is called at the end.

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    //Or QGuiApplication, or QApplication
    ...
    ...
    return app.exec();
}

In the application class, after removing startup parameters, versions and other related things, the key is to maintain a QEventLoop, and the exec of application is the exec of QEventLoop. However, this EventLoop in the application is called the "main event loop". All event distribution and event handling begin here. Application also provides sendEvent and poseEvent functions to send events respectively. The event sent by sendEvent will be processed immediately, that is, "synchronous" execution. The events sent by postEvent will be added to the event queue and processed in the next round of event loop, that is, "asynchronous" execution. There is also a special sendPostedEvents, which is to immediately synchronize the events that have been added to the queue for asynchronous execution.

3, Start of event cycle

Generally, our event loop is started by exec(), for example, the following example:

 QCoreApplicaton::exec()
 QApplication::exec()
 QDialog::exec()
 QThread::exec()
 QDrag::exec()
 QMenu::exec()

These all start the event loop. The event loop is an infinite "loop" first. The program loops indefinitely in exec (), which can prevent the code following exec () from running until the program jumps out of exec (). When you jump out of exec(), the event loop is terminated. QEventLoop::quit() can terminate the event loop. The event loop is actually similar to an event queue, which processes the listed events in turn. When the time is finished but the time loop does not end, it is actually similar to a for(;;) that does not occupy CPU time Cycle. Its essence is actually to redistribute time slices in the form of queues.

4, processEvents

Our UI interface should be refreshed continuously (for QWidget, the paintEvent event event is triggered) to ensure smooth display and timely response to user input. Generally, it is necessary to have a good frame rate, such as refreshing 60 frames per second, that is, FPS 60, which is often called. After conversion, it is 1000 ms/ 60 ≈ 16 ms, that is, refreshing every 16 Ms. Sometimes we need to do some complex calculations, which take far more than 16 milliseconds. Before the calculation is completed, the function will not exit (equivalent to blocking). If the event loop is not processed in time, the UI will get stuck. In this scenario, we can use the interface provided by Qt to immediately process an event loop to ensure the fluency of the UI.

//time-consuming operation 
doWork1()
//Insert a processEvents in the appropriate position to ensure that the event loop is processed
QCoreApplication::processEvents();

//time-consuming operation 
doWork2()

5, QEventLoop class

QEventLoop is the event loop class in Qt. The main interfaces are as follows:

int exec(QEventLoop::ProcessEventsFlags flags = AllEvents)
void exit(int returnCode = 0)
bool isRunning() const
bool processEvents(QEventLoop::ProcessEventsFlags flags = AllEvents)
void processEvents(QEventLoop::ProcessEventsFlags flags, int maxTime)
void wakeUp()

Exec is the initiating event loop. After calling exec, the function that calls exec will be blocked until the while loop ends in EventLoop.

6, Nesting of event loop and Simulation of synchronous call by QEventLoop

Event loops can be nested. When in a child event loop, the events in the parent event loop are actually in an interrupted state. The events in the parent loop can be executed only after the child loop jumps out of exec. Of course, this does not mean that the interface response similar to that in the parent loop will be interrupted when executing the child loop, because most of the events of the parent loop will also be in the child loop. When executing QMessageBox::exec(), QEventLoop::exec(), although these execs() interrupt QApplication::exec() in main(), the GUI interface response has been included in the child loop, So the GUI interface can still get a response. If a child event loop is still valid, but its parent loop is forced to jump out, the parent loop will not jump out immediately, but will not jump out until the child event loop jumps out.

1. Get data synchronously

There are often scenarios where an operation is triggered, and the next step can only be carried out after the operation is completed. For example: data acquisition, after initiating a login request to the server, you must wait until you receive the data returned by the server before deciding how to execute the next step. In this scenario, if it is designed as asynchronous call, you can directly use Qt signal / slot. If it is designed as synchronous call, you can use local QEventLoop.

void A::onFinish(bool r, const QString &info)
{
   m_result = r;
   qDebug() << info;
 //Exit event loop in slot 
  loop.quit(); 
}​

bool A::get(const QString &userName, const QString &passwdHash, const QString &dataName)
 { 
    //Declare local EventLoop QEventLoop loop;
    m_result = false; 
   //Connect the signal first 
   connect(&network, SIGNAL(finished(bool,const QString &)),this,SLOT(onFinish(bool,const QString &)));
 //Initiate login request 
  getData(userName, passwdHash, dataName); 
//Start the event loop. Block the current function call, but the event loop can still run. 
//This is not going to run down to the front slot, calling loop.. After quitting, we will continue to go down
 loop.exec();
 //Return result. Before loop exits, M_ The value in result has been updated.
 return m_result;
 }

​

2. Main thread waiting

For example, if you want to wait for the main thread for 100ms, you can't use sleep, which will cause the GUI interface to stop responding, but you can avoid this by using event loop:

QEventLoop loop;
QTimer::singleShot(100, &loop, SLOT(quit()));
loop.exec();

3. The dialog box pops up

void A::Show()
{
     QDialog dlg;
     dlg.show();
     QEventLoop loop;
     connect(&dlg, SIGNAL(finished(int)), &loop, SLOT(quit()));
     loop.exec(QEventLoop::ExcludeUserInputEvents);
}

Keywords: Qt UI

Added by bacarudaguy on Thu, 09 Dec 2021 17:11:54 +0200