Подтвердить что ты не робот

Как определить "шаблон" с дочерними заполнителями в 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 в заполнитель, а родительские привязки должны быть такими же, что и в элементе-заполнителе, а не в элементе шаблона.

4b9b3361

Ответ 1

Я нашел гораздо более хороший ответ на этот вопрос, предложенный в презентации Qt Developer Days 2011 "Краткие рекомендации и шаблоны Qt" .

Они используют default property alias ... для псевдонимов дочерних элементов для любого свойства любого элемента. Если вы не хотите использовать псевдоним для детей, но присваиваете свойству псевдоним имя, просто удалите 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
        }
    }
}

Ответ 2

Некро отвечает на случай, если кто-то еще окажется здесь, как я.

В Qt5 где-то вдоль строки свойство по умолчанию стало "data", а не "children". Это позволяет добавлять объекты других типов, кроме "Item". например Соединения также могут быть добавлены (чтобы ответить на мой вопрос выше)

Так что в 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
    ...

}

Ответ 3

Собственно, правильный ответ из того, что я слышал, заключается в использовании QML Loader, чтобы выполнить то, что вы хотите.

[это сказано; Я еще не пробовал это, но это в моем ближайшем списке задач и выглядит довольно прямолинейно]

Кроме того, поиск stackoverflow для другого "QML Loader" задает вопросы, так как есть число, которое поможет вам начать работу.

Ответ 4

Вы можете переместить элементы (если вы хотите поддерживать несколько элементов в заполнителе) с помощью этого фрагмента кода:

property list<Item> contents

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

Обратите внимание, что вам не нужно предоставлять список элементов для свойства содержимого, так как одиночные значения будут приниматься движком QML также для свойств списка.

Ответ 5

Короче (чтобы показать идею):

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()
    }
}

Надеюсь, что это поможет:)