Spec-Zone.ru › Qt 5.11

Модели и представления в Qt Quick

Проще говоря, приложениям необходимо формировать данные и отображать их. Qt Quick использует понятия моделей, представлений и делегатов для отображения данных. Они модулизируют визуализацию данных, предоставляя разработчику или дизайнеру контроль над различными аспектами данных. Разработчик может заменить представление списка на представление сетки с небольшими изменениями в данных. Аналогично, инкапсулирование экземпляра данных в делегат позволяет разработчику определять способ представления или обработки данных.

  • Модель - содержит данные и их структуру. Существуют различные типы QML для создания моделей.
  • Представление - контейнер, отображающий данные. Представление может отображать данные в виде списка или сетки.
  • Делегат - определяет, как данные должны отображаться в представлении. Делегат берет каждый элемент данных из модели и инкапсулирует его. Данные доступны через делегат.

Для визуализации данных свяжите свойство model представления с моделью, а свойство delegate - с компонентом или другим совместимым типом.

Отображение данных с помощью представлений

Представления являются контейнерами для наборов элементов. Они обладают богатым функционалом и могут быть настраиваемыми для удовлетворения требований к стилю или поведению.

В базовом наборе графических типов Qt Quick предоставляется набор стандартных представлений:

  • ListView - упорядочивает элементы в горизонтальный или вертикальный список
  • GridView - упорядочивает элементы в сетку в доступном пространстве
  • PathView - упорядочивает элементы по пути

Эти типы имеют свойства и поведение, уникальные для каждого типа. Подробнее см. в соответствующей документации.

Декорирование представлений

Представления позволяют настраивать визуальное оформление через свойства декораций, такие как header, footer, и section свойства. Связывая объект, обычно другой визуальный объект, с этими свойствами, представления могут быть декорированы. Подвал может включать тип Rectangle, демонстрирующий границы, или заголовок, отображающий логотип в верхней части списка.

Предположим, что определенный клуб хочет декорировать список своих членов в своих фирменных цветах. Список членов находится в model, и delegate отобразит содержимое модели.

ListModel {
    id: nameModel
    ListElement { name: "Alice" }
    ListElement { name: "Bob" }
    ListElement { name: "Jane" }
    ListElement { name: "Harry" }
    ListElement { name: "Wendy" }
}
Component {
    id: nameDelegate
    Text {
        text: name;
        font.pixelSize: 24
    }
}

Клуб может декорировать список членов, связав визуальные объекты со свойствами header и footer. Визуальный объект может быть определен непосредственно, в другом файле или в типе Component.

ListView {
    anchors.fill: parent
    clip: true
    model: nameModel
    delegate: nameDelegate
    header: bannercomponent
    footer: Rectangle {
        width: parent.width; height: 30;
        gradient: clubcolors
    }
    highlight: Rectangle {
        width: parent.width
        color: "lightgray"
    }
}

Component {     //instantiated when header is processed
    id: bannercomponent
    Rectangle {
        id: banner
        width: parent.width; height: 50
        gradient: clubcolors
        border {color: "#9EDDF2"; width: 2}
        Text {
            anchors.centerIn: parent
            text: "Club Members"
            font.pixelSize: 32
        }
    }
}
Gradient {
    id: clubcolors
    GradientStop { position: 0.0; color: "#8EE2FE"}
    GradientStop { position: 0.66; color: "#7ED2EE"}
}

Обработка мыши и сенсорного ввода

Представления обрабатывают перетаскивание и прокрутку своего содержимого, но не обрабатывают взаимодействие с сенсорным вводом с отдельными делегатами. Чтобы делегаты реагировали на сенсорный ввод, например, для установки currentIndex, делегат должен содержать MouseArea с соответствующей логикой обработки сенсорного ввода.

Обратите внимание, что если highlightRangeMode установлено в StrictlyEnforceRange, значение currentIndex будет изменяться при перетаскивании/прокрутке представления, поскольку представление всегда будет гарантировать, что currentIndex находится в диапазоне выделения.

Разделы ListView

ListView содержимое может быть сгруппировано в разделы, где связанные элементы списка маркируются в соответствии с их разделами. Кроме того, разделы могут быть декорированы делегатами.

