How to import custom QML element using qmldir and QML module

If I use import MyModule 1.0 I got an error message what MyModule is not installed.

QQmlApplicationEngine failed to load component
qrc:/main.qml:3:1: module "MyModule" is not installed

But I correctly wrote qmldir file and resources file. And if I use import "MyModule" all is Ok. But I need to use import without quotes and import my modules in all files in different dirrectories. How to correctly import custom Qml element?

File structure

QMLDIRTEST
│   CMakeLists.txt
│   main.cpp
│   main.qml
│   qml.qrc
│
└───MyModule
        qmldir
        RedRectangle.qml

CMakeLists.txt

cmake_minimum_required(VERSION 3.14)
project(QmlDirTest VERSION 0.1 LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(QT NAMES Qt5 COMPONENTS Core Quick REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Quick REQUIRED)

set(PROJECT_SOURCES main.cpp qml.qrc)
add_executable(QmlDirTest ${PROJECT_SOURCES})
target_link_libraries(QmlDirTest PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Quick)

qml.qrc

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
        <file>MyModule/RedRectangle.qml</file>
        <file>MyModule/qmldir</file>
    </qresource>
</RCC>

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif

    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.addImportPath("qrc:/Ui");
    engine.load(url);

    return app.exec();
}

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15
import MyModule 1.0
//import "MyModule"

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    RedRectangle { anchors.centerIn: parent }
}

RedRectangle.qml

import QtQuick 2.15

Rectangle {
    width: 200
    height: 100
    color: "red"
}

qmldir

Module MyModule

RedRectangle 1.0 RedRectangle.qml

Upvotes: 2

Views: 1433

Answers (1)

smr
smr

Reputation: 984

According to the documentation, addImportPath is needed to tell the engine where to search for the new modules that you have provided.

QQmlEngine::addImportPath states:

Adds a path as a directory where the engine searches for installed modules in a URL-based directory structure. The path may be a local filesystem directory, a Qt Resource path (:/imports), a Qt Resource URL (qrc:/imports), or a URL.

After Qt 6.*, there is a qt_add_qml_module function in CMake that you can use to add new modules using only CMake. This function will automatically generate the qmldir and resource files.

qt_add_qml_module(${PROJECT_NAME}
    URI module.uri
    QML_FILES Component1.qml Component2.qml
)

In Qt 6.*, there is a qrc:/qt/qml in the default import path list. By using qt_policy(SET QTP0001 NEW) in your CMake file, the generated resources (using qt_add_qml_module) will have /qt/qml as their default prefix. This allows the engine to detect your module without needing to add any new import paths. (ref: QTP0001)

You can check this by printing engine.importPathList(), and you will see qrc:/qt/qml in the output list.

qDebug << engine.importPathList();
// Output:
// ("path/to/build-directory/", "path/to/Qt/6.6.1/current-kit/qml", "qrc:/qt-project.org/imports", "qrc:/qt/qml")

The Qt 5.* solution

You can use the same trick in Qt 5 as well.
By printing importPathList, you will see that there is /qt-project.org/imports as the default resource path in the import path list:

qDebug << engine.importPathList();
// Output: 
QList(
    "path/to/build-directory/",
    "path/to/Qt/5.15.2/current-kit/qml",
    "qrc:/qt-project.org/imports",
)

So, by adding /qt-project.org/imports to your resource prefix, you can make the engine recognize your provided module. For example:

qml.qrc

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
    </qresource>
    <qresource prefix="/qt-project.org/imports">
        <file>MyModule/RedRectangle.qml</file>
        <file>MyModule/qmldir</file>
    </qresource>
</RCC>

To ensure that the linter recognizes your modules, you can also set the QML_IMPORT_PATH variable in your CMake file.

list(APPEND QML_DIRS "${CMAKE_SOURCE_DIR}/.")
set(QML_IMPORT_PATH "${QML_DIRS}" CACHE STRING "Qt Creator extra qml import paths")

Upvotes: 1

Related Questions