Spec-Zone.ru › Qt 5.14

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

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

Повторители

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

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

В приведенном ниже примере показан 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 Data Models.

Если модель представляет собой список строк, делегат также получает доступ к свойству только для чтения 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.

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

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

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

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

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

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

Spec-Zone.ru

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