Spec-Zone.ru › Qt 5.6

Модели и представления в 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. Визуальный объект может быть определен inline, в другом файле или в типе 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 GridView.view доступен для делегированных элементов. Соответствующая модель и ее свойства, следовательно, доступны через ListView.view.model. Кроме того, любые определенные сигналы или методы в модели также доступны.

Этот механизм полезен, когда вы хотите использовать один и тот же делегированный элемент для нескольких представлений, но хотите, чтобы украшения или другие функции отличались для каждого представления, и вы хотите, чтобы эти различные настройки были свойствами каждого из представлений. Аналогично, может быть интересно получить доступ к некоторым свойствам модели или отобразить их.

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

Если между свойствами модели и свойствами делегированного элемента возникает конфликт имен, к ролям можно получить доступ с использованием квалифицированного имени модели вместо него. Например, если у типа Text были свойства тип или возраст, текст в приведенном выше примере отображал бы эти значения свойств вместо значений тип и возраст из элемента модели. В этом случае свойства можно было бы сослаться как model.type и model.age вместо этого, чтобы гарантировать, что делегированный элемент отображает значения свойств из элемента модели.

Для делегированного элемента также доступна специальная роль индекс, содержащая индекс элемента в модели. Обратите внимание, что этот индекс установлен в -1, если элемент удален из модели. Если вы привязываетесь к роли индекса, убедитесь, что логика учитывает возможность того, что индекс равен -1, т.е. что элемент больше не действителен. (Обычно элемент вскоре будет уничтожен, но возможно отложить уничтожение делегированного элемента в некоторых представлениях с помощью присоединенного свойства delayRemove. )

Модели, у которых нет именованных ролей (например, ListModel, показанный ниже), будут предоставлять данные через роль modelData. Роль modelData также предоставляется для моделей, имеющих только одну роль. В этом случае роль modelData содержит те же данные, что и именованная роль.

QML предоставляет несколько типов моделей данных среди встроенного набора типов QML. Кроме того, модели могут быть созданы с помощью Qt C++ и затем сделаны доступными для QQmlEngine для использования компонентами QML. Для получения информации о создании этих моделей посетите статьи Использование моделей C++ с представлениями Qt Quick и создание типов QML.

Расположение элементов из модели может быть достигнуто с помощью Repeater.

ListModel

ListModel представляет собой простую иерархию типов, указанную в QML. Доступные роли определяются свойствами ListElement.

ListModel {
    id: fruitModel

    ListElement {
        name: "Apple"
        cost: 2.45
    }
    ListElement {
        name: "Orange"
        cost: 3.25
    }
    ListElement {
        name: "Banana"
        cost: 1.95
    }
}

Приведенная выше модель имеет две роли, имя и стоимость. К ним может быть привязано представление 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 будет иметь две роли, стоимость и имя. Даже если последующие роли будут добавлены, только первые две будут обрабатываться представлениями, использующими модель. Чтобы сбросить доступные роли в модели, вызовите ListModel::clear().

XmlListModel

XmlListModel позволяет создать модель из XML источника данных. Роли определяются с помощью типа XmlRole. Тип необходимо импортировать.

import QtQuick.XmlListModel 2.0

Следующая модель имеет три роли, заголовок, ссылка и описание:

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()" }
}

Демо RSS News демонстрирует, как XmlListModel может быть использован для отображения ленты RSS.

VisualItemModel

VisualItemModel позволяет использовать элементы QML в качестве модели.

Эта модель содержит как данные, так и делегированный элемент; дочерние элементы VisualItemModel предоставляют содержимое делегированного элемента. Модель не предоставляет никаких ролей.

VisualItemModel {
    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
    }

}

Экземпляры объектов как модели

Экземпляр объекта может использоваться для указания модели с одним типом объекта. Свойства объекта предоставляются в качестве ролей.

Нижеприведенный пример создает список с одним элементом, отображающим цвет текста 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 представлениями.

Повторители

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

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

Следующий пример демонстрирует использование повторителя с элементом Grid для упорядочения набора прямоугольников. Элемент Повторителя создаёт серию из 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 Модели данных.

Также можно использовать делегат в качестве шаблона для элементов, созданных повторителем. Это задаётся с помощью свойства delegate.

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

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

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

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

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

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

Spec-Zone.ru

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