web-dev-qa-db-ja.com

QMLで子プレースホルダーを使用して「テンプレート」を定義するにはどうすればよいですか?

私はQMLが本当に好きです。コンポーネント(クラスと比較して)とそのプロパティを定義し、他の場所(オブジェクトと比較して)からそれらをインスタンス化する方法が好きです。

たとえば、ルックアンドフィールのあるボタンと、その上にラベルテキストを定義できます。これは、たとえば、次のコンポーネント定義(Button.qml)を使用して実行できます。

Item {
    id: button
    property string label

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Text {
            anchors.centerIn: parent
            font.pixelSize: 20
            text: button.label
            color: "white"
        }
    }
}

このメインファイル(main.qml)にインスタンス化されています:

Rectangle {
    width: 300
    height: 200

    Button {
        anchors.centerIn: parent
        anchors.margins: 50
        label: "Hello button!"
    }
}

しかし、次の制限があります。ボタンテンプレートを定義できるのは一部のプロパティを含むのみで、一部のプレースホルダーは定義できません。インスタンスで定義されたすべての子は、少なくともデフォルトでは直接の子になります。この動作を変更したいと思います。

ボタンにアイテム(画像としましょうが、Buttonの定義に画像になることを伝えたくない)を配置したいとします。私はこのようなものを想像します:

Item {
    id: button
    property Item contents   <-- the client can set the placeholder content here

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Item {
            id: placeholder     <-- where the placeholder should be inserted
        }
    }

    Component.onCompleted: {
        // move the contents into the placeholder...
    }
}

どうすればこれを達成できますか? Component.onCompletedを使用することが正しい方法かどうかはわかりません。ただし、私の場合、内容は後で変更されないことに注意してください(少なくとも現在のアプリケーションの設計では...)。

また、プレースホルダー内でアンカーを機能させたいです。たとえば、コンテンツをText要素として定義し、その親(最初はテンプレート自体)を中心に配置するとします。次に、私のコードはこのTextインスタンスをプレースホルダーに移動し、親アンカーはテンプレートアイテムではなく、プレースホルダーアイテムのアンカーになります。

29
leemes

Qt Developer Days 2011 "Qt Quick Best Practices and Design Patterns" のプレゼンテーションで提案された、この質問に対するより良い答えを見つけました。

default property alias ...を使用してalias子アイテムを任意のアイテムの任意のプロパティに使用します。 childrenにエイリアスを付けたくないが、エイリアスプロパティに名前を付けたい場合は、defaultを削除してください。 (リテラルの子は、QML定義ごとにデフォルトプロパティの値です。)

Item {
    id: button
    default property alias contents: placeholder.children

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Item {
            id: placeholder     <-- where the placeholder should be inserted
        }
    }
}
36
leemes

私のように他の誰かがここにたどり着いた場合に備えて、ネクロが答えます。

Qt5のどこかで、デフォルトのプロパティは「children」ではなく「 data "」になりました。これにより、「アイテム」以外のオブジェクトタイプを追加できます。例えば接続も追加できます(上記の私自身の質問に答えるため)

したがって、Qt5では次のことを行う必要があります。

Item {
    id: button
    default property alias contents: placeholder.data

    anchors.fill: parent

    Rectangle {
        anchors.fill: parent
        radius: 10
        color: "gray"

        Item {
            id: placeholder     <-- where the placeholder should be inserted
        }
    }
}

次の点に注意してください:placeholder.data の代わりに placeholder.childrenエイリアス名contentsを使用する必要がないことにも注意してください。これは任意の名前にすることができます。例:

Item {
    id: button
    default property alias foo: placeholder.data
    ...

}
8
Larpon

実際、私が聞いたことからの正解は、 QMLローダー を使用して目的を達成することです。

[言われている;私はまだ実際に試していませんが、それは私の短期的な試用リストにあり、かなり簡単に見えます]

また、stackoverflowで他の "QML Loader" の質問を検索してください。開始するのに役立つ番号があります。

3
Wes Hardaker

要するに(アイデアを示すために):

import QtQuick 1.1

Item {
    width: 200
    height: 100

    //<-- the client can set the placeholder content here
    property Item contents: Rectangle {
        anchors.fill: parent
        anchors.margins: 25
        color: "red"
    }

    Rectangle {
        id: container

        anchors.fill: parent
        radius: 10
        color: "gray"

        //<-- where the placeholder should be inserted
    }

    Component.onCompleted: {
        contents.parent = container
    }
}

やや長いバージョン(コンテンツの再割り当てをサポート):

import QtQuick 1.1

Item {
    width: 200
    height: 100

    //<-- the client can set the placeholder content here
    property Item contents: Rectangle {
        //anchors can be "presupplied", or set within the insertion code
        //anchors.fill: parent
        //anchors.margins: 25
        color: "red"
    }

    Rectangle {
        id: container

        anchors.fill: parent
        radius: 10
        color: "gray"

        //<-- where the placeholder should be inserted
        //Item {
        //    id: placeholder
        //}
    }

    //"__" means private by QML convention
    function __insertContents() {
        // move the contents into the placeholder...
        contents.parent = container
        contents.anchors.fill = container
        contents.anchors.margins = 25
    }

    onContentsChanged: {
        if (contents !== null)
            __insertContents()
    }

    Component.onCompleted: {
        __insertContents()
    }
}

お役に立てれば :)

1
mlvljr

次のコードを使用して、アイテムを移動できます(プレースホルダー内で複数のアイテムをサポートする場合)。

property list<Item> contents

Component.onCompleted: {
    var contentItems = [];
    for(var i = 0; i < contents.length; ++i)
        contentItems.Push(contents[i]);
    placeholder.children = contentItems;
}

QMLエンジンはリストプロパティに対しても単一の値を受け入れるため、contentsプロパティにアイテムのリストを指定する必要はないことに注意してください。

1
Misch