QMLコンポーネントを動的に生成させる

Dynamically QML components creating
既存のQMLに、新しいQMLを動的に追加し、常に使用しないコンポーネントを後から追加できるようにさせることで、初期動作やメモリーリソースの効率化を図ることができます。

「Qt.createComponent」でコンポーネントを作成し、「Component.createObject」でQMLの実体を対象のアイテム内に追加するのが基本的な流れになります。

プロジェクトに任意のQt Quickファイルを追加し、後から追加したいQMLのデータを記入します。
Qt.createComponentの第1引数には読み込みたいQMLファイル、第2引数には読み込みモードを指定します。

読み込みモードには、同期読み込み「Component.PreferSynchronous」と、非同期読み込み「Component.Asynchronous」のいずれかを選択でき、引数を省略した際は同期モードでの読み込みとなります。

createComponentでは作成されたComponentオブジェクトが返されます。コンポーネントの作成が完了すると、このComponentのstatusプロパティが「Component.Ready」になるので、それを確認したら、「Component.createObject([作成する親アイテム],[生成時に追加するプロパティ])」で実体を作成します。

まずは同期モードでの作成例です。ボタンを押すたびにウィンドウ内にRect.qmlで定義したアイテムが次々と追加されていきます。
sync.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.0

Window {
    id: mainwin
    visible: true
    width: 640
    height: 480
    title: qsTr("Dynamical Object")

    Button{
        text: "Generate"
        property Component c: null

        function intRandom(mn, mx){
            // mnからmxの範囲でランダムな整数を生成
            return Math.floor((Math.random() * ((mx + 1) - mn)) + mn);
        }

        onClicked: {
            // qmlファイルよりコンポーネントの作成
            if(!c) c = Qt.createComponent("Rect.qml");

            // コンポーネントが正しく取得できたら、mainwinにオブジェクトを生成
            if(c.status == Component.Ready){
                var x = intRandom(0, mainwin.width - 100);
                var y = intRandom(0, mainwin.height - 100);
                var i = c.createObject(mainwin, {x:x, y:y});
            }
        }
    }
}
続いて非同期モードでの作成例です。Windowコンポーネントの生成が完了すると「Component.onCompleted」が呼び出されるので、ここでRect.qmlの非同期読み込みを実行しています。イベント関数(スロット)への登録は「connect」メソッドを使用します。
async.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.0

Window {
    id: mainwin
    visible: true
    width: 640
    height: 480
    title: qsTr("Dynamical Object")

    property Component c: null

    function intRandom(mn, mx){
        return Math.floor((Math.random() * ((mx + 1) - mn)) + mn);
    }

    function onComponentStateChanged(){
        if(c.status == Component.Ready){
            btn.enabled = true;
        }
    }

    Component.onCompleted:{
        c = Qt.createComponent("Rect.qml", Component.Asynchronous);
        c.statusChanged.connect(onComponentStateChanged);
        // これは間違い
        // c.onStateChanged = onComponentStateChanged;
    }

    Button{
        text: "Generate"
        id: btn
        // 非同期読み込みが成功するまでボタンを無効に
        enabled: false

        onClicked: {
            var x = intRandom(0, mainwin.width - 100);
            var y = intRandom(0, mainwin.height - 100);
            var i = c.createObject(mainwin, {x:x, y:y});
        }
    }
}
参照先のファイルにはURLも指定できますが、ほとんどのアプリストアではアプリに初めから含まれていないスクリプトを実行することを禁止しています。不要なトラブルを避けるためにも、アプリ外からのQMLの読み込みは避けたほうがよいでしょう。

動的に作成したオブジェクトは「destroy()」で破棄することができます。

「Qt.createQmlObject()」を使うと、文字列からQMLオブジェクトを作成できます。第1引数にはQML構文を、第2引数には追加する親アイテムを指定します。
fromstr.qml
try{
    item = Qt.createQmlObject(
        'import QtQuick 2.0; Rectangle ' +
        '{color: "red"; width: 20; height: 20}',
        mainwin);
}catch(e){
    // 作成に失敗したときは例外が発生し、qmlErros配列に情報が格納される
    console.log(e.qmlErros[0].message);
}
2018/11/01