このページでは

QMLモジュールの作成

以下の目的で、CMakeのQMLモジュールAPIを使用してQMLモジュールを宣言する必要があります:

  • qmldirおよび*.qmltypes ファイルを生成します。
  • QML_ELEMENT でアノテーションされたC++型を登録する。
  • QMLファイルとC++ベースの型を同じモジュールに統合する。
  • すべての QML ファイルに対してqmlcachegenを実行する。
  • モジュール内で QML ファイルのプリコンパイル済みバージョンを使用します。
  • 物理ファイルシステムとリソースファイルシステムの両方にモジュールを提供します。
  • バッキングライブラリとオプションのプラグインを作成します。実行時にプラグインが読み込まれるのを避けるため、バッキングライブラリをアプリケーションにリンクします。

上記の操作はすべて個別に設定することも可能です。詳細については、CMake QML モジュール API を参照してください。

1つのバイナリに複数のQMLモジュールを組み込む

1つのバイナリに複数のQMLモジュールを追加できます。各モジュールに対してCMakeターゲットを定義し、そのターゲットを実行ファイルにリンクします。追加のターゲットがすべて静的ライブラリである場合、結果は複数のQMLモジュールを含む1つのバイナリとなります。要するに、次のようなアプリケーションを作成できます:

myProject
    | - CMakeLists.txt
    | - main.cpp
    | - main.qml
    | - onething.h
    | - onething.cpp
    | - ExtraModule
        | - CMakeLists.txt
        | - Extra.qml
        | - extrathing.h
        | - extrathing.cpp

まず、main.qml に Extra.qml のインスタンス化が含まれていると仮定しましょう:

import ExtraModule
Extra { ... }

この追加モジュールは、メインプログラムにリンクできるように、静的ライブラリである必要があります。そのため、ExtraModule/CMakeLists.txtに次のように記述します:

# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

qt_add_library(extra_module STATIC)
qt_add_qml_module(extra_module
    URI "ExtraModule"
    VERSION 1.0
    QML_FILES
        Extra.qml
    SOURCES
        extrathing.cpp extrathing.h
    RESOURCE_PREFIX /
)

これにより、2つのターゲットが生成されます。バックグラウンドライブラリ用のextra_module と、プラグイン用のextra_moduleplugin です。プラグインも静的ライブラリであるため、実行時にロードすることはできません。

myProject/CMakeLists.txt では、main.qml および onething.h で宣言されたすべての型が属する QML モジュールを指定する必要があります:

qt_add_executable(main_program main.cpp)

qt_add_qml_module(main_program
    VERSION 1.0
    URI myProject
    QML_FILES
        main.qml
    SOURCES
        onething.cpp onething.h

)

そこから、追加のモジュール用のサブディレクトリを追加します:

add_subdirectory(ExtraModule)

追加モジュールのリンクが正しく行われるようにするには、次の操作が必要です:

  • 追加モジュール内でシンボルを定義します。
  • メインプログラムからそのシンボルへの参照を作成します。

QML プラグインには、この目的で使用できるシンボルが含まれています。Q_IMPORT_QML_PLUGIN マクロを使用すると、このシンボルへの参照を作成できます。main.cpp に以下のコードを追加してください:

#include <QtQml/QQmlExtensionPlugin>
Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)

ExtraModulePlugin は、生成されたプラグインクラスの名前です。これは、モジュールURIの末尾にPlugin を付加したもので構成されます。次に、メインプログラムのCMakeLists.txtで、バッキングライブラリではなく、このプラグインをメインプログラムにリンクします:

target_link_libraries(main_program PRIVATE extra_moduleplugin)

バージョン

QMLには、コンポーネントやモジュールにバージョンを割り当てる複雑なシステムがあります。ほとんどの場合、以下のようにしてこれらをすべて無視すべきです:

  1. インポート文にバージョンを絶対に指定しない
  2. qt_add_qml_moduleでバージョンを指定しない
  3. QML_ADDED_IN_VERSION やQT_QML_SOURCE_VERSIONS を絶対に使用しない
  4. Q_REVISION やREVISION() 属性をQ_PROPERTY
  5. 修飾なしのアクセスを避ける
  6. インポートネームスペースを積極的に使用する

