Spec-Zone.ru › Qt 6.1

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

Объектная модель

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 Views.

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

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

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

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

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

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

Spec-Zone.ru

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