Коммит 23edb605 создал по автору OMP Education's avatar OMP Education
Просмотр файлов

Add module: Models and Views. #11

владелец a2e895ac
...@@ -46,7 +46,13 @@ Copyright © 2016–2023 ООО «Открытая мобильн ...@@ -46,7 +46,13 @@ Copyright © 2016–2023 ООО «Открытая мобильн
[cлайды](./components/lecture.fodp), [cлайды](./components/lecture.fodp),
[конспект](./components/lecture.md) [конспект](./components/lecture.md)
* [Тесты](./components/tests.md) * [Тесты](./components/tests.md)
* Модели и представления * [Модели и представления](./models_and_views)
* Лекция:
[cлайды](./models_and_views/lecture.fodp),
[конспект](./models_and_views/lecture.md)
* [Примеры](./models_and_views/examples.md)
* [Задания](./models_and_views/tasks.md)
* [Тесты](./models_and_views/tests.md)
* JavaScript * JavaScript
* Silica * Silica
* Qt Quick Controls 2 * Qt Quick Controls 2
......
# Примеры по теме «Модели и представления»
Copyright © 2016–2023 ООО «Открытая мобильная платформа».
Этот документ предоставляется в соответствии
с [Публичной лицензией Creative Commons с указанием авторства версии 4.0 Международная](../../LICENSE.CC-BY-4.0.ru.md).
* [Пример работы с моделями и представлениями для Qt Creator](../../projects/planet_names_qtcreator)
* [Пример работы с моделями и представлениями для Аврора IDE](../../projects/planet_names_aurora)
Этот отличия не может быть отображен по слишком большой. Вместо этого вы можете использовать просмотреть бинарные данные
# Модели и представления
Copyright © 2016–2023 ООО «Открытая мобильная платформа».
Этот документ предоставляется в соответствии
с [Публичной лицензией Creative Commons с указанием авторства версии 4.0 Международная](../../LICENSE.CC-BY-4.0.ru.md).
## Repeater — дупликатор однотипных элементов
Тип Repeater используется для создания большого количества похожих элементов.
Как и у других типов представлений, у Repeater есть модель и делегат.
Элемент Repeater обычно заключен в контейнер, такой как строка или столбец, чтобы визуально
позиционировать несколько элементов делегата
Он работает с моделью данных итеративно, как цикл for.
В простейшем случае модель представляет собой просто число, обозначающее количество циклов.
Модель Repeater может быть любой из поддерживаемых моделей данных.
Так же, как и делегаты для других представлений, делегат Repeater может получить доступ
к своему индексу, а также данные модели.
Свойства:
* count : int - Это свойство содержит количество элементов в модели.
Количество элементов в модели, о которых сообщается в счетчике, может отличаться
от количества созданных делегатов, если Repeater находится в процессе создания экземпляров
делегатов или неправильно настроен.
* delegate : Component - делегат;
* model : any - модель.
Сигналы:
* itemAdded(int index, Item item) - Этот сигнал испускается, когда элемент добавляется в Repeater.
Параметр index содержит индекс, в который элемент был вставлен в Repeater, а параметр item
содержит элемент, который был добавлен.
* itemRemoved(int index, Item item) - сигнал испускается, когда элемент удаляется.
Метод:
* Item itemAt(index) - Возвращает элемент, созданный с заданным индексом,
или null, если по индексу нет элемента.
## Пример заполнения таблицы
На слайде изображён пример использования Repeater для заполнения таблицы числами от 0 до 79.
## Model-View-Delegate
Шаблон проектирования MVC (Model-View-Controller) предполагает разделение данных приложения,
пользовательского интерфейса и управляющей логики на три отдельных компонента:
Модель, Представление и Контроллер – таким образом, что модификация каждого компонента
может осуществляться независимо.
MVC позволяет снизить сложность и упростить архитектуру программ с графическим интерфейсом
при помощи того самого разделения ответственности.
* Модель отвечает за данные и обеспечивает доступ к ним.
* Представление отвечает за отображение данных, полученных из модели.
* Контролер отвечает за взаимодействие с пользователем.
Может изменять данные в модели.
В идеале эти модули должны быть независимы друг от друга и позволять вносить изменения
или даже полностью заменить какой-либо модуль без переделки остальных.
Это бывает необходимо при разработке разных версий одного и того же приложения.
Например, есть десктопная и мобильная версии одного и того же приложения.
Их Модель (ядро программы) должна быть одной и той же, поскольку приложения работают
с одними и теми же данными, а вот Представление у приложений должно отличаться.
Схема взаимодействия модулей представлена на слайде.
Предполагается, что пользователь может отдавать команды Контроллеру посредством Вида,
Контроллер обновляет данные Модели, Модель оповещает Контроллер о том, что необходимо
обновить Вид и Контроллер обновляет его.
Одним из основных предназначений Qt является разработка графических интерфейсов пользователя,
поэтому в нем без MVC тоже не обошлось и Qt включает в себя свою вариацию – Model-View шаблон.
Основной идеей является разделение данных и отображения.
Здесь Модель отвечает за данные и доступ к ним. В Qt за отображение элементов и получение ввода
от пользователя нередко отвечают одни и те же элементы, поэтому вполне логична идея объединить
Вид и Контроллер.
Но для того, чтобы из-за такого объединения не терять гибкость, было введено понятие делегата.
Делегат позволяет определять, как данные будут отображаться и как пользователь может их изменять.
Вид же, по сути, теперь является контейнером для экземпляров делегата.
Представление в MVC отображает данные. Оно определяет, как будут выглядеть данные и,
в конечном итоге, что увидит пользователь.
У представления в QML есть три задачи:
* создавать экземпляры делегата для каждого элемента в модели;
* расположить эти элементы требуемым образом;
* обеспечить навигацию по элементам.
## Стандартные представления
Стандартные представления:
* ListView — вертикальный или горизонтальный список;
* GridView — таблица;
* PathView — траектория.
Общие свойства стандартных представлений:
* model : model — модель с данными;
* delegate : Component — делегат, отвечающий за представление элемента модели;
* currentItem : Item — текущий элемент в представлении;
* currentIndex : int — индекс текущего элемента;
* count : int — общее количество элементов в модели.
## Типы моделей
Модель отвечает за доступ к данным.
Модель может быть реализована как в самом QML, так и на C++.
Выбор тут больше всего зависит от того, где находится источник данных.
Если в качестве источника данных используется код на C++, то там удобнее сделать и модель.
Если же данные поступают напрямую в QML, то лучше и модель реализовать на QML.
В QML в качестве моделей используются компоненты ListModel, XmlListModel,
а также JavaScript-модели.
Причем модель, загружаемую из xml-файла, редактировать нельзя.
## ListModel
ListModel – QML-компонент для создания модели данных.
Он простой и функциональный, является контейнером для объектов ListElement.
ListElement не имеет собственных свойств и методов, служит для описания полей модели,
как показано на слайде.
Свойства text и color определяем мы сами.
ListModel имеет методы для добавления нового объекта к модели, для вставки нового объекта
в указанное место, для перемещения нескольких объектов из одного места в модели в другое,
для удаления несколько числа объектов из модели, а также для получения объекта по указанному
индексу и для удаления всех объектов из модели.
Описанный тип jsobject представляет собой стандартный JavaScript-объект.
Описывается он как ассоциативный массив с помощью фигурных скобок.
## Модель данных — список объектов
Пример создания и использования модели, состоящей из списка сложных объектов.
## Модель данных — список объектов [2]
У каждого элемента модели имеются поля name и surfaceColor.
Внутри делегата к этим полям мы обращаемся по именам напрямую.
## Пример с изменением модели
Пример добавления объектов в модель после нажатия на кнопку.
Здесь к свойствам модели обращаются через свойство model у ListView.
## ObjectModel: свойства
ObjectModel содержит визуальные элементы, которые будут использоваться в представлении.
Когда ObjectModel используется в представлении, представление не требует делегата,
так как ObjectModel уже содержит визуальный делегат (элементы).
* count : int - Количество элементов в модели.
Это свойство только для чтения.
* ObjectModel.index : int - Это присоединенное свойство содержит индекс элемента этого делегата в модели.
Он прикрепляется к каждому экземпляру делегата.
## ObjectModel: методы
* append(object item) - Добавляет новый элемент в конец модели.
* object get(int index) - Возвращает элемент по индексу в модели.
Индекс должен быть элементом в списке.
* insert(int index, object item) - Вставляет новый элемент в модель по индексу позиции.
Индекс должен соответствовать существующему элементу в списке или следовать
за концом списка (что эквивалентно добавлению).
* move(int from, int to, int n = 1) - Перемещает n элементов из одной позиции в другую.
Можно использовать диапазоны от и до; например, чтобы переместить первые 3 элемента в конец модели.
* remove(int index, int n = 1) - Удаляет элементы по индексу из модели.
* clear() - Удаляет все элементы из модели.
## ObjectModel: пример
Пример помещает три цветных прямоугольника в ListView.
## XmlListmodel
При работе с веб-ресурсами нередко применяется формат XML.
Он используется в таких вещах, как RSS, различных подкастах.
Значит, у нас появляется задача получить этот файл и его распарсить.
Перебирать дерево элементов и наполнять модель вручную неудобно, так что нужна возможность
выбрать элементы из XML-файла автоматически и представить их в виде модели.
Эти задачи и реализует XmlListModel.
XmlListModel – QML-компонент для определения модели данных.
Компонент позволяет создавать только Модель для чтений.
Запись в нее не будет поддерживаться.
Свойство source необходимо для указания пути к XML-файлу с данными.
Это может быть путь к локальному файлу или же путь к файлу на каком-нибудь веб-ресурсе.
Свойство query позволяет указать XPath запрос на получение элементов модели из XML дерева.
Данные элементы имеют свойства/атрибуты (поля модели), для извлечения которых из XML
используется компонент XmlRole.
Объект XmlRole определяет поля Модели.
Он имеет свойство name для определения имени атрибута, по этому имени мы сможем обращаться
к свойствам в делегате.
А также свойство query, в котором указывается запрос на извлечение значения данного
атрибута из XML.
## Пример модели xml
Пример xml-файла и кода, который считывает и обрабатывает данную модель.
В качестве примера создадим собственный XML файл со каталогом книг.
Наш XML будет иметь структуру, представленную на слайде.
Корневой элемент <catalog> и внутри него несколько элементов <book>.
У каждого элемента <book> есть свои дочерние элементы: <title>, <year> и <author>.
Итак мы имеем каталог книг, где у каждой книги определено название, год издания и автор.
Объект XmlListModel, созданный для парсинга данного файла, также представлен на слайде.
В качестве свойства source указывается имя файла при условии, что файл исходного кода,
где используется XmlListModel и XML-файл лежат в одной директории.
В качестве свойства query указывается XPath запрос к элементу <book>.
Таким образом модель получит доступ к любому объекту <book> внутри XML.
Далее необходимо получить доступ к элементам-атрибутам каждой книги.
Для этого используются объекты XmlRole.
Свойства name объектов XmlRole определяют имена свойств модели, по которым мы будем
к ним обращаться внутри представления.
А свойства query задают запрос к каждому атрибуту внутри элемента <book>.
Например, чтобы обратиться к элементу <title> внутри <book> необходимо описать запрос
“title/string()”, где string означает, что данное поле текстовое.
В случае с атрибутом year, после символа / указывается number(), что означает,
что данный атрибут числовой.
## Пример модели xml [2]
Продолжение примера. Отображение xml-модели на странице приложения.
Код на слайде демонстрирует простейший пример использования XmlListModel
на странице со списком записей.
Здесь наглядно видно, что определенные нами, с помощью XmlRole, “имена” свойств модели
(title, year и author) используются в делегате компонента SilicaListView.
Также стоит отметить, что XmlListModel и XmlRole вынесены в отдельный модуль
QtQuick.XmlListModel 2.0 и его нужно дополнительно импортировать.
## Javascript-модели. Список строк
Помимо специально разработанных для создания моделей компонентов, в качестве модели
могут использоваться и другие объекты. Так моделями могут являться JavaScript-массивы и целые числа.
Рассмотрим в качестве модели JavaScript-массив.
Для каждого элемента массива будет создан делегат и данные самого элемент массива
будут доступны в делегате через свойство modelData.
Если в массиве находятся объекты, то modelData тоже будет объектом и будет содержать
все свойства исходного объекта.
## Javascript-модели. Массивы
В примере на слайде JavaScript-массив dataModel используется в качестве модели
для SilicaListView.
Внутри массива объявлены два JavaScript-объекта.
Один имеет свойство color, второй имеет два свойства color и text.
Доступ к свойствам осуществляется в делегате с помощью объекта modelData.
У одного из объектов модели не определено свойство text, в таком случае вместо него
отображается строка “empty text”.
Все Javascript-модели являются пассивными.
При изменении элементов и их добавлении/удалении представление не будет знать, что они поменялись.
Так происходит потому, что у свойств JavaScript-объектов нет сигналов, которые вызываются
при изменении свойства, в отличие от QML-объектов.
Обратите внимание, что к полям модели мы обращаемся через modelData.
## Javascript-модели. Целые числа
В качестве модели также можно использовать целое число.
Это число является количеством элементов модели.
Так можно напрямую передать свойству model целое число, как это сделано в примере на слайде.
В делегате будет доступно свойство modelData, которое содержит число.
Индекс будет доступен через model.index.
Никакой другой информации такая модель содержать не может.
# Задания по теме «Модели и&nbsp;представления»
Copyright&nbsp;©&nbsp;2016–2023 ООО&nbsp;«Открытая мобильная платформа».
Этот документ предоставляется в&nbsp;соответствии
с&nbsp;[Публичной лицензией Creative Commons с&nbsp;указанием авторства версии&nbsp;4.0 Международная](../../LICENSE.CC-BY-4.0.ru.md).
## Задание 1
Создать приложение, которое позволяет отображать список (ListView) из прямоугольников (Rectangle)
с использованием компонента ListModel в качестве модели.
Модель должна содержать информацию о цвете фона прямоугольника, тексте, отображаемом
в прямоугольнике и цвете текста.
Текст должен содержать название цвета фона прямоугольника.
## Задание 2
Создать приложение, которое отображает кнопку и список из прямоугольников.
По нажатию на кнопку происходит добавление нового элемента в список.
По нажатию на элемент списка происходит удаление данного элемента.
В прямоугольниках должен отображаться текст с порядковым номером элемента в списке.
При удалении элементов порядковые номера у добавленных прямоугольников остаются неизменными.
## Задание 3
Получить и отобразить курсы валют в виде списка из ресурса ЦБ РФ
по адресу http://www.cbr.ru/scripts/XML_daily.asp.
Используйте для решения данной задачи XmlListModel.
# Тесты по теме «Модели и&nbsp;представления»
Copyright&nbsp;©&nbsp;2016–2023 ООО&nbsp;«Открытая мобильная платформа».
Этот документ предоставляется в&nbsp;соответствии
с&nbsp;[Публичной лицензией Creative Commons с&nbsp;указанием авторства версии&nbsp;4.0 Международная](../../LICENSE.CC-BY-4.0.ru.md).
## Multiple choice
Какие из перечисленных моделей являются редактируемыми?
---
* **JavaScript-модель**
* **ListModel**
* XmlListModel
## Single choice
Какое из полей XmlListModel служит для указания ссылки на ресурс для получения данных?
---
* url
* query
* **source**
## Text
Какой сигнал у Repeater испускается после добавления элемента модели?
---
itemAdded
## Single choice
Элементы какого типа может содержать ListModel?
---
* **ListElement**
* JS-примитивы
* QML-компоненты
## Text
Через какое свойство модели можно получить доступ к данным, если они заданы как примитивы?
---
modelData
## Single choice
Сколько элементов модели можно переместить при помощи ObjectModel.move()?
---
* у ObjectModel нет такого метода
* один
* два
* три
* **от 1 до ObjectModel.count**
// SPDX-FileCopyrightText: 2023 Open Mobile Platform LLC <edu@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import QtQuick 2.1
import "components"
Item {
objectName: "PlanetNames"
Grid {
id: layout
objectName: "layout"
columns: 2
anchors.fill: parent
Repeater {
model: ListModel {
ListElement { name: "Меркурий"; surfaceColor: "gray" }
ListElement { name: "Венера"; surfaceColor: "yellow" }
ListElement { name: "Земля"; surfaceColor: "blue" }
ListElement { name: "Марс"; surfaceColor: "orange" }
ListElement { name: "Юпитер"; surfaceColor: "orange" }
ListElement { name: "Сатурн"; surfaceColor: "yellow" }
ListElement { name: "Уран"; surfaceColor: "lightBlue" }
ListElement { name: "Нептун"; surfaceColor: "lightBlue" }
}
PlanetItem {
planetName: name
planetColor: surfaceColor
width: layout.width / layout.columns
}
}
}
}
// SPDX-FileCopyrightText: 2023 Open Mobile Platform LLC <edu@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import QtQuick 2.0
Item {
id: root
objectName: "PlanetItem"
property alias planetName: text.text
property alias planetColor: planetCircle.color
height: width / 3
Rectangle {
id: planetCircle
objectName: "planetCircle"
width: parent.height * 0.8
height: width
radius: width / 2
anchors {
left: parent.left
leftMargin: width / 8
verticalCenter: parent.verticalCenter
}
}
Text {
id: text
objectName: "text"
font.pixelSize: root.width / 8
anchors {
left: planetCircle.right
leftMargin: planetCircle.width / 8
verticalCenter: parent.verticalCenter
}
}
}
Name: ru.auroraos.PlanetNames
Summary: Planet Names
Version: 1.0
Release: 1
Group: Qt/Qt
License: BSD-3-Clause
URL: https://auroraos.ru
Source0: %{name}-%{version}.tar.bz2
Requires: sailfishsilica-qt5 >= 0.10.9
BuildRequires: pkgconfig(auroraapp)
BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(Qt5Quick)
BuildRequires: desktop-file-utils
%description
List model example
%prep
%setup -q -n %{name}-%{version}
%build
%qmake5
%make_build
%install
rm -rf %{buildroot}
%qmake5_install
desktop-file-install --delete-original --dir %{buildroot}%{_datadir}/applications %{buildroot}%{_datadir}/applications/*.desktop
%files
%defattr(-,root,root,-)
%{_bindir}/%{name}
%defattr(644,root,root,-)
%{_datadir}/%{name}
%{_datadir}/applications/%{name}.desktop
%{_datadir}/icons/hicolor/*/apps/%{name}.png
[Desktop Entry]
Type=Application
X-Nemo-Application-Type=silica-qt5
Icon=ru.auroraos.PlanetNames
Exec=/usr/bin/ru.auroraos.PlanetNames
Name=Planet Names
Name[ru]=Имена планет
[X-Application]
Permissions=
OrganizationName=ru.auroraos
ApplicationName=PlanetNames
ExecDBus=/usr/bin/ru.auroraos.PlanetNames
# SPDX-FileCopyrightText: 2023 Open Mobile Platform LLC <edu@omp.ru>
# SPDX-License-Identifier: BSD-3-Clause
TARGET = ru.auroraos.PlanetNames
CONFIG += \
auroraapp \
SOURCES += \
src/main.cpp \
DISTFILES += \
rpm/$${TARGET}.spec \
AURORAAPP_ICONS = 86x86 108x108 128x128 172x172
// SPDX-FileCopyrightText: 2023 Open Mobile Platform LLC <edu@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
#include <auroraapp.h>
#include <QtQuick>
int main(int argc, char *argv[])
{
QScopedPointer<QGuiApplication> application(Aurora::Application::application(argc, argv));
application->setOrganizationName(QStringLiteral("ru.auroraos"));
application->setApplicationName(QStringLiteral("PlanetNames"));
QScopedPointer<QQuickView> view(Aurora::Application::createView());
view->setSource(Aurora::Application::pathTo(QStringLiteral("qml/PlanetNames.qml")));
view->show();
return application->exec();
}
# SPDX-FileCopyrightText: 2023 Open Mobile Platform LLC <edu@omp.ru>
# SPDX-License-Identifier: BSD-3-Clause
QT += \
quick\
CONFIG += \
c++17 \
DEFINES += \
QT_DEPRECATED_WARNINGS \
SOURCES += \
src/main.cpp \
RESOURCES += \
qml.qrc \
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
<RCC>
<qresource prefix="/">
<file>qml/components/PlanetItem.qml</file>
<file>qml/main.qml</file>
<file>qml/PlanetNames.qml</file>
</qresource>
</RCC>
// SPDX-FileCopyrightText: 2023 Open Mobile Platform LLC <edu@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import QtQuick 2.1
import "components"
Item {
objectName: "PlanetNames"
Grid {
id: layout
objectName: "layout"
columns: 2
anchors.fill: parent
Repeater {
model: ListModel {
ListElement { name: "Меркурий"; surfaceColor: "gray" }
ListElement { name: "Венера"; surfaceColor: "yellow" }
ListElement { name: "Земля"; surfaceColor: "blue" }
ListElement { name: "Марс"; surfaceColor: "orange" }
ListElement { name: "Юпитер"; surfaceColor: "orange" }
ListElement { name: "Сатурн"; surfaceColor: "yellow" }
ListElement { name: "Уран"; surfaceColor: "lightBlue" }
ListElement { name: "Нептун"; surfaceColor: "lightBlue" }
}
PlanetItem {
planetName: name
planetColor: surfaceColor
width: layout.width / layout.columns
}
}
}
}
// SPDX-FileCopyrightText: 2023 Open Mobile Platform LLC <edu@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
import QtQuick 2.0
Item {
id: root
objectName: "PlanetItem"
property alias planetName: text.text
property alias planetColor: planetCircle.color
height: width / 3
Rectangle {
id: planetCircle
objectName: "planetCircle"
width: parent.height * 0.8
height: width
radius: width / 2
anchors {
left: parent.left
leftMargin: width / 8
verticalCenter: parent.verticalCenter
}
}
Text {
id: text
objectName: "text"
font.pixelSize: root.width / 8
anchors {
left: planetCircle.right
leftMargin: planetCircle.width / 8
verticalCenter: parent.verticalCenter
}
}
}
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать