Spec-Zone.ru › Qt 5.13

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

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

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.view GridView доступен делегатам. Соответствующая модель и ее свойства, следовательно, доступны через 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, если элемент удален из модели. Если вы привязываетесь к роли индекса, убедитесь, что логика учитывает возможность того, что индекс равен -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 показывает, как можно использовать 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 типа Текст в делегате.

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

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

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

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

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

Также возможно использовать делегат в качестве шаблона для элементов, созданных Repeater. Это задается с помощью свойства 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.13/qtquick-modelviewsdata-modelview.html

Spec-Zone.ru

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