多个 QApplication 实例
Posted
技术标签:
【中文标题】多个 QApplication 实例【英文标题】:Multiple QApplication instances 【发布时间】:2017-09-19 15:14:54 【问题描述】:我想知道在同一个进程中有多个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;
输出是
QObject(0x0)
QApplication(0x6f9400, name = "test")
QCoreApplication 中的 ASSERT 失败:“应该只有一个应用程序对象”,文件 kernel\qcoreapplication.cpp,第 595 行
TheOtherQApplication(0x2550dc0, name = "test")
TheOtherQApplication(0x2550dc0, name = "test") TheOtherQApplication(0x2550dc0, name = "test")
可以看出,在创建第二个QApplication
之后,它会替换全局实例。有没有办法解决这个问题?由于插件是可选的,因此显而易见的答案(在第二位加载主 QApplication
)不是一个合适的选项。
另外,拥有多个QApplication
实例还有其他含义吗?还是都与事件循环(选中)和单例有关?
注意:由于第三方依赖尚未完全更新,这是一个基于 Qt 4.7 的项目。计划在一年左右迁移到最新版本,但目前我要处理4.7。
顺便说一句,我已经查看了几个相关问题,包括 this one,但这并没有提供任何有用的信息。
【问题讨论】:
您确定唯一的方法是为您的自定义MainWindow
再创建一个QCoreApplication
实例吗?对我来说,这似乎非常骇人听闻,几乎任何其他方式都更可取。这对我来说似乎是XY problem...
我知道这很hackish,但是插件的问题是客户端希望能够利用该代码的未来更新,所以想法是尽可能少地修改外部项目(甚至在未来提出拉取请求)。另一方面,该插件是完全可选的,所以我不能依赖它的存在作为主要的QApplication
:(
所以,插件使用了 Qt,但用户可以将其包含在他的应用程序中,即使他不使用 Qt?
否:插件和应用程序都在Qt中,但插件是可选的,所以我不能使用插件的QApplication
作为主可执行文件的QApplication
,这就是为什么我'要求它的共存。
【参考方案1】:
好吧,据我所知,使用两个或多个Q*Application
s(QCoreApplication
、QGuiApplication
、QApplication
)意味着:
在创建第二个(或更多)应用程序时,断言在调试模式下失败。发布模式下没有崩溃。
Q*Application
的每个实例都会更新单例(即,qApp
将始终指向最后一个实例)。
应用程序名称和版本等全局属性随实例传输并覆盖之前的属性。
任何连接到Q*Application
插槽的信号都会调用单例的插槽,即使在创建最新实例之前连接也是如此。
只有连接到最新Q*Application
信号的槽才会被调用(它们不会被转移到新实例)。
创建新的Q*Application
实例时不会转移翻译器。
如果最后一个 Q*Application
被销毁,则单例变为 null(它不会回退到前一个实例)。
您可以使用以下代码并切换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"
是的
是的
来自 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/***/blob/master/46304070-multiple-qapplication-instances/main.cpp中提供了此答案的更简洁的代码版本
在 cmets 之后,此代码还测试当所有 QApplication
对象被销毁然后再次创建时会发生什么。结果:正如预期的那样,没有发生什么特别的事情,似乎没有副作用。
结论
考虑到这些要点,似乎可以与两个或更多Q*Application
一起工作,更重要的是,当在除最后一个Q*Application
之外的任何一个上完成时,与信号的连接会丢失并且不会安装翻译器。此外,如果最后一个实例被销毁,则没有可用的应用程序,因此您应该注意这些情况(例如,如果卸载创建最后一个实例的 DLL)。
【讨论】:
有趣。那么,看起来我可以创建/销毁多个实例?我问是因为我想在一些非并行单元测试中使用 QCoreApplication。 @PaulKnopf 我已经测试过在添加第二个之前删除第一个QApplication
,结果是相同的。我已经向 GitHub 上传了一个更简洁的版本,它隐式测试了 QApplication
破坏。以上是关于多个 QApplication 实例的主要内容,如果未能解决你的问题,请参考以下文章
完全删除 QApplication 实例并在另一个线程中重新创建它
在 Qt 中隐藏和重新启动相同的 QApplication 实例
QApplication 实例/qtbot 夹具导致 travis-ci 中止和核心转储