Список может содержать список, указывающий имена людей и команду, к которой они принадлежат.

ListModel {
    id: nameModel
    ListElement { name: "Alice"; team: "Crypto" }
    ListElement { name: "Bob"; team: "Crypto" }
    ListElement { name: "Jane"; team: "QA" }
    ListElement { name: "Victor"; team: "QA" }
    ListElement { name: "Wendy"; team: "Graphics" }
}
Component {
    id: nameDelegate
    Text {
        text: name;
        font.pixelSize: 24
        anchors.left: parent.left
        anchors.leftMargin: 2
    }
}

Тип ListView имеет section присоединённое свойство, которое может объединять смежные и связанные типы в раздел. section.property определяет, какое свойство типа списка использовать в качестве разделов. section.criteria определяет способ отображения названий разделов, а section.delegate аналогично свойству делегата представлений.

ListView {
    anchors.fill: parent
    model: nameModel
    delegate: nameDelegate
    focus: true
    highlight: Rectangle {
        color: "lightblue"
        width: parent.width
    }
    section {
        property: "team"
        criteria: ViewSection.FullString
        delegate: Rectangle {
            color: "#b0dfb0"
            width: parent.width
            height: childrenRect.height + 4
            Text { anchors.horizontalCenter: parent.horizontalCenter
                font.pixelSize: 16
                font.bold: true
                text: section
            }
        }
    }
}

Делегаты представлений

Для визуального представления элемента в списке представлениям необходим делегат. Представление визуализирует каждый элемент списка в соответствии с шаблоном, определенным делегатом. Элементы модели доступны через свойство index, а также через свойства элемента.

Component {
    id: petdelegate
    Text {
        id: label
        font.pixelSize: 24
        text: if (index == 0)
            label.text = type + " (default)"
        else
            text: type
    }
}

Доступ к представлениям и моделям из делегатов

Список представлений, к которому привязан делегат, доступен из делегата через свойство ListView.view. Аналогично, GridView GridView.view доступен делегатам. Соответствующая модель и её свойства, следовательно, доступны через ListView.view.model. Кроме того, доступны все определённые сигналы или методы в модели.

Этот механизм полезен, когда вы хотите использовать один и тот же делегат для нескольких представлений, например, но хотите, чтобы декорации или другие функции отличались для каждого представления, и вы хотите, чтобы эти разные настройки были свойствами каждого из представлений. Аналогично, может быть интересно получить доступ к свойствам модели или отобразить их.

В следующем примере делегат показывает свойство language модели, а цвет одного из полей зависит от свойства fruit_color представления.

Rectangle {
     width: 200; height: 200

    ListModel {
        id: fruitModel
        property string language: "en"
        ListElement {
            name: "Apple"
            cost: 2.45
        }
        ListElement {
            name: "Orange"
            cost: 3.25
        }
        ListElement {
            name: "Banana"
            cost: 1.95
        }
    }

    Component {
        id: fruitDelegate
        Row {
                id: fruit
                Text { text: " Fruit: " + name; color: fruit.ListView.view.fruit_color }
                Text { text: " Cost: $" + cost }
                Text { text: " Language: " + fruit.ListView.view.model.language }
        }
    }

    ListView {
        property color fruit_color: "green"
        model: fruitModel
        delegate: fruitDelegate
        anchors.fill: parent
    }
}

Модели

Данные предоставляются делегату с помощью именованных ролей данных, к которым делегат может привязаться. Вот ListModel с двумя ролями, type и age, и ListView с делегатом, привязанным к этим ролям для отображения их значений:

import QtQuick 2.0

Item {
    width: 200; height: 250

    ListModel {
        id: myModel
        ListElement { type: "Dog"; age: 8 }
        ListElement { type: "Cat"; age: 5 }
    }

    Component {
        id: myDelegate
        Text { text: type + ", " + age }
    }

    ListView {
        anchors.fill: parent
        model: myModel
        delegate: myDelegate
    }
}

Если между свойствами модели и свойствами делегата возникает конфликт имён, к ролям можно получить доступ с использованием квалифицированного имени model. Например, если у типа Text были свойства type или age, текст в приведённом выше примере отображал бы эти значения свойств вместо значений type и age из элемента модели. В этом случае к свойствам можно было бы обратиться как model.type и model.age, чтобы гарантировать, что делегат отображает значения свойств из элемента модели.

Также доступна специальная роль index, содержащая индекс элемента в модели. Обратите внимание, что этот индекс устанавливается в -1, если элемент удалён из модели. Если вы привязываетесь к роли index, убедитесь, что логика учитывает возможность значения index быть -1, т.е. что элемент больше недействителен. (Обычно элемент будет вскоре уничтожен, но есть возможность отложить уничтожение делегата в некоторых представлениях с помощью delayRemove присоединённого свойства.)

Модели, не имеющие именованных ролей (например, ListModel, показанный ниже), будут предоставлять данные через роль modelData. Роль modelData также предоставляется для моделей, имеющих только одну роль. В этом случае роль modelData содержит те же данные, что и именованная роль.

QML предоставляет несколько типов моделей данных в наборе встроенных типов QML. Кроме того, модели могут быть созданы с помощью Qt C++ и затем предоставлены QQmlEngine для использования компонентами QML. Сведения о создании этих моделей см. в статьях Использование C++ моделей с Qt Quick представлениями и создание типов QML.

Расположение элементов из модели можно достичь с помощью Repeater.

Модель списка

ListModel представляет собой простую иерархию типов, указанную в QML. Доступные роли задаются свойствами ListElement.

ListModel {
    id: fruitModel

    ListElement {
        name: "Apple"
        cost: 2.45
    }
    ListElement {
        name: "Orange"
        cost: 3.25
    }
    ListElement {
        name: "Banana"
        cost: 1.95
    }
}

Вышеупомянутая модель имеет две роли, name и cost. К ним можно привязаться, например, делегатом ListView:

ListView {
    anchors.fill: parent
    model: fruitModel
    delegate: Row {
        Text { text: "Fruit: " + name }
        Text { text: "Cost: $" + cost }
    }
}

ListModel предоставляет методы для непосредственного управления ListModel через JavaScript. В этом случае первый вставленный элемент определяет доступные роли для любых представлений, использующих модель. Например, если создаётся пустой ListModel и заполняется через JavaScript, роли, предоставляемые первым элементом, являются единственными ролями, которые будут показаны в представлении:

ListModel { id: fruitModel }
    ...
MouseArea {
    anchors.fill: parent
    onClicked: fruitModel.append({"cost": 5.95, "name":"Pizza"})
}

При нажатии на MouseArea, у fruitModel будут две роли, cost и name. Даже если впоследствии добавляются другие роли, только первые две будут обрабатываться представлениями, использующими модель. Чтобы сбросить доступные роли в модели, вызовите ListModel::clear().

Модель XML

XmlListModel позволяет создавать модель из источника данных XML. Роли задаются с помощью типа XmlRole. Необходимо импортировать тип.

import QtQuick.XmlListModel 2.0

Следующая модель имеет три роли, title, link и description:

XmlListModel {
     id: feedModel
     source: "http://rss.news.yahoo.com/rss/oceania"
     query: "/rss/channel/item"
     XmlRole { name: "title"; query: "title/string()" }
     XmlRole { name: "link"; query: "link/string()" }
     XmlRole { name: "description"; query: "description/string()" }
}

Свойство query указывает, что XmlListModel генерирует элемент модели для каждого <item> в документе XML.

Демонстрация RSS новостей показывает, как можно использовать XmlListModel для отображения ленты RSS.

Модель объекта

ObjectModel содержит визуальные элементы, которые будут использоваться в представлении. Когда ObjectModel используется в представлении, представлению не требуется делегат, так как ObjectModel уже содержит визуальный делегат (элементы).

В примере ниже три цветных прямоугольника размещаются в ListView.

import QtQuick 2.0
import QtQml.Models 2.1

Rectangle {
    ObjectModel {
        id: itemModel
        Rectangle { height: 30; width: 80; color: "red" }
        Rectangle { height: 30; width: 80; color: "green" }
        Rectangle { height: 30; width: 80; color: "blue" }
    }

    ListView {
        anchors.fill: parent
        model: itemModel
    }
}

Примечание: VisualItemModel также может быть использован, но он предоставляется только по соображениям совместимости. VisualItemModel позволяет предоставить QML-элемент в качестве модели. Эта модель содержит данные и делегат; дочерние элементы VisualItemModel обеспечивают содержимое делегата. Модель не предоставляет никаких ролей.

Целые числа как модели

Целое число может использоваться как модель, содержащая определённое количество типов. В этом случае у модели нет ролей данных.

В следующем примере создаётся ListView с пятью элементами:

Item {
    width: 200; height: 250

    Component {
        id: itemDelegate
        Text { text: "I am item number: " + index }
    }

    ListView {
        anchors.fill: parent
        model: 5
        delegate: itemDelegate
    }

}

Примеры объектов в качестве моделей

Объект может использоваться для указания модели с единственным типом объекта. Свойства объекта предоставляются в качестве ролей.

В примере ниже создаётся список с одним элементом, отображающим цвет текста myText. Обратите внимание на использование полностью квалифицированного свойства model.color для предотвращения конфликта со свойством color типа Text в делегате.

Rectangle {
    width: 200; height: 250

    Text {
        id: myText
        text: "Hello"
        color: "#dd44ee"
    }

    Component {
        id: myDelegate
        Text { text: model.color }
    }

    ListView {
        anchors.fill: parent
        anchors.topMargin: 30
        model: myText
        delegate: myDelegate
    }
}

Модели данных C++

Модели могут быть определены в C++ и затем предоставлены для QML. Этот механизм полезен для экспонирования существующих моделей данных C++ или иных сложных наборов данных для QML.

Для получения дополнительной информации посетите статью Использование моделей C++ с Qt Quick Views.

Повторители

Повторители создают элементы из шаблона для использования с позиционерами, используя данные из модели. Объединение повторителей и позиционеров — простой способ выкладывать множество элементов. Элемент Repeater размещается внутри позиционера и генерирует элементы, которые позиционер размещает.

Каждый Repeater создаёт определённое количество элементов, объединяя каждый элемент данных из модели, указанный с помощью свойства model, со шаблонным элементом, определённым как дочерний элемент внутри Repeater. Общее количество элементов определяется количеством данных в модели.

Следующий пример демонстрирует повторитель, используемый с элементом Grid для упорядочивания набора элементов Rectangle. Элемент Repeater создаёт серию из 24 прямоугольников для элемента Grid, чтобы расположить их в 5 на 5 сетке.

import QtQuick 2.0

Rectangle {
    width: 400; height: 400; color: "black"

    Grid {
        x: 5; y: 5
        rows: 5; columns: 5; spacing: 10

        Repeater { model: 24
                   Rectangle { width: 70; height: 70
                               color: "lightgreen"

                               Text { text: index
                                      font.pointSize: 30
                                      anchors.centerIn: parent } }
        }
    }
}

Количество элементов, созданных повторителем, хранится в свойстве count. Невозможно установить это свойство для определения количества создаваемых элементов. Вместо этого, как в примере выше, мы используем целое число в качестве модели.

Для получения более подробной информации, см. документ QML Модели данных.

Если модель представляет собой список строк, делегат также получает доступ к свойству только для чтения modelData , которое содержит строку. Например:

Column {
    Repeater {
        model: ["apples", "oranges", "pears"]
        Text { text: "Data: " + modelData }
    }
}

Также возможно использовать делегат в качестве шаблона для элементов, созданных повторителем. Это указывается с помощью свойства delegate.

Использование переходов

Переходы могут использоваться для анимации элементов, добавляемых, перемещаемых или удаляемых из позиционера.

Переходы для добавления элементов применяются к элементам, создаваемым в рамках позиционера, а также к тем, которые переназначаются в качестве дочерних элементов позиционера.

Переходы для удаления элементов применяются к элементам внутри позиционера, которые удаляются, а также к тем, которые удаляются из позиционера и получают новых родителей в документе.

Примечание: Изменение непрозрачности элементов на ноль не приведёт к их исчезновению из позиционера. Их можно удалить и добавить повторно, изменив свойство visible.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.11/qtquick-modelviewsdata-modelview.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API