How to use a QML plugin (run time link problem)?
-
Hello all,
I'm making a QML GUI prototype to determine whether I want to replace a QWidgets GUI.
I've decided to use the plugin system to keep things modular and collect up my calls to qmlRegisterType somewhere other than a main.cpp. I've been following the examples: Getting Started Programming with QtQuick (Extending with C++) and Creating C++ Plugins for QML.
However when I try to use my plugin I get the following error at run time:
QQmlApplicationEngine failed to load component qrc:/main.qml:6 module "MaterialIcons" is not installed
QtCreator seems to be able to see my module as it doesn't underline the import statement (import MaterialIcons 1.0) in my main.qml, this is both with and without the // @qml MaterialIcons annotation as long as the project has been built.
I feel like this is a run time linking problem, my plugin compiles into the imports directory in my build tree, but it seems the QML engine can't load it. Ideally I just want the plugin static linked into the binary for ease of distribution (in the same manner as you can bundle .qml files through the use of the .qrc), my thinking is on a Windows platform a user wouldn't be likely to have a central Qt installation as I gather they might on linux or mac.
Can anyone see where I'm going wrong? I apologise if this question seems quite confused, I think I'm missing some knowledge in general when it comes to linking.
Thanks for any help!
My File Tree is as follows:
TEMPLATE = subdirs SUBDIRS += \ application\ materialicons\
QT += qml quick CONFIG += c++11 SOURCES += main.cpp RESOURCES += qml.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = # Additional import path used to resolve QML modules just for Qt Quick Designer QML_DESIGNER_IMPORT_PATH = DEFINES += QT_DEPRECATED_WARNINGS # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target DISTFILES += HEADERS +=
TEMPLATE = lib CONFIG += plugin QT += qml DESTDIR += ../imports/MaterialIcons TARGET = materialiconsplugin # Copy the qmldir file to the same folder as the plugin binary cpqmldir.files = $$PWD/qmldir cpqmldir.path = $$DESTDIR COPIES += cpqmldir SOURCES += \ materialiconfont.cpp \ materialiconsplugin.cpp HEADERS += \ materialiconfont.h \ materialiconsplugin.h RESOURCES += \ materialicons.qrc DISTFILES += qmldir
materialiconsplugin.h:
#ifndef MATERIALICONSPLUGIN_H #define MATERIALICONSPLUGIN_H #include <QtQml/QQmlExtensionPlugin> class MaterialIconsPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) public: void registerTypes(const char *uri) Q_DECL_OVERRIDE; }; #endif // MATERIALICONSPLUGIN_H
materialicons.cpp
#include "materialiconsplugin.h" #include "materialiconfont.h" #include <QtQml> void MaterialIconsPlugin::registerTypes(const char *uri) { // @qml MaterialIcons Q_ASSERT(uri == QLatin1String("MaterialIcons")); qmlRegisterType<MaterialIconFont>(uri, 1, 0, "MaterialIconFont"); }
main.qml contains:
import MaterialIcons 1.0
-
@FabriceS said in How to use a QML plugin (run time link problem)?:
Maybe QML2_IMPORT_PATH could help
Thanks for the hint, I think I'm getting somewhere. I'm going to write up the steps I took to get the application to run incase anyone else runs into this question and finds it helpful.
First I enabled QML_IMPORT_TRACE = 1 in my project's environment variables to let me see the QML paths that it is trying to import.
I've then set the "imports" folder in my build directory onto the QML2_IMPORT_PATH environment variable for my project, in my case this is:
C:\Users\Username\Documents\My Documents\Programs\C++\build-QmlGuiPrototype-Desktop_Qt_5_8_0_MSVC2015_32bit-Debug\imports\
But really this could be anywhere you build a QML plugin to, whether it's a global plugin repository or just a set of project plugins.
I put a breakpoint in registerTypes in my MaterialIconsPlugin.cpp and this now fired, but generated the following error message:
QQmlImportDatabase::resolvePlugin: Could not resolve plugin "materialiconsplugin" in ":" QQmlApplicationEngine failed to load component file::/qmldir:-1 module ":" plugin "materialiconsplugin" not found
This I resolved by removing my qmldir file from my materialicons.qrc I'm not really sure why this was an issue - perhaps someone can enlighten me?
Running windeployqt and then copying across all relevant Qml directories from my Qt installation allows the application to run stand alone, the "MaterialIcons" directory from my build imports directory also needs to be moved into the executable directory.
In short:
Through this process I realised that a QML plugin is much more than just an easy way to load and configure a bunch of QML/C++ files and should be regarded more as an actual library just as any other c++ library would, hence the need to specify the QML2_IMPORT_PATH so that the .dlls can be loaded. The alternative to this seems to be just to make sure that the plugin's folder (not just the .dll) appears along side the .exe and the qml loader will find it automatically. This seems to be how it's demonstrated in the Pie Chart Example.Thanks again for the help @FabriceS, it set me on the right track I think. I'll mark this off as solved in a few days unless anyone has anything to add - though hopefully I've got the right end of the stick now.