多个 QApplication 实例
Multiple QApplication instances
我想知道在同一个进程中有多个 QApplication
/QCoreApplication
实例有什么影响(问题),以及如何解决一些相关问题。
场景如下:我想在开源 third-party 应用程序上制作一个包装器,以便将其转换为可嵌入的小部件作为可选插件(该应用程序基本上由一个单一的QMainWindow
基于接口)。
这样的项目严重依赖于 QCoreApplication
派生的 class 但主要是因为它被用作已经存在的单例。我能够修改代码(我必须这样做才能将 QMainWindow
公开为可嵌入的小部件),尽管由于该项目的复杂性,我不能简单地 remove parent class.
因此,最终应用程序将拥有自己的 QApplication
(在启动时创建),然后可选择加载上述插件(从而创建第二个 QCoreApplication
)。只有第一个(主要)QApplication
用于事件循环(QCoreApplication::exec()
)。
我知道 QCoreApplication
是单身人士。在我的测试中,单例总是指向最后创建的实例:
qDebug() << qApp;
auto app1 = new QApplication(argc, argv);
qDebug() << qApp;
auto app2 = new TheOtherQApplication(argc, argv);
qDebug() << qApp;
输出为
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")
可以看出,在创建第二个 QApplication
后,它替换了全局实例。有什么办法可以解决这个问题吗?由于该插件是可选的,所以显而易见的答案(加载主 QApplication
放在第二位)不是一个合适的选项。
此外,拥有多个 QApplication
实例是否还有其他影响?还是都和事件循环(勾选)和单例有关?
注意:这是一个基于 Qt 4.7 的项目,由于 third-party 依赖项尚未完全更新。计划在一年左右迁移到最新版本,但目前我必须处理4.7.
顺便说一句,我已经查看了几个相关问题,包括 this one 但没有提供任何有用的信息。
嗯,据我所知,使用两个或更多 Q*Application
s(QCoreApplication
、QGuiApplication
、QApplication
)意味着:
创建第二个(或更多)应用程序时,断言在调试模式下失败。发布模式下没有崩溃。
Q*Application
的每个实例化都会更新单例(即,qApp
将始终指向最后一个实例)。
应用程序名称和版本等全局属性随实例一起传输并覆盖以前的属性。
连接到 Q*Application
的插槽的任何信号都会调用单例的插槽,即使在创建最新实例之前已连接也是如此。
只会调用连接到最新 Q*Application
信号的插槽(它们不会转移到新实例)。
创建新的 Q*Application
个实例时不会转移翻译器。
如果最后一个 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();
}
结果有一个 Q*Application
QCoreApplication(0xfafb74) QCoreApplication(0xfafb74)
a1.applicationName() = "a1"
true
true
Hello world from a1!
结果有两个 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!
测试点7时,a2
在退出if
语句时被销毁。在这种情况下,每次调用 Q*Application
方法 done 都会引发警告并且不会执行(它们不会崩溃并且没有断言被破坏)。即使从以前的应用程序调用时也会发生这种情况:a1.installTranslator(&tr1);
QApplication::installTranslator: Please instantiate the QApplication object first
注意:使用Visual Studio 2010 进行测试。Qt 版本为4.7 和5.6.1-1,两者的结果相同
更新: https://github.com/cbuchart/Whosebug/blob/master/46304070-multiple-qapplication-instances/main.cpp
中提供了此答案的更清晰的代码版本
根据评论,这段代码还测试了当所有 QApplication
对象被销毁然后再次创建时会发生什么。结果:正如预期的那样,没有什么特别的发生,似乎没有副作用。
结论
考虑到这些要点,似乎可以使用两个或更多 Q*Application
,更重要的是,除了最后一个 Q*Application
之外的任何一个都完成后,与信号的连接就会丢失并且没有安装翻译器。此外,如果最后一个实例被销毁,则没有应用程序可用,因此您应该注意这些情况(例如,如果卸载创建最后一个实例的 DLL)。
我想知道在同一个进程中有多个 QApplication
/QCoreApplication
实例有什么影响(问题),以及如何解决一些相关问题。
场景如下:我想在开源 third-party 应用程序上制作一个包装器,以便将其转换为可嵌入的小部件作为可选插件(该应用程序基本上由一个单一的QMainWindow
基于接口)。
这样的项目严重依赖于 QCoreApplication
派生的 class 但主要是因为它被用作已经存在的单例。我能够修改代码(我必须这样做才能将 QMainWindow
公开为可嵌入的小部件),尽管由于该项目的复杂性,我不能简单地 remove parent class.
因此,最终应用程序将拥有自己的 QApplication
(在启动时创建),然后可选择加载上述插件(从而创建第二个 QCoreApplication
)。只有第一个(主要)QApplication
用于事件循环(QCoreApplication::exec()
)。
我知道 QCoreApplication
是单身人士。在我的测试中,单例总是指向最后创建的实例:
qDebug() << qApp;
auto app1 = new QApplication(argc, argv);
qDebug() << qApp;
auto app2 = new TheOtherQApplication(argc, argv);
qDebug() << qApp;
输出为
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")
可以看出,在创建第二个 QApplication
后,它替换了全局实例。有什么办法可以解决这个问题吗?由于该插件是可选的,所以显而易见的答案(加载主 QApplication
放在第二位)不是一个合适的选项。
此外,拥有多个 QApplication
实例是否还有其他影响?还是都和事件循环(勾选)和单例有关?
注意:这是一个基于 Qt 4.7 的项目,由于 third-party 依赖项尚未完全更新。计划在一年左右迁移到最新版本,但目前我必须处理4.7.
顺便说一句,我已经查看了几个相关问题,包括 this one 但没有提供任何有用的信息。
嗯,据我所知,使用两个或更多 Q*Application
s(QCoreApplication
、QGuiApplication
、QApplication
)意味着:
创建第二个(或更多)应用程序时,断言在调试模式下失败。发布模式下没有崩溃。
Q*Application
的每个实例化都会更新单例(即,qApp
将始终指向最后一个实例)。应用程序名称和版本等全局属性随实例一起传输并覆盖以前的属性。
连接到
Q*Application
的插槽的任何信号都会调用单例的插槽,即使在创建最新实例之前已连接也是如此。只会调用连接到最新
Q*Application
信号的插槽(它们不会转移到新实例)。创建新的
Q*Application
个实例时不会转移翻译器。如果最后一个
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();
}
结果有一个 Q*Application
QCoreApplication(0xfafb74) QCoreApplication(0xfafb74)
a1.applicationName() = "a1"
true
true
Hello world from a1!
结果有两个 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!
测试点7时,a2
在退出if
语句时被销毁。在这种情况下,每次调用 Q*Application
方法 done 都会引发警告并且不会执行(它们不会崩溃并且没有断言被破坏)。即使从以前的应用程序调用时也会发生这种情况:a1.installTranslator(&tr1);
QApplication::installTranslator: Please instantiate the QApplication object first
注意:使用Visual Studio 2010 进行测试。Qt 版本为4.7 和5.6.1-1,两者的结果相同
更新: https://github.com/cbuchart/Whosebug/blob/master/46304070-multiple-qapplication-instances/main.cpp
中提供了此答案的更清晰的代码版本根据评论,这段代码还测试了当所有 QApplication
对象被销毁然后再次创建时会发生什么。结果:正如预期的那样,没有什么特别的发生,似乎没有副作用。
结论
考虑到这些要点,似乎可以使用两个或更多 Q*Application
,更重要的是,除了最后一个 Q*Application
之外的任何一个都完成后,与信号的连接就会丢失并且没有安装翻译器。此外,如果最后一个实例被销毁,则没有应用程序可用,因此您应该注意这些情况(例如,如果卸载创建最后一个实例的 DLL)。