[Developers] Calling GUI functions in a multi-threaded application

Jaap Glas Jaap.Glas at dgbes.com
Thu Apr 9 13:59:08 CEST 2009


Dear fellow developers,


Calling GUI Functions in a Multi-Threaded Application.
======================================================

The graphical user interface (GUI) of OpendTect has been built on top of
the open-source cross-platform application and UI framework called Qt
(see www.qtsoftware.com and/or doc.trolltech.com). OpendTect's interface
to the Qt functionality is located in the directory uiBase. Qt raises
some specific problems when the programmer wants to apply GUI functions
in a multi-threaded application. The use of multi-threaded code is rapidly
gaining popularity now multi-processor hardware becomes available to
many users. The fact is that up to the Qt-version OpendTect uses today,
Qt does not (fully) support its functions to be called in a different thread
than the base (GUI) thread in which the OpendTect console was started up.

These are some of the error messages that might show up when functions
from uiBase are calling Qt functions in a thread other than the GUI-thread.

     Xlib: unexpected async reply
(PE) QObject::setParent: Cannot set parent, new parent is in a different thread
(PE) QObject: Cannot create children for a parent that is in a different thread.
(PE) QGLContext::makeCurrent(): Failed.

Often these error messages are accompanied by a crash of OpendTect.
It is not said that every uiBase function you call will cause problems,
and neither that it does that any time it is called. At least const-functions
are safe. One can expect most problems when the called function is somehow
adding or removing graphical elements to your desktop.

What can be done if you are developing a multi-threaded application and
experience the problems mentioned above. The first step should be trying
to reorganize your multi-threaded design such that all GUI actions are
executed in the base thread. However, there will be applications where
this is not feasible. One example is OpendTect's Command Driver plugin,
where a second thread is needed to run the command interpreter that
steers the OpendTect console, which is residing in the base thread.
That command interpreter cannot simply call the click() function of
the uiButton-class (see uiBase/uibutton.h) to push for instance the
button that pops up the attribute set window. It would be executed
in the second thread, where it should be executed in the base thread
in order not to cause a crash. The trick to force this is hidden in
the code of the activate() function (see uiBase/uibutton.cc) that has
to be called instead.

What does the activate() function of the uiButton class do to force a
button click to be executed in the GUI thread? It calls the function
QApplication::postEvent(*QObject receiver,*QEvent) to insert a QEvent
of a user-defined type into the event queue of Qt. The key point is
that Qt is dispatching its event queue in the base thread. The receiver
of our user-defined event must be a class that inherits QObject and
is somehow connected to the uiButton to be clicked. In this case
the i_ButMessenger class applies (see uiBase/i_qbutton.h). It needs
to overload the QObject::event(QEvent*) function and when it receives
an event of our used-defined type, it (indirectly) calls the click()
function of the uiButton it has been connected to. This detour does
guarantee that the button is executed in the base thread.

Many classes in the uiBase directory contain functions usually named
activateXxxxx(.,.,.) to force that the uiBase object performs a
particular GUI action in the base thread. One might have a look at
them when developing a multi-threaded application and experiencing
the problems described above. If the GUI action you want to be
performed in the base thread is not supported yet, you can add it
or contact me to have it added. Since plugin builders do not have
write access to the code in uiBase, we might start thinking of
replacing all those special purpose functions in uiBase by a more
general code mechanism to force execution in the base thread.
However, much easier would be to wait for a future Qt version
that allows all its functions to be called from any thread.

Best regards,


Jaap Glas.


-- 
-- dr. Jaap C. Glas
-- Software Engineer
-- dGB Earth Sciences
-- Nijverheidstraat 11-2
-- 7511 JM Enschede, The Netherlands
-- jaap.glas at dgbes.com
-- http://www.dgbes.com
-- Tel: +31 534315155, Fax: +31 534315104





More information about the Developers mailing list