Spec-Zone.ru › Qt 5.12

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

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

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

Для визуализации данных свяжите свойство 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 содержимое может быть сгруппировано в разделы, где связанные элементы списка помечены в соответствии с их разделами. Кроме того, разделы можно украсить делегатами delegates.

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

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 аналогичен свойству 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 News demo демонстрирует, как можно использовать 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
    }
}

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

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

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

Item {
    width: 200; height: 250

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

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

}

Примечание: Предел количества элементов в модели целого числа составляет 100 000 000.

Объекты-экземпляры как модели

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

В приведенном ниже примере создается список с одним элементом, отображающим цвет текста 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.

Повторители

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

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

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

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.

Изменение данных модели

Для изменения данных модели можно назначить обновлённые значения свойствам model. QML ListModel по умолчанию редактируемый, в то время как C++ модели должны реализовать setData(), чтобы стать редактируемыми. Целочисленные и массивы JavaScript — модели только для чтения.

Предположим, C++ модель на основе QAbstractItemModel, реализующая метод setData, зарегистрирована как тип QML под названием EditableModel. Данные можно записать в модель следующим образом:

ListView {
    anchors.fill: parent
    model: EditableModel {}
    delegate: TextEdit {
        width: ListView.view.width
        height: 30
        text: model.edit
        Keys.onReturnPressed: model.edit = text
    }
}

Примечание: Роль edit равна Qt::EditRole. См. roleNames() для встроенных имён ролей. Однако реальные модели обычно регистрируют пользовательские роли.

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

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

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

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

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

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

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

Spec-Zone.ru

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