多个QApplication实例

cbu*_*art 9 c++ singleton qt qapplication

我想知道在同一进程中拥有多个QApplication/实例会产生什么影响(问题),以及如何解决与之相关的一些问题。QCoreApplication

场景如下:我想在开源第三方应用程序上制作一个包装器,以便将其转换为可嵌入小部件作为可选插件(该应用程序基本上由基于单一的QMainWindow接口组成)。

此类项目严重依赖QCoreApplication派生类,但基本上是因为它被用作已经存在的单例。我能够修改代码(并且我必须这样做才能将其公开QMainWindow为可嵌入的小部件),尽管由于该项目的复杂性我不能简单地删除父类。

因此,最终的应用程序将有自己的QApplication(在启动时创建),然后可以选择加载上述插件(从而创建第二个QCoreApplication)。只有第一个(主)QApplication用于事件循环 ( QCoreApplication::exec())。

我知道QCoreApplication单身的事实。在我的测试中,单例始终指向最后创建的实例:

qDebug() << qApp;
auto app1 = new QApplication(argc, argv);
qDebug() << qApp;
auto app2 = new TheOtherQApplication(argc, argv);
qDebug() << qApp;
Run Code Online (Sandbox Code Playgroud)

输出是

Q对象(0x0)

QApplication(0x6f9400,名称=“测试”)

QCoreApplication 中的 ASSERT 失败:“应该只有一个应用程序对象”,文件 kernel\qcoreapplication.cpp,第 595 行

TheOtherQApplication(0x2550dc0,名称=“测试”)

TheOtherQApplication(0x2550dc0,名称 =“测试”) TheOtherQApplication(0x2550dc0,名称 =“测试”)

可以看出,QApplication创建第二个实例后,它会替换全局实例。有什么办法可以解决这个问题吗?由于插件是可选的,明显的答案(QApplication在第二位加载主程序)不是一个合适的选择。

另外,拥有多个QApplication实例是否还有其他影响?或者都与事件循环(选中)和单例相关?

注意:由于第三方依赖项尚未完全更新,这是一个基于 Qt 4.7 的项目。计划在一年左右的时间内迁移到最新版本,但目前我必须处理 4.7。


顺便说一句,我已经审查了几个相关的问题,包括这个问题,但它没有提供任何有用的信息。

cbu*_*art 9

好吧,据我所知,使用两个或多个Q*Applications ( QCoreApplication, QGuiApplication, QApplication) 意味着:

  1. 创建第二个应用程序(或更多)时,断言在调试模式下失败。发布模式下没有崩溃。

  2. 每个实例化都会Q*Application更新单例(即,qApp将始终指向最后一个实例)。

  3. 应用程序名称和版本等全局属性随实例传输并覆盖以前的属性。

  4. 连接到 a 的槽的任何信号都会调用Q*Application单例的槽,即使在创建最新实例之前连接也是如此。

  5. 只有连接到最新信号的槽Q*Application才会被调用(它们不会转移到新实例)。

  6. Q*Application创建新实例时不会传输转换器。

  7. 如果最后一个实例Q*Application被销毁,则单例将变为空(它不会回退到前一个实例)。

您可以使用以下代码和切换来测试这些功能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();
}
Run Code Online (Sandbox Code Playgroud)

结果一Q*Application

QCoreApplication(0xfafb74) QCoreApplication(0xfafb74)

a1.applicationName() = "a1"

真的

真的

来自a1的世界,你好!

结果有两个Q*Application

QCoreApplication(0xbefb2c) QCoreApplication(0xbefb2c)

a1.applicationName() = "a1"

QCoreApplication 中的 ASSERT 失败:“应该只有一个应用程序对象”,文件 ########\qtbase\src\corelib\kernel\qcoreapplication.cpp,第 769 行

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

a2.applicationName() = "a2"

a1.applicationName() = "a2"

错误的

真的

来自a2的世界,你好!

当测试点7时,a2退出语句时被销毁if。在这种情况下,每次调用Q*Application完成的方法都会引发警告并且不会被执行(它们不会崩溃,也不会破坏任何断言)。即使从以前的应用程序调用时也会发生这种情况:a1.installTranslator(&tr1);

QApplication::installTranslator:请先实例化 QApplication 对象

注:使用 Visual Studio 2010 进行测试。Qt 版本为 4.7 和 5.6.1-1,两者结果相同


更新:此答案的更清晰的代码版本可在https://github.com/cbuchart/stackoverflow/blob/master/46304070-multiple-qapplication-instances/main.cpp中找到

QApplication在注释之后,此代码还测试了当所有对象被销毁然后再次创建时会发生什么。结果:正如预期的那样,没有发生任何特殊情况,似乎没有副作用。


结论

考虑到这些点,似乎可以与两个或多个一起工作Q*Application,更重要的是,当在除最后一个之外的任何一个上完成时Q*Application,与信号的连接都会丢失并且不会安装翻译器。另外,如果最后一个实例被销毁,则没有可用的应用程序,因此您应该注意这些情况(例如,如果卸载创建最后一个实例的 DLL)。