A brief study about the coexistence of multiple Q*Application

About this post

A couple of years ago I had to embed the Qt interface editor (Qt Designer) into another application. While how I did it is not too important right now, it turns out that I faced the following problem: Qt Designer creates its own instance of QApplication, but the new container application does the same! So, to answer the obvious question – will I have problems? – I decided to study the subject a little more thoroughly.

Background: QApplication

A large number of Qt functionalities require the existence of a special object, the application: it manages the event loops, a large number of global variables (such as the current language), etc.

Depending on the mode of the application, this object can be of type QCoreApplication for console applications, orqApplication for applications with a graphical interface (there is the QGuiApplication as well, but it is more common to use theqApplication). Example:

#include <qapplication.h>

int main(int argc, char* argv[])
{
  QApplication a(argc, argv);

  // Setting some global application properties
  qApp->setApplicationName("Multiple QApplication");
  qApp->setOrganizationName("Header Files");

  return a->exec(); // event loop
}

As you can see in this example, the pointer qApp has been used, which is basically a [_singleton _](https: //en.wikipedia.org/wiki/Singleton_pattern) (well, a macro that simplifies its use). Now, as any other singleton, it is not possible that there are two instances. What happens then with the scenario exposed at the beginning, where it is possible that QApplication is instantiated twice?

Sample Code

The following example will illustrate the consequences of having one or more Q*Applications instantiated. The tests have been done by running Qt 5.12.2 32-bit compiled with Visual Studio 2017 (15.9.11). I have performed the same tests with previous versions of Qt 5 with the same results.

The code for this project (for VS) is available at GitHub.

#include <QtCore>
#include <qapplication.h>

QTranslator tr1;

void testTranslators(QApplication& a)
{
  qDebug() << qApp->removeTranslator(&tr1); // false if the translator is not installed
  a.installTranslator(&tr1); // it is installed in the latest instance (as if called from qApp)
  qDebug() << qApp->removeTranslator(&tr1); // false if the translator is not installed
}

void oneQApplication(int argc, char* argv[])
{
  qDebug() << __FUNCTION__;

  QApplication a1(argc, argv);
  a1.setApplicationName("a1");
  a1.installTranslator(&tr1);
  qDebug() << qApp << &a1;
  qDebug() << "a1.applicationName() =" << a1.applicationName();

  // qApp == &a1
  QObject::connect(&a1, &QCoreApplication::aboutToQuit, []() { qDebug() << "aboutToQuit from a1!"; });
  QTimer::singleShot(2000, &a1, &QCoreApplication::quit); // as if connected to latest qApp

  testTranslators(a1);

  qApp->exec();
  qDebug() << "-----";
}

void twoQApplications(int argc, char* argv[])
{
  qDebug() << __FUNCTION__;

  QApplication a1(argc, argv);
  a1.setApplicationName("a1");
  a1.installTranslator(&tr1);
  qDebug() << qApp << &a1;
  qDebug() << "a1.applicationName() =" << a1.applicationName();

  // qApp == &a1
  QObject::connect(&a1, &QCoreApplication::aboutToQuit, []() { qDebug() << "aboutToQuit from a1!"; });
  QTimer::singleShot(2000, &a1, &QCoreApplication::quit); // as if connected to latest qApp

  qApp->setStyleSheet("QLineEdit{}");
  qDebug() << "qApp->styleSheet() = " << qApp->styleSheet();

  do { // limite scope of second application
    QApplication a2(argc, argv);
    a2.setApplicationName("a2");
    qDebug() << qApp << &a1 << &a2;
    qDebug() << "a2.applicationName() =" << a2.applicationName();
    qDebug() << "a1.applicationName() =" << a1.applicationName(); // as if called from qApp
    qDebug() << "qApp->applicationName() =" << qApp->applicationName();
    qDebug() << "qApp->styleSheet() = " << qApp->styleSheet();
    QObject::connect(&a2, &QCoreApplication::aboutToQuit, []() { qDebug() << "aboutToQuit from a2!"; });

    testTranslators(a1);

    qApp->exec();
  } while (false);

  qDebug() << qApp << &a1;

  a1.installTranslator(&tr1);

  qDebug() << "-----";
}

int main(int argc, char* argv[])
{
  oneQApplication(argc, argv);
  twoQApplications(argc, argv);

  return 0;
}

Results

Using a Release configurtion

oneQApplication
QApplication(0x79fd90) QApplication(0x79fd90)
a1.applicationName() = "a1"
true
true
aboutToQuit from a1!
-----
twoQApplications
QApplication(0x79fd88) QApplication(0x79fd88)
a1.applicationName() = "a1"
qApp->styleSheet() =  "QLineEdit{}"
QApplication(0x79fd80) QApplication(0x79fd88) QApplication(0x79fd80)
a2.applicationName() = "a2"
a1.applicationName() = "a2"
qApp->applicationName() = "a2"
qApp->styleSheet() =  "QLineEdit{}"
false
true
aboutToQuit from a2!
QObject(0x0) QApplication(0x79fd88)
QApplication::installTranslator: Please instantiate the QApplication object first
-----

Using a Debug configuration

In debug, the result is similar, saving for a couple of asserts that can be ignored since they do not violate any preconditions but simply warn of possible unexpected results.

oneQApplication
QApplication(0x59f9b4) QApplication(0x59f9b4)
a1.applicationName() = "a1"
true
true
aboutToQuit from a1!
-----
twoQApplications
QApplication(0x59f9b8) QApplication(0x59f9b8)
a1.applicationName() = "a1"
qApp->styleSheet() =  "QLineEdit{}"
ASSERT failure in QCoreApplication: "there should be only one application object", file kernel\qcoreapplication.cpp, line 791
ASSERT: "!eventDispatcher" in file kernel\qcoreapplication.cpp, line 852
QApplication(0x59f8d8) QApplication(0x59f9b8) QApplication(0x59f8d8)
a2.applicationName() = "a2"
a1.applicationName() = "a2"
qApp->applicationName() = "a2"
qApp->styleSheet() =  "QLineEdit{}"
false
true
aboutToQuit from a2!
QObject(0x0) QApplication(0x59f9b8)
QApplication::installTranslator: Please instantiate the QApplication object first
-----

What then involves several Q*Application?

  • The most obvious: in debug some asserts will fail, which can be ignored. In release there is no crash.
  • Each instantiation of the Q*Application updates the singleton (qApp always points to the last instance), but does not save a history for rollback (see next point).
  • When the active instance of Q*Application (the last one) is destroyed, qApp becomes null, not the previous instance.
  • Properties like applicationName andapplicationOrganization are preserved between versions: this is quite logical given that these methods are static.
  • slots always act on the last Q*Application (aka: qApp).
  • The only case I have seen in this brief study where we should be careful about is with translators, which are not transferred to the next instance, but they need the singleton (qApp) to be valid to be installed.

Conclusions

It is possible to have a process that instantiates the Q*Applications multiple times, as long as we consider:
– The last instance must exist whenever we want to access the singleton. If the last instance is created by a plugin that can be downloaded, we must ensure the creation of a new global instance before that happens.
– Be careful if our application uses QApplication but the current instance (the last one) is aQCoreApplication.

Related pages