JavaScriptからの動的なQMLオブジェクト生成

QML では、JavaScript からオブジェクトを動的に生成することができます。これは、オブジェクトのインスタンス化を必要な時まで遅らせ、 アプリケーションの起動時間を短縮するのに便利です。また、ビジュアルオブジェクトを動的に生成し、ユーザー入力やその他のイベントに応じてシーンに追加することもできます。

オブジェクトの動的作成

JavaScriptから動的にオブジェクトを作成するには、2つの方法があります。Qt.createComponent() を呼び出してComponent オブジェクトを動的に作成する方法と、Qt.createQmlObject() を使って QML の文字列からオブジェクトを作成する方法です。QML文書に既存のコンポーネントが定義されていて、そのコンポーネントのインスタンスを動的に生成したい場合は、コンポーネントを作成する方がよいでしょう。そうでない場合は、QMLの文字列からオブジェクトを生成する方法が便利で す。

コンポーネントの動的生成

QMLファイルで定義されたコンポーネントを動的にロードするには、Qt objectQt.createComponent() 関数を呼び出します。この関数はQMLファイルのURLを唯一の引数として受け取り、このURLからComponent オブジェクトを生成します。

Component を作成したら、createObject() メソッドを呼び出して、 コンポーネントのインスタンスを作成することができます。この関数は1つまたは2つの引数を取ります:

  • 最初の引数は新しいオブジェクトの親です。親はグラフィカル・オブジェクト(すなわちItem 型)または非グラフィカル・オブジェクト(すなわちQtObject または C++QObject 型)です。グラフィカルな親オブジェクトを持つグラフィカル・オブジェクトだけが、ビジュアル・キャンバスにレンダリングされます。 Qt Quickにレンダリングされます。後で親を設定したい場合は、null をこの関数に渡すと安全です。
  • 2番目はオプションで、オブジェクトの初期プロパティ値を定義するプロパティ値ペアのマップです。この引数で指定されたプロパティ値は、オブジェクトの作成が確定する前にオブジェクトに適用され、他のプロパティのバインディングを有効にするために特定のプロパティを初期化しなければならない場合に発生する可能性のあるバインディングエラーを回避します。さらに、オブジェクトが作成された後にプロパティ値とバインディングを定義する場合と比較すると、パフォーマンス上の利点はわずかです。

以下はその例です。まず、シンプルなQMLコンポーネントを定義したSprite.qml があります:

import QtQuick

Rectangle { width: 80; height: 50; color: "red" }

メイン・アプリケーション・ファイルであるmain.qml は、Sprite オブジェクトを生成するcomponentCreation.js JavaScript ファイルをインポートしています:

import QtQuick
import "componentCreation.js" as MyScript

Rectangle {
    id: appWindow
    width: 300; height: 300

    Component.onCompleted: MyScript.createSpriteObjects();
}

以下はcomponentCreation.js です。createObject() を呼び出す前に、コンポーネントstatusComponent.Ready であるかどうかをチェックしています。

var component;
var sprite;

function createSpriteObjects() {
    component = Qt.createComponent("Sprite.qml");
    if (component.status == Component.Ready)
        finishCreation();
    else
        component.statusChanged.connect(finishCreation);
}

function finishCreation() {
    if (component.status == Component.Ready) {
        sprite = component.createObject(appWindow, {x: 100, y: 100});
        if (sprite == null) {
            // Error Handling
            console.log("Error creating object");
        }
    } else if (component.status == Component.Error) {
        // Error Handling
        console.log("Error loading component:", component.errorString());
    }
}

読み込まれるQMLファイルがローカルファイルであることが確実であれば、finishCreation() 関数を省略し、createObject() をすぐに呼び出すことができます:

function createSpriteObjects() {
    component = Qt.createComponent("Sprite.qml");
    sprite = component.createObject(appWindow, {x: 100, y: 100});

    if (sprite == null) {
        // Error Handling
        console.log("Error creating object");
    }
}

Qt Quickどちらの場合も、appWindow を親引数としてcreateObject() を呼び出しています。作成されたオブジェクトは、main.qmlappWindow オブジェクトの子となり、シーンに表示されます。

相対パスでファイルを使用する場合、パスはQt.createComponent() が実行されるファイルからの相対パスでなければなりません。

動的に作成されるオブジェクトにシグナルを接続する(またはオブジェクトからシグナルを受信する)には、シグナルconnect() メソッドを使用します。詳しくは、シグナルをメソッドやシグナルに接続するを参照してください。

また、incubateObject() 関数を使用して、ブロックせずにコンポーネントをインスタンス化することも可能です。

QMLの文字列からオブジェクトを生成する

警告 QMLの文字列からオブジェクトを生成するのは非常に時間がかかります。さらに、プログラム的に QML コードを構成する場合、無効な QML を生成するのはとても簡単です。文字列操作によって新たなコンポーネントを生成するよりも、QMLコンポーネントを別ファイルとして保持し、プロパティやメソッドを追加して動作をカスタマイズする方がずっとよいでしょう。

実行時までQMLが定義されていない場合、次の例のようにQt.createQmlObject()関数を使うことで、QMLの文字列からQMLオブジェクトを生成することができます:

