Lately I’ve been working with Qt, a C++ framework for developing cross platform applications. Like many other frameworks/environments Qt enables event-driven programming. Using that methodology of programming, a typical Qt applications does something like this:
- Setup the UI, start listening in socket.
- Connect objects to react to: user manipulating the UI, connection happening in socket.
- Calls app.exec().
That’s it. Then it’s job of app to act like a dispatcher, waiting for external world events to happen and propagating them to the application objects. Qt uses QEvent to propagate those events.
Well, this is common in other frameworks. In some of them the word mainloop or eventloop is used to describe the job that app in Qt is doing. For example: EFL has the ecore mainloop, glib has a mainloop and Node.js has this concept built in as well.
In this article I’ll focus mostly on Qt machinery to work with mainloops.
Underneath app
One interesting aspect of the Qt implementation is that the concept of mainloop is abstracted, so other parts of Qt don’t heavily depend on how the mainloop is implemented. This supports Qt’s goal of being crossplatform. So the real class that implements a mainloop in Qt is not the QApplication, but another platform dependent class that implements the interface defined by QAbstractEventDispatcher.
This abstract class is the minimum interface that QApplication needs to provide the event-driven programming for its users. This allows different implementations for each platform, and even multiple implementations on the same platform.
In Unix, for example, besides the default dispatcher implemented using POSIX API, you can use an event dispatcher implemented on top of glib mainloop. And this gives you something very nice: your program can make use of libraries that rely on the glib mainloop as well. This kind of integration has been done in other libraries as well, for example with ecore on top of glib.
When you instantiate QApplication, Qt will automatically create a proper event dispatcher for the platform and use it. However if you instantiate one before, QApplication will use it. But before we play with that, let’s look at event dispatchers a little bit more.
What can an event dispatcher do?
Well, if we look at QAbstractEventDispatcher, we’ll see the following groups of methods:
- Timer: to register/unregister timers, so the mainloop will send a QTimerEvent to an object after certain time.
- Socket notifier: in Unix-world this would be registering/unregistering file descriptors to watch. The mainloop will send a QSockAct type of event to the socket notifier object.
- Control: to run one iteration of the mainloop, to enable another thread wake the mainloop if it is idle waiting for new external events.
If you’re familiar with the select() function from POSIX, you can imagine how this functionality can be implemented. Running an iteration of the mainloop would be the select() call for example. Actually, that’s the way the default unix event dispatcher works in Qt.
With such an interface, it’s easy to see that Qt could run on top of most mainloops, and for this article I wrote a simple QEventDispatcherEcore, which implements this interface on top of EFL’s ecore mainloop.
Show me the code!
The code is in my playground repository, and the important file for now is the dispatcher.cpp. You’ll see that it is all marked with ### comments, which indicates points of the code that are incomplete: this is just a simple example of the concept, be warned!
I won’t go step by step in the code, because it should be not so hard to understand. The Ecore library gives us enough functionality to implement all Qt needs. I’ll highlight two pieces that I think deserve special explanation
First, how the wakeUp() call was implemented: since ecore mainloop didn’t have a similar functionality, I used the same trick as the Unix event dispatcher. The trick is to create a pipe and watch one of the ends inside the mainloop. Then, to wake up we simply write on the other end of the pipe.
A second important point is: the processEvents() function takes a flags argument, which indicates to the event dispatcher important parameters about the iteration it is going to run: whether it should ignore UI events or whether it should block waiting for the next event if there’s no event right now.
The block until event show up is a very important functionality, since we don’t want to spend all CPU cycles calling processEvents() again and again with no side effect, no new event being delivered, and avoid busy loop.
In the code for the event dispatcher example, what’s tricky is that while Ecore does have a iterate() primitive it doesn’t have a iterate_and_wait(). The rationale is that part of the work that Qt is doing here inside QApplication code
// Simplified version of what QApplication do
while (!quitLoop)
processEvents(flagToWait);
is something that usually libraries encapsulate for the user. In our case has a mainloop_begin() function to encapsulate the while-loop.
To the event dispatcher example, what I did was to simulate the functionality I wanted by making use of Ecore’s idle_exiter mechanism, which allow functions to be registered to be called after a iteration the mainloop
// Simplified version of what Ecore do...
while (!quitLoop) {
// ...
processEvents(flagToWait);
// ...
runIdleExiters();
}
The trick was to register an idle_exiter that caused the mainloop to quit. After inspecting Ecore implementation, the result of doing this be exactly what I wanted to achieve with an iterate_and_wait() primitive without changing the API.
Hmmm, what’s the guidispatcher?
If you followed the repository link, you’ll see there’s another dispatcher there in the guidispatcher.cpp file, that’s the QEventDispatcherEcoreGUI. To understand it we need to remember the difference between a QCoreApplication and a QApplication: the former doesn’t depend on GUI code, and the latter is the subclass with GUI functionality.
This separation also happens in event dispatchers. Using the case of Unix again, in Qt the GUI for Unix is based on X11. So a QApplication needs to create a X11 connection and watch for events, translate them to QEvents and send them to the proper objects.
This job is exactly what an event dispatcher is for. So in Unix, QCoreApplication uses the Unix’ select() implementation we mentioned before, while QApplication uses a subclass of it, which also handles X11 events.
Letting the UI event handling inside the event dispatcher allows the existence of a flag to skip handling of UI events.
Back on our example. This new dispatcher is GUI-enabled, it watches for X11 events and translates them to QEvents for delivery with help of the function QApplication::x11ProcessEvent(). Note that since this is just a proof-of-concept, we are not checking for all the flags passed.
Inside the directory ‘qapplication’, there is one example using this dispatcher. It mixes a QWidget on the screen (using QApplication for the GUI) and an EFL window showing Evas in other. They are in the same process but with different X connections.
Last but not least: the QEventLoop
If you look the Qt documentation, you can see there’s a QEventLoop class. But, does this class do if the mainloop is already in the event dispatcher?
Simply put: QEventLoop is a helper class that provides convenient access to the dispatcher’s processEvents() call. For example, QEventLoop has an exec() method, that looks like this
// Simplified version of QEventLoop::exec()
while (!eventLoopQuit)
dispatcher->processEvents();
One usage of this pattern is to make a function blocks but still running the mainloop (so things like UI still work). This enable APIs like the ones used in some QDialogs
QMessageBox msgBox;
msgBox.setText("The document has been modified.");
msgBox.setInformativeText("Do you want to save your changes?");
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
// Blocks until a button is clicked in the dialog, ret will contain information
// to identify that button.
int ret = msgBox.exec();
To see some downsides of this kind of API in dialogs, I recommend reading the article Unpredictable exec() from Qt Labs blogs.
Further reading
For those who are interested in digging further, there are many ways to go from here:
- Event-driven programming: there are plenty of resources on the net, but I would like to point out two:
- A talk about Node.js (pdf and video). Node.js provides an environment for server-side programming in JavaScript based on events. The talk particularly highlights the advantages of using event-driven programming.
- A talk from the Plan 9 conference in 2007 called Threads without Locks. The author shows the relation between usage of threads and event loops and how they can be used together to avoid locks for syncronization. These ideas appear also in Go programming language.
- QThreads: I didn’t mentioned threads, but in Qt, if your thread runs a loop, it’ll create a new event dispatcher. As of today you still can’t use custom dispatchers for the QThreads, only for the main thread.
- Evas in Qt: the example showed Evas being used in a Qt application. While this was one way to use Evas in Qt, there are other possibilities. You could just use the Evas library directly with Qt, without Ecore/EcoreEvas. But in that case you’ll need to do the job of feeding Evas with events yourself.
That’s all folks.
Comment (1)
A great piece of information.
Thanks!