Solved Creating QGuiApplication on the heap - crashes (multiple UIs via plugins)
-
You are passing a reference to a stack variable to
QGuiApplication
, which gets invalidated when you return from the function. Notice thatargc
should be passed asint &
... not that I have any idea why you would want to create the application object in the heap ... -
Thanks for your help kshegunov. Passing a reference to a local stack variable to QGuiApplication was indeed the source of the problem. I was misled by the fact that createApplication1() and createApplication2() worked flawlessly even with this error.
"valgrind --leak-check=full" doesn't show anything, but valgrind isn't probably the best tool for stack-related errors . Maybe sanitizers could be helpful to detect this kind of bug.
I'm creating an application with both Qt Widgets and Qt Quick Controls UI.
My idea is to put both UIs into separate plugins which the main application starts. I was thinking about letting the plugins create a QGuiApplication / QApplication instance in a createApplication() function so the main app doesn't have to know the details (maybe it could even work with KApplication?).
The alternative could be to create a QGuiApplication / QApplication in the main app based on the id (e.g. QString or enum) retrieved from the chosen plugin. This might have some limitations.
Are there any better solutions?
-
@Deckard said in Creating QGuiApplication on the heap - crashes (multiple UIs):
The alternative could be to create a QGuiApplication / QApplication in the main app based on the id (e.g. QString or enum) retrieved from the chosen plugin. This might have some limitations.
Well, just creating one
QApplication
as one usually does should be just enough,QApplication
is alreadyQGuiApplication
. -
The documentation for QApplication:
https://doc.qt.io/qt-5/qapplication.html#details
says it depends on QtWidgets.I would like to avoid this in the Qt Quick version as it might ruin start-up time:)
-
I don't follow, if you will have plugins that depend on widgets, then you will need the
QtWidgets
module (and by extensionQApplication
) anyway ... and you're complicating your life on a "might" ... doesn't make much sense to me. -
I admit I'm not an expert so I may be wrong. I will try to explain my idea.
Here is a simplified view of my application:
Link to the image:
https://i.imgsafe.org/5e6f40aa3d.png
https://s2.postimg.org/ei5411mkp/My_App.pngThe idea is that UI plugins are optional and only one is needed at the time. If I load ONLY MyAppQtQuick plugin, the QtWidgets will not get loaded (hopefully) because QGuiApplication that is created in MyAppQtQuick doesn't depend on QtWidgets.
This will not be useful in Qt based environments like KDE or LXQt because QtWidgets are always loaded there but it might be useful in environments like GNOME where QtWidgets is not always loaded (because no other Qt application is running). So starting QtWidgets in addition to QtQuick
willmight affect start-up time in GNOME.There is also a packaging issue. The app will be free software and I'd like it to be packaged (hopefully) by distributors in way that allows the user selecting only needed plugins. Again, if someone is using GNOME and wants to use only MyAppQtQuick plugin, he can avoid downloading QtWidgets package if MyAppQtQuick creates QGuiApplication. But if I create QApplication in MyApp instead, then it will always depend on QtWidgets, whatever plugin is used.
And for Android or other mobile systems QtWidgets are not useful.But you made me thinking if all plugins are not loaded anyway even I'm looking for only one with particular ID. This would be a common way of loading a particular plugin:
MyPluginType* loadPlugin(const QString& pluginId) { QDir dir ("/usr/lib/myapp"); for (QString fileName : dir.entryList(QDir::Files)) { auto path = dir.absoluteFilePath(fileName); QPluginLoader loader(path); QObject *somePlugin = loader.instance(); // loads a plugin; are plugin's dependencies loaded too? if (somePlugin) { if (auto myPlugin = qobject_cast<MyPluginType*>(somePlugin)) { if ( myPlugin->name() == pluginId) return myPlugin; } loader.unload(); // unloading a plugin } } return nullptr; }
So if both plugins are installed and I want to load only a plugin with pluginId == "QtQuick" I will have to iterate over all files in the given folder, load them one by one to check if they have desired ID and unload them if not. QPluginLoader::instance() likely loads all plugin's dependencies.
But the files in QDir::entryList() can be sorted in any order so MyAppQtWidgets plugin might be loaded (likely with its dependencies e.g. QtWidgets) before MyAppQtQuick plugin, only to discard it and unload.
So to summarize: loading plugins selectively based on the retreived id is probably not the best idea because I have to load a plugin (with its dependencies) first to check its id. Another option would be to just blacklist or whitelist plugin file name but this may be error prone.
What I'm writing may be completely wrong of course...
EDIT: Ok, I've found something that looks like a good solution: I can get plugin's info without loading it using QPluginLoader::metaData().
-
Well, yes, there's that. But I still doubt the wisdom in delegating the responsibility to create the application to the plugins. You also run into the trouble of creating
QObject
s beforeQCoreApplication
(by means of the plugin'sQObject
entry point) which is unsupported. -
I understand that, without QCore/QGUI/QApplication, events and singals/slots don't work. But in the documentation for QObject, QCoreApplication, QGuiApplication, QApplication and QPluginLoader I haven't found any warning that creating QObjects before QCoreApplication is not supported, dangerous, illegal or leads to e.g. undefined behaviour. It is only recommended to create the application object as early as possible and QApplication must be created before any QPaintDevice objects, e.g. QWidgets. If I'm wrong, please correct me.
And if I'm wrong about this, another option is to split my application into two executables with different names, both dependent on different UIs. I wanted to avoid that because it adds some work to maintain separate files for each version for desktop environment / file manager integration.
-
In general, creating QObjects before the QApplication is not supported and can lead to weird crashes on exit, depending on the platform. This means static instances of QObject are also not supported. A properly structured single or multi-threaded application should make the QApplication be the first created, and last destroyed QObject.
From here and on the the mailing list. There are multiple posts for
QObject
s not working and/or crashing when created as static variables (i.e. before the application object) but I'm too lazy to dig them all up.And if I'm wrong about this, another option is to split my application into two executables with different names, both dependent on different UIs.
As I said, just create one
QApplication
and roll with it. If you're so worried about the time it takes to initialize the widget stuff, benchmark it and only then decide if you need another solution. If you still insist on creating a separate application object for each UI, then pass an argument on startup and create the corresponding application object based on that. -
Ok, so I have no other choice then to create an application object first. Otherwise I will end up with a malfunctioning application.
Regarding the Qt Widgets dependency, I'd rather go with splitting my app into two parts. It's not about start up time - the difference may be negligible indeed.
It's about additional size (6.4 MiB on amd64 for Qt Widgets). If I understand correctly I have to deploy all needed Qt libs with my application for Android. So every time I update my app users have to also download all included Qt libs, including ~6.4 MiB for Qt Widgets which are redundant. On mobile devices it makes a difference - connection is slow, costly and drains battery, especially on 3G). Also, if all Qt-based apps are deployed that way they waste disk space.
Or, maybe I'm just oversensitive. Or wrong again:)
Yet another option would be a conditional, macro-based compilation to switch to QGuiApplication when compiling for Android. Maybe as a last resort...
Anyway, thank you very much for helping me out! Understanding the QApplication/QObject problem has been very important and saved me a frustration later in development.
-
@Deckard said in Creating QGuiApplication on the heap - crashes (multiple UIs via plugins):
So every time I update my app users have to also download all included Qt libs, including ~6.4 MiB for Qt Widgets which are redundant.
Why? Are you going to do static linking? If not what's the problem with only updating your app without updating the Qt binaries. Additionally, one of the very fine things I like about Qt is that it's binary compatible, which means you can even update the Qt binaries without deploying your app (assuming you don't use new-version features. This actually enables you to fetch patched Qt versions without modifying your app (or redeploying it).
if all Qt-based apps are deployed that way they waste disk space.
Well, the best way to deploy on Linux (I don't know much about android) is to not deploy Qt at all, but rather use the one from the distro's repository.
Yet another option would be a conditional, macro-based compilation to switch to QGuiApplication when compiling for Android. Maybe as a last resort...
You can setup this rather easily:
android-*: DEFINES += ANDROID_BUILD else: QT += widgets
in the pro file and then you can typedef the class in main (for brevity):
#ifdef ANDROID_BUILD #include <QGuiApplication> typedef QGuiApplication Application; #else #include <QApplication> typedef QApplication Application; #endif int main(int argc, char ** argv) { Application app(argc, argv); return Application::exec(); }
-
Yes, I know that on Linux package managers will install dependencies automatically, however I was worried about Android,
Fortunately there are two ways of deploying Qt-based apps for Android (blog post):
-
Using Ministro service, which will install needed Qt libs on demand. The libs are shared between applications. Ministro is available at Google Play and F-Droid.
-
Packaging Qt libs and app in the APK.
But it's too early for me to decide which one to choose.
-