Spec-Zone.ru › Qt 5.9

Модели и представления в 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.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
    }
}

Примечание: 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 типа Текст в делегате.

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.

Повторители

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

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

Следующий пример демонстрирует повторитель, используемый с элементом 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/qt-5.9/qtquick-modelviewsdata-modelview.html

Spec-Zone.ru

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