バージョン管理は、理想的には言語自体の外部で処理されます。たとえば、QMLモジュールの異なるセットごとに別々のインポートパスを用意することができます。あるいは、オペレーティングシステムが提供するバージョン管理メカニズムを使用して、QMLモジュールを含むパッケージをインストールまたはアンインストールすることもできます。

場合によっては、Qt独自のQmlモジュールでも、インポートするバージョンによって動作が異なることがあります。特に、Qmlコンポーネントにプロパティが追加された際、コード内で同名の別のプロパティに対して修飾子なしのアクセスを行っている場合、コードは動作しなくなります。 次の例では、topLeftRadius プロパティがQt 6.7で追加されたため、コードの動作はQtのバージョンによって異なります。

import QtQuick

Item {
    // property you want to use
    property real topLeftRadius: 24

    Rectangle {

        // correct for Qt version < 6.7 but uses Rectangle's topLeftRadius in 6.7
        objectName: "top left radius:" + topLeftRadius
    }
}

この問題の解決策は、修飾子なしのアクセスを避けることです。qmllintを使用すると、このような問題を見つけることができます。次の例では、意図したプロパティに、安全な修飾付きアクセスを行っています:

import QtQuick

Item {
    id: root

    // property you want to use
    property real topLeftRadius: 24

    Rectangle {

        // never mixes up topLeftRadius with unrelated Rectangle's topLeftRadius
        objectName: "top left radius:" + root.topLeftRadius
    }
}

また、QtQuick の特定のバージョンをインポートすることで、互換性の問題を回避することもできます:

// make sure Rectangle has no topLeftRadius property
import QtQuick 6.6

Item {
    property real topLeftRadius: 24
    Rectangle {
        objectName: "top left radius:" + topLeftRadius
    }
}

バージョン管理によって解決されるもう一つの問題は、異なるモジュールによってインポートされた QML コンポーネントが互いにシャドウイングし合う可能性があるという事実です。次の例では、もしMyModule が新しいバージョンでRectangle という名前のコンポーネントを導入した場合、このドキュメントによって作成されるRectangle は、もはやQQuickRectangle ではなく、MyModule で導入された新しいRectangle になってしまいます。

import QtQuick
import MyModule

Rectangle {
    // MyModule's Rectangle, not QtQuick's
}

このシャドウイングを回避する良い方法は、次のようにQtQuick および/またはMyModule を型ネームスペースにインポートすることです。

import QtQuick as QQ
import MyModule as MM

QQ.Rectangle {
   // QtQuick's Rectangle
}

あるいは、MyModule を固定バージョンでインポートし、新しいコンポーネントがQML_ADDED_IN_VERSION またはQT_QML_SOURCE_VERSIONS を通じて正しいバージョンタグを受け取るようにすれば、同様にオーバーライドを回避できます:

import QtQuick 6.6

// Types introduced after 1.0 are not available, like Rectangle for example
import MyModule 1.0

Rectangle {
    // QtQuick's Rectangle
}

これを機能させるには、MyModule でバージョンを使用する必要があります。注意すべき点がいくつかあります。

バージョンを追加する場合は、すべての場所に追加してください

qt_add_qml_module にVERSION 属性を追加する必要があります。バージョンには、モジュールが提供する最新バージョンを指定してください。同じメジャーバージョン内の古いマイナーバージョンは自動的に登録されます。古いメジャーバージョンについては、以下を参照してください。

モジュールのバージョンx.0 で導入されなかったすべての型に対して、QML_ADDED_IN_VERSION またはQT_QML_SOURCE_VERSIONSを追加する必要があります。ここで、x は現在のメジャーバージョンを指します。

バージョンタグの追加を忘れると、そのコンポーネントはすべてのバージョンで利用可能となり、バージョン管理が機能しなくなります。

ただし、QML で定義されたプロパティ、メソッド、およびシグナルにバージョンを追加する方法はありません。QML ドキュメントにバージョンを付ける唯一の方法は、変更ごとに個別のQT_QML_SOURCE_VERSIONS を指定して新しいドキュメントを追加することです。

バージョンは推移的ではない

モジュールA のコンポーネントが、別のモジュールB をインポートし、そのモジュールの型をルート要素としてインスタンス化する場合、ユーザーがどのバージョンのA をインポートしたかに関係なく、結果として得られるコンポーネントから利用可能なプロパティには、B のインポートバージョンが適用されます。

モジュール `A` 内のバージョン `2.6 ` を持つファイル `TypeFromA.qml ` を考えてみましょう:

import B 2.7

// Exposes TypeFromB 2.7, no matter what version of A is imported
TypeFromB { }

ここで、TypeFromA を使用するユーザーについて考えてみましょう:

import A 2.6

// This is TypeFromB 2.7.
TypeFromA { }

ユーザーはバージョン2.6 を期待していますが、実際には基底クラスTypeFromB のバージョン2.7 が取得されてしまいます。

したがって、安全を期すためには、プロパティを自分で追加する際だけでなく、インポートするモジュールのバージョンを上げる際にも、QMLファイルを複製して新しいバージョンを割り当てる必要があります。

修飾付きアクセスではバージョン管理が適用されない

バージョン管理は、型のメンバまたは型自体への修飾されていないアクセスにのみ影響します。topLeftRadius の例で、this.topLeftRadius と記述した場合、Qt 6.7を使用していれば、import QtQuick 6.6 であっても、そのプロパティは解決されます。

バージョンとリビジョン

QML_ADDED_IN_VERSION Q_REVISION Q_PROPERTY REVISION() QMetaMethod::revision および で公開されている リビジョンと密接に紐づいたバージョンのみを宣言できます。これは、型階層内のすべての型が同じバージョン管理スキームに従わなければならないことを意味します。これには、Qt自体が提供し、あなたが継承している型も含まれます。QMetaProperty::revision metaobject's

qmlRegisterType および関連関数を使用すると、メタオブジェクトのリビジョンと型バージョン間の任意のマッピングを登録できます。その場合、Q_REVISION の単一引数形式と、Q_PROPERTY のREVISION 属性を使用する必要があります。ただし、これはかなり複雑で分かりにくくなる可能性があるため、推奨されません。

同一モジュールからの複数のメジャーバージョンのエクスポート

qt_add_qml_module は、個々の型がQT_QML_SOURCE_VERSIONSやQ_REVISION を通じて追加された特定のバージョンで他のバージョンを宣言している場合でも、デフォルトでは VERSION 引数で指定されたメジャーバージョンを考慮します。 モジュールが複数のバージョンで利用可能な場合、個々のQMLファイルがどのバージョンで利用可能かを決定する必要があります。さらなるメジャーバージョンを宣言するには、qt_add_qml_module のPAST_MAJOR_VERSIONS オプションや、個々のQMLファイルのQT_QML_SOURCE_VERSIONS プロパティを使用できます。

set_source_files_properties(Thing.qml
    PROPERTIES
        QT_QML_SOURCE_VERSIONS "1.4;2.0;3.0"
)

set_source_files_properties(OtherThing.qml
    PROPERTIES
        QT_QML_SOURCE_VERSIONS "2.2;3.0"
)

qt_add_qml_module(my_module
    URI MyModule
    VERSION 3.2
    PAST_MAJOR_VERSIONS
        1 2
    QML_FILES
        Thing.qml
        OtherThing.qml
        OneMoreThing.qml
    SOURCES
        everything.cpp everything.h
)

MyModule はメジャーバージョン 1、2、3 で利用可能です。利用可能な最大バージョンは 3.2 です。x が正の値であれば、バージョン 1.x または 2.x のいずれでもインポートできます。Thing.qml および OtherThing.qml には、明示的なバージョン情報を追加しました。 Thing.qmlはバージョン1.4以降で利用可能であり、OtherThing.qmlはバージョン2.2以降で利用可能です。メジャーバージョンを上げる際にモジュールからQMLファイルを削除する可能性があるため、各set_source_files_properties() でも新しいバージョンを指定する必要があります。 OneMoreThing.qml には明示的なバージョン情報はありません。これは、OneMoreThing.qml がマイナーバージョン 0 以降、すべてのメジャーバージョンで利用可能であることを意味します。

この設定では、生成された登録コードは、各メジャーバージョンに対して `qmlRegisterModule()` を使用してモジュール `versions ` を登録します。これにより、すべてのバージョンをインポートできるようになります。

カスタムディレクトリ構成

QMLモジュールを構造化する最も簡単な方法は、URIに基づいて名付けられたディレクトリにそれらを配置することです。たとえば、モジュール My.Extra.Module は、それを使用するアプリケーションを基準として、My/Extra/Module というディレクトリに配置されます。こうすることで、実行時や各種ツールから簡単に見つけることができます。

より複雑なプロジェクトでは、この規約では制限が厳しすぎる場合があります。たとえば、プロジェクトのルートディレクトリが散らかるのを防ぐために、すべてのQMLモジュールを1か所にまとめたい場合や、単一のモジュールを複数のアプリケーションで再利用したい場合などです。そのような場合は、QT_QML_OUTPUT_DIRECTORY をRESOURCE_PREFIX およびIMPORT_PATHと組み合わせて使用できます。

QMLモジュールを特定の出力ディレクトリ(例えば、ビルドディレクトリQT_QML_OUTPUT_DIRECTORY 内のサブディレクトリ「qml」)に集約するには、最上位の CMakeLists.txt で次のように設定します:

set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)

QMLモジュールの出力ディレクトリは新しい場所に移動します。同様に、qmllint およびqmlcachegen の呼び出しも、新しい出力ディレクトリをインポートパスとして使用するように自動的に調整されます。新しい出力ディレクトリはデフォルトのQMLインポートパスの一部ではないため、QMLモジュールが検出されるように、実行時に明示的に追加する必要があります。

物理ファイルシステムの処理は完了しましたが、リソースファイルシステム内の別の場所にQMLモジュールを移動させたい場合もあるでしょう。これには、RESOURCE_PREFIXオプションを使用します。 このオプションは、各 `qt_add_qml_module` で個別に指定する必要があります。そうすることで、QML モジュールは指定されたプレフィックスの下に配置され、URI から生成されたターゲットパスが末尾に追加されます。例えば、次のモジュールを考えてみましょう。

qt_add_qml_module(
    URI My.Great.Module
    VERSION 1.0
    RESOURCE_PREFIX /example.com/qml
    QML_FILES
        A.qml
        B.qml
)

これにより、リソースファイルシステムに `example.com/qml/My/Great/Module ` ディレクトリが追加され、上記で定義された QML モジュールがその中に配置されます。 モジュールは物理ファイルシステム上でも見つかるため、厳密にはQMLのインポートパスにリソースプレフィックスを追加する必要はありません。しかし、ほとんどのモジュールにおいて、リソースファイルシステムからの読み込みは物理ファイルシステムからの読み込みよりも高速であるため、一般的にQMLのインポートパスにリソースプレフィックスを追加することをお勧めします。

QMLモジュールを、複数のインポートパスを持つ大規模なプロジェクトで使用する場合は、追加の手順が必要です。実行時にインポートパスを追加しても、qmllint などのツールはそれにアクセスできないため、正しい依存関係を見つけられない可能性があります。IMPORT_PATH を使用して、ツールが考慮すべき追加のパスを指定してください。 例:

qt_add_qml_module(
    URI My.Dependent.Module
    VERSION 1.0
    QML_FILES
        C.qml
    IMPORT_PATH "/some/where/else"
)

実行時のファイルシステムへのアクセス排除

すべての QML モジュールが常にリソースファイルシステムから読み込まれる場合、アプリケーションを単一のバイナリとしてデプロイできます。

QTP0001ポリシーが NEW に設定されている場合、qt_add_qml_module() のRESOURCE_PREFIX 引数はデフォルトで/qt/qml/ となります。したがって、モジュールはリソースファイルシステム内の:/qt/qml/ に配置されます。これはデフォルトのQMLインポートパスの一部ですが、Qt自体では使用されません。アプリケーション内で使用するモジュールについては、ここが適切な配置場所です。

代わりにカスタムリソースプレフィックス `RESOURCE_PREFIX` を指定した場合は、そのカスタムリソースプレフィックスをQML インポートパスに追加する必要があります。複数のリソースプレフィックスを追加することも可能です:

QQmlEngine qmlEngine;
qmlEngine.addImportPath(QStringLiteral(":/my/resource/prefix"));
qmlEngine.addImportPath(QStringLiteral(":/other/resource/prefix"));
// Use qmlEngine to load the main.qml file.

これは、サードパーティ製ライブラリを使用する際に、モジュール名の競合を回避するために必要になる場合があります。それ以外の場合は、カスタムリソースプレフィックスの使用は推奨されません。

:/qt-project.org/imports/ というパスも、デフォルトのQML インポートパスに含まれています。異なるプロジェクトや Qt バージョン間で頻繁に再利用されるモジュールについては、:/qt-project.org/imports/ をリソースプレフィックスとして使用しても問題ありません。ただし、Qt 独自の QML モジュールもそこに配置されています。それらを上書きしないよう注意する必要があります。

不要なインポートパスを追加しないでください。そうすると、QMLエンジンがモジュールを誤った場所で見つけてしまう可能性があります。これにより、特定の環境でのみ再現される問題が発生する恐れがあります。

カスタム QML プラグインの統合

QMLモジュールにimage provider をバンドルする場合は、QQmlEngineExtensionPlugin::initializeEngine() メソッドを実装する必要があります。これに伴い、独自のプラグインを作成する必要があります。このユースケースに対応するために、NO_GENERATE_PLUGIN_SOURCEを使用できます。

独自のプラグインソースを提供するモジュールを例に考えてみましょう。

qt_add_qml_module(imageproviderplugin
    VERSION 1.0
    URI "ImageProvider"
    PLUGIN_TARGET imageproviderplugin
    NO_PLUGIN_OPTIONAL
    NO_GENERATE_PLUGIN_SOURCE
    CLASS_NAME ImageProviderExtensionPlugin
    QML_FILES
        AAA.qml
        BBB.qml
    SOURCES
        moretypes.cpp moretypes.h
        myimageprovider.cpp myimageprovider.h
        plugin.cpp
)

myimageprovider.h 内で、次のように画像プロバイダを宣言できます:

class MyImageProvider : public QQuickImageProvider
{
    [...]
};

次に、plugin.cpp でQQmlEngineExtensionPlugin を次のように定義します:

#include <myimageprovider.h>
#include <QtQml/qqmlextensionplugin.h>

class ImageProviderExtensionPlugin : public QQmlEngineExtensionPlugin
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
public:
    void initializeEngine(QQmlEngine *engine, const char *uri) final
    {
        Q_UNUSED(uri);
        engine->addImageProvider("myimg", new MyImageProvider);
    }
};

これにより、イメージプロバイダが利用可能になります。プラグインとバックエンドライブラリは、どちらも同じ CMake ターゲット imageproviderplugin に含まれています。これは、さまざまなシナリオにおいてリンカがモジュールの一部を削除してしまうことを防ぐためです。

このプラグインはイメージプロバイダを作成するため、単純なinitializeEngine 関数ではなくなりました。したがって、このプラグインはもはやオプションではなくなりました。

「Qt Qml の変更点」、「QML モジュールの近代化」、および「QML モジュールの CMake への移植」も参照してください 。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.