Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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:
    File Tree
    Build Tree

    QmlGuiPrototype.pro:

    TEMPLATE = subdirs
    
    SUBDIRS += \
        application\
        materialicons\
    

    application.pro:

    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 +=
    

    materialicons.pro

    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
    


  • Maybe QML2_IMPORT_PATH could help



  • @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.


Log in to reply