Spec-Zone.ru › Qt

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

Чтобы получить более точный контроль над доступными ролями и сделать делегатов более автономными и применимыми вне представлений, можно использовать требуемые свойства. Если делегат содержит требуемые свойства, именованные роли не предоставляются. Вместо этого движок 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, если элемент удалён из модели. Если вы привязываетесь к роли index, убедитесь, что логика учитывает возможность того, что индекс равен -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. Роли задаются с помощью типа XmlListModelRole. Необходимо импортировать тип.

import QtQml.XmlListModel

Указанной ниже модели есть три роли, title, link и pubDate:

XmlListModel {
     id: feedModel
     source: "http://rss.news.yahoo.com/rss/oceania"
     query: "/rss/channel/item"
     XmlListModelRole { name: "title"; elementName: "title" }
     XmlListModelRole { name: "link"; elementName: "link" }
     XmlListModelRole { name: "pubDate"; elementName: "pubDate" }
}

Свойство 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 } }
        }
    }
}

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

Примечание: Если роль модели привязана к требуемому свойству, присвоение этому свойству не изменит модель. Вместо этого это разорвёт привязку к модели (так же, как присвоение любому другому свойству разрывает существующие привязки). Если вы хотите использовать требуемые свойства и изменить данные модели, сделайте модель также требуемым свойством и присвойте значение 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-6.2/qtquick-modelviewsdata-modelview.html

Spec-Zone.ru

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