Spec-Zone.ru › Qt 5.15

Модели и представления в 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: index === 0 ? type + " (default)" : type

        required property int index
        required property string 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
    }
}

Чтобы получить более тонкий контроль над доступными ролями и сделать делегаты более автономными и пригодными для использования вне представлений,

{Требуемые свойства}{требуемые свойства} могут быть использованы. Если делегат содержит требуемые свойства, именованные роли не предоставляются. Вместо этого движок QML проверит, соответствует ли имя требуемого свойства имени роли модели. Если это так, то это свойство будет привязано к соответствующему значению из модели.

import QtQuick 2.0

Item {
    width: 200
    height: 250

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

    component MyDelegate : Text {
        required property string type
        required property int age
        text: type + ", " + age
        // WRONG: Component.onCompleted: () => console.log(noise)
        // The above line would cause a ReferenceError
        // as there is no required property noise,
        // and the presence of the required properties prevents
        // noise from being injected into the scope
    }

    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 содержит те же данные, что и именованная роль.

Примечание: роли model, index и 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 типа 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. Общее количество элементов определяется объёмом данных в модели.

В следующем примере показан повторитель, используемый с элементом 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 }
    }
}

Также возможно использовать делегат в качестве шаблона для элементов, созданных повторителем. Это задаётся свойством 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() для встроенных имён ролей. Однако реальные модели обычно регистрируют пользовательские роли.

\nodeЕсли роль модели связана с

{Необходимым Свойством}{необходимым свойством}, присвоение этому свойству не изменит модель. Вместо этого это разорвёт привязку к модели (точно так же, как присвоение любому другому свойству разрывает существующие привязки). Если вы хотите использовать необходимые свойства и изменять данные модели, сделайте модель также необходимым свойством и назначьте model.propertyName.

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

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

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

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

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

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

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

Spec-Zone.ru

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