const newObject = Qt.createQmlObject(`
    import QtQuick

    Rectangle {
        color: "red"
        width: 20
        height: 20
    }
    `,
    parentItem,
    "myDynamicSnippet"
);

最初の引数は作成するQMLの文字列です。最初の引数は、作成するQMLの文字列です。新規ファイルと同じように、使いたい型があればインポートする必要があります。第2引数は、新しいオブジェクトの親オブジェクトです。コンポーネントに適用される親引数のセマンティクスは、createQmlObject() にも同様に適用されます。第3引数は新しいオブジェクトに関連付けるファイルパスです。

QMLの文字列が相対パスを使ってファイルをインポートする場合、パスは親オブジェクト(メソッドの第2引数)が定義されているファイルからの相対パスでなければなりません。

重要: 静的QMLアプリケーションをビルドする際、QMLファイルはインポート依存性を検出するためにスキャンされます。これにより、必要なプラグインやリソースはコンパイル時にすべて解決されます。ただし、考慮されるのは明示的なimport文(QMLファイルの先頭にあるもの)のみで、文字列リテラルで囲まれたimport文は考慮されません。そのため、静的ビルドをサポートするためには、Qt.createQmlObject()を使用するQMLファイルにおいて、文字列リテラルの中だけでなく、ファイルの先頭にも必要なインポートを明示的に記述する必要があります。

動的に作成されるオブジェクトの管理

動的に作成されるオブジェクトを管理する場合、作成されたオブジェクトよりも作成コンテキストの方が長生きするようにしなければなりません。そうしないと、作成コンテキストが先に破棄された場合、動的オブジェクトのバインディングやシグナル・ハンドラが動作しなくなります。

実際の作成コンテキストは、オブジェクトの作成方法によって異なります:

  • Qt.createComponent() が使用された場合、作成コンテキストはこのメソッドが呼び出されたQQmlContext となります。
  • Qt.createQmlObject() が呼び出された場合、作成コンテキストはこのメソッドに渡された親オブジェクトのコンテキストになります。
  • Component{} オブジェクトが定義され、そのオブジェクトに対してcreateObject() またはincubateObject() が呼び出された場合、作成コンテキストはComponent が定義されたコンテキストとなります。

また、動的に生成されたオブジェクトは他のオブジェクトと同じように使用することができますが、QMLではidを持たないことに注意してください。

オブジェクトの動的削除

多くのユーザーインターフェースでは、ビジュアルオブジェクトを削除するのではなく、ビジュアルオブジェクトの不透明度を0に設定したり、ビジュアルオブジェクトを画面外に移動させるだけで十分です。しかし、動的に生成されるオブジェクトが多い場合、未使用のオブジェクトを削除した方がパフォーマンスに有利な場合があります。

便利な QML オブジェクトファクトリ(LoaderRepeater など)によって動的に生成されたオブジェクトは、決して手動で削除してはいけないことに注意してください。また、自分で動的に生成していないオブジェクトの削除も避けるべきです。

アイテムの削除にはdestroy() メソッドを使います。このメソッドにはオプションの引数 (デフォルトは 0) があり、 オブジェクトが破棄されるまでのおおよその遅延時間をミリ秒単位で指定します。

以下はその例です。application.qmlSelfDestroyingRect.qml コンポーネントのインスタンスを5つ作成する。各インスタンスはNumberAnimation を実行し、アニメーションが終了すると、ルートオブジェクトのdestroy() を呼び出して自身を破棄します:

application.qml
import QtQuick

Item {
    id: container
    width: 500; height: 100

    Component.onCompleted: {
        var component = Qt.createComponent("SelfDestroyingRect.qml");
        for (var i=0; i<5; i++) {
            var object = component.createObject(container);
            object.x = (object.width + 10) * i;
        }
    }
}
SelfDestroyingRect.qml
import QtQuick

Rectangle {
    id: rect
    width: 80; height: 80
    color: "red"

    NumberAnimation on opacity {
        to: 0
        duration: 1000

        onRunningChanged: {
            if (!running) {
                console.log("Destroying...")
                rect.destroy();
            }
        }
    }
}

あるいは、application.qml は、object.destroy() を呼び出して、作成されたオブジェクトを破壊することもできます。

あるいは、、 を呼び出すことで、作成されたオブジェクトを破壊することもできます。オブジェクトの中でdestroy()を呼び出すことは安全であることに注意してください。オブジェクトは、destroy()が呼び出された瞬間に破棄されるのではなく、スクリプト・ブロックの終了から次のフレームまでの間にクリーンアップされます(ゼロ以外の遅延を指定した場合を除く)。

また、このようにSelfDestroyingRect インスタンスを静的に作成した場合にも注意が必要です:

Item {
    SelfDestroyingRect {
        // ...
    }
}

オブジェクトが動的に破棄されるのは、動的に生成された場合のみだからです。

Qt.createQmlObject() で作成されたオブジェクトは、同様にdestroy() で破棄することができます:

const newObject = Qt.createQmlObject(`
    import QtQuick

    Rectangle {
        color: "red"
        width: 20
        height: 20
    }
    `,
    parentItem,
    "myDynamicSnippet"
);
newObject.destroy(1000);

© 2025 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.