Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple QApplication instances

I'd like to know what are the implications (problems) of having multiple QApplication/QCoreApplication instances in the same process, and how to solve some issues regarding it.

The scenario is as follows: I'd like to make a wrapper on a open source third-party application in order to convert it into an embeddable widget as an optional plugin (the application consists basically on a single QMainWindow-based interface).

Such project heavily relies on a QCoreApplication derived class but basically because it is used as an already existing singleton. I'm able to modify the code (and I'll have to do so in order to expose the QMainWindow as an embeddable widget), although for the complexity of that project I cannot simply remove the parent class.

So, the final application will have its own QApplication (created on start) and then will optionally load the aforementioned plugin (thus creating the second QCoreApplication). Only the first (main) QApplication is used for events loop (QCoreApplication::exec()).

I'm aware of the facts that QCoreApplication is a singleton. In my tests, the singleton always points to the last created instance:

qDebug() << qApp;
auto app1 = new QApplication(argc, argv);
qDebug() << qApp;
auto app2 = new TheOtherQApplication(argc, argv);
qDebug() << qApp;

The output is

QObject(0x0)

QApplication(0x6f9400, name = "test")

ASSERT failure in QCoreApplication: "there should be only one application object", file kernel\qcoreapplication.cpp, line 595

TheOtherQApplication(0x2550dc0, name = "test")

TheOtherQApplication(0x2550dc0, name = "test") TheOtherQApplication(0x2550dc0, name = "test")

As it can be seen, after the second QApplication is created it replaces the global instance. Is there any way to solve this? As the plugin is optional the obvious answer (loading the main QApplication on second place) is not a suitable option.

Also, are there any other implications of having multiple QApplication instances? Or are all related to the events loop (checked) and the singleton?

Note: This is a project based on Qt 4.7 due to third-party dependencies not fully updated yet. It is planned to be migrated to the latest version in a year or so, but for the moment I have to deal with 4.7.


BTW, I've already reviewed several related questions, including this one but that doesn't provide any useful information.

like image 344
cbuchart Avatar asked Sep 19 '17 15:09

cbuchart


1 Answers

Well, as far as I've been able to understand, using two or more Q*Applications (QCoreApplication, QGuiApplication, QApplication) implies:

  1. An assert fails in debug mode when creating the second application (or more). No crashes in release mode.

  2. Each instantiation of Q*Application updates the singleton (i.e., qApp will always point to the last instance).

  3. Global attributes such as application name and version are transferred along instances and override former ones.

  4. Any signal connected to a slot of a Q*Application calls the slot of the singleton, even if connected before creating the newest instances.

  5. Only slots connected to signals of latest Q*Application will be called (they are not transferred to the new instance).

  6. Translators are not transferred when new Q*Application instances are created.

  7. If the last Q*Application is destroyed then the singleton becomes null (it does not fallback to the previous instance).

You can test these features using the following code and toggling USE_TWO_QAPPS:

#include <QtCore>

#define USE_TWO_QAPPS

int main(int argc, char* argv[])
{
  QTranslator tr1;

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

  // qApp == &a1
  QObject::connect(&a1, &QCoreApplication::aboutToQuit, []() {
    // point 5, never called with  Q*Application
    qDebug() << "Hello world from a1!";
  });
  QTimer::singleShot(2000, &a1, &QCoreApplication::quit); // as if connected to latest qApp, point 4

#ifdef USE_TWO_QAPPS
// if (true) { // uncomment to test point 7
  QCoreApplication a2(argc, argv);
  a2.setApplicationName("a2");
  qDebug() << qApp << &a1 << &a2; // test point 2
  qDebug() << "a2.applicationName() =" << a2.applicationName();
  qDebug() << "a1.applicationName() =" << a1.applicationName(); // as if called from qApp, point 3
  QObject::connect(&a2, &QCoreApplication::aboutToQuit, []() {
    qDebug() << "Hello world from a2!";
  });
// } // uncomment to test point 7
#endif

  qDebug() << qApp->removeTranslator(&tr1); // false if the translator is not installed, point 6
  a1.installTranslator(&tr1); // it is installed in the latest instance (as if called from qApp)
  qDebug() << qApp->removeTranslator(&tr1);

  return qApp->exec();
}

Results with one Q*Application

QCoreApplication(0xfafb74) QCoreApplication(0xfafb74)

a1.applicationName() = "a1"

true

true

Hello world from a1!

Results with two Q*Application

QCoreApplication(0xbefb2c) QCoreApplication(0xbefb2c)

a1.applicationName() = "a1"

ASSERT failure in QCoreApplication: "there should be only one application object", file ########\qtbase\src\corelib\kernel\qcoreapplication.cpp, line 769

QCoreApplication(0xbefb1c) QCoreApplication(0xbefb2c) QCoreApplication(0xbefb1c)

a2.applicationName() = "a2"

a1.applicationName() = "a2"

false

true

Hello world from a2!

When testing point 7, a2 is destroyed when quitting the if statement. In this case, every call to Q*Application methods done raises a warning and are not executed (they don't crash and no asserts are broken). It happens even when calling from the previous application: a1.installTranslator(&tr1);

QApplication::installTranslator: Please instantiate the QApplication object first

Note: tested using Visual Studio 2010. Qt versions are 4.7 and 5.6.1-1, both with identical results


UPDATE: a cleaner code version for this answer is available in https://github.com/cbuchart/stackoverflow/blob/master/46304070-multiple-qapplication-instances/main.cpp

Following comments, this code also tests what happens when all QApplication objects are destroyed and then created again. Results: as expected nothing special occurs, no side effects seems to be present.


Conclusion

It seems possible to work with two or more Q*Application having in mind these points, being the more critical the fact that, when done on any but last Q*Application, connections to signals are lost and translators are not installed. Also, if the last instance is destroyed then no application is available, so you should take care of those cases (for example, if unloading a DLL that created the last instance).

like image 84
cbuchart Avatar answered Nov 14 '22 20:11

cbuchart