# Основы QML

Copyright&nbsp;©&nbsp;2016–2023 ООО&nbsp;«Открытая мобильная платформа».
Этот документ предоставляется в&nbsp;соответствии
с&nbsp;[Публичной лицензией Creative Commons с&nbsp;указанием авторства версии&nbsp;4.0 Международная](../../LICENSE.CC-BY-4.0.ru.md).

## Qt Quick — технология быстрой разработки

Интрерфейсы на Qt удобно создавать с помощью языка разметки QML.
Он находится в модуле Qt Quick.

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

Интерфейсы для десктопных приложений можно создавать с помощью QtQuick Controls, для мобильных
приложений для платформы ОС Аврора — с помощью Silica.

Silica поставляется внутри Аврора SDK. Компоненты Silica позволяют создавать приложения, внешний
вид и поведение которых соответствуют принципам ОС Аврора.
К особенностям платформы можно отнести управление жестами, дополнительные элементы, уникальные
для ОС: вытягиваемые сверху и снизу меню, обложка свернутых приложений.

## QML: вложенные объекты, свойства, привязки

Рассмотрим структуру интерфейса приложения на примере первой страницы.
Страница состоит из различных элементов: как визуальных (Label, PageHeader), так и не визуальных
(Page, Column и др.).
Для каждого элемента доступен свой определенный набор свойств.
На слайде можно увидеть примеры таких свойств, как width, spacing, text, color и др.
Стоит обратить внимание на свойство id, которое позволяет идентифицировать компонент среди других
объектов и ссылаться на его свойства.
Оно должно иметь уникальное значение внутри области видимости.

Свойству можно присвоить как константное, так и динамически вычисляемое значение.
Механизм связывания (property binding) позволяет устанавливать связи между свойствами одного
или различных объектов.
При изменении значения соответствующих свойств, зависимое свойство автоматически обновится согласно
установленной связи.

## Элементы компонента QML

У всех компонентов QML имеются общие элементы.

*	id — уникальный идентификатор для обращения.
*	Properties — набор свойств заданных типов, обладающих названиями и значениями.
*	Methods — методы объектов, написанные на JavaScript.
*	Signals — уведомления от компонентов QML.
*	Signal Handlers — функции обработки сигналов.
*	Nested objects — вложенные или дочерние компоненты QML.

## Глоссарий терминов QML

Основные термины, которые используются в QML-разработке.

*	Type — базовый тип или тип объекта QML.
	*	Basic Type — простой тип данных, например int, string и bool.
	*	Object Type — тип объекта может быть создан механизмом QML.
*	Object — экземпляр типа объекта QML.
*	Component — шаблон для создания объекта или дерева объектов.
*	Document — самостоятельная часть кода QML.
	Находится в отдельном файле.
	*	Начинается с одного или нескольких операторов импорта.
	*	Содержит одно объявление объекта верхнего уровня.
*	Binding — выражение JS, присвоенное и «привязанное» к свойству.

## Базовые типы QML

Базовые типы данных, используемые в QML.

*	bool — логическое значение (true или false).
*	int — целое число.
*	real — число с плавающей точкой.
*	double — число с плавающей точкой, хранимое с двойной точностью.
*	string — строка текста в свободной форме.
*	url — ссылка на ресурсы.
*	list — список QML-объектов.
*	enumeration — именованное перечисление.
*	var — общий тип.

Базовый тип — это тип, который ссылается на простое значение, например int или строку.
Это контрастирует с типами объектов QML, которые относятся к объекту со свойствами, сигналами, методами и т. д.
В отличие от типа объекта, базовый тип нельзя использовать для объявления объектов QML

## Базовые типы QtQuick

Базовые типы данных, определённые в QtQuick.

*	date — дата.
*	point — точка с координатами x и y.
*	rect — прямоугольник с x, y, width и height.
*	size — размер с атрибутами width и height.
*	color — цвет в формате ARGB.
*	font — шрифт со свойствами QFont.
*	matrix4x4 — матрица с 4 строками и 4 столбцами.
*	quaternion — кватернион с scalar, x, y и z.
*	vector2d — вектор с атрибутами x и y.
*	vector3d — вектор с атрибутами x, y и z.
*	vector4d — вектор с атрибутами x, y, z и w.

## Основные визуальные типы

Item — самый примитивный визуальный элемент в QML.
Часто используется в качестве контейнера для других элементов.
Все визуальные компоненты в QtQuick унаследованы от Item. 

## Item — базовый визуальный тип

Хотя Item не имеет внешнего вида, он определяет свойства, которые являются общими для всех
визуальных элементов, такие как позиция на экране (x и y координаты), размеры (ширина и высота),
идентификатор объекта и др.

*	parent : Item — родительский элемент.
*	children : list<Item> — список потомков.
*	data : list<Object> — потомки и ресурсы.
*	x, y : int — позиция элемента.
*	z : int — позиционирование соседей элемента.
*	width, height : int — размер элементов.
*	visible : bool — отображен ли элемент.
*	anchors — относительное позиционирование.
*	clip : bool — включена ли обрезка.
*	opacity : real — прозрачность (от 0 до 1).

## Простой пример QML-кода

На слайде приведён простой пример создания объекта строго заданных размеров.

## Rectangle — закрашенный прямоугольник с рамкой

Rectangle — компонент, отображающий прямоугольную область на экране.
Так как класс Rectangle является унаследованным от класса Item, то он обладает и всеми его свойствами, описанными ранее.

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

*	color : color — цвет для заполнения.
*	radius : real — радиус скругления углов.
*	border — свойства рамки.
*	width : int — ширина рамки.
*	color : color — цвет рамки.
*	gradient : Gradient — градиент для заполнения.
*	antialiasing : bool — использовать ли сглаживание.

## Пример прямоугольника

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

## Пример с градиентом

Свойство color определяет цвет прямоугольника, который отрисовывается.
В качестве параметра здесь используется QML-тип “color”, который может быть представлен в виде
строки с именем цвета (“red”, “green”, “black”) или же в виде строки с шестнадцатеричным кодом. 
Свойство gradient принимает в качестве аргумента объект Gradient как представлено на слайде.
Gradient служит контейнером для объектов GradientStop, определяющих цвет и его позицию в градиенте.
Если у объекта определены оба свойства, то приоритет отдается градиенту.

## Text — текст в заданном формате

Text — QML-компонент для отображения текста на экране.
Компонент поддерживает как обычный “plain” текст, так и “rich” текст, т.е. имеет поддержку HTML-тэгов.
Компонент Text позволяет только отображать текст, для редактирования необходимо использовать
компонент TextEdit.

Свойство lineCount используется в ситуации, когда ширина объекта Text строго определенная,
а длина строки переданной в свойство text — нет.
Т.е. возможна ситуация, когда строка не уберется в выделенную ширину и “отрежется” часть текста.
Если же установлено свойство lineCount и его значение больше 1, то не помещающийся текст будет
перенесен на следующую строку (или строки, если их несколько).
Свойства horizontalAlignment и verticalAlignment аналогичны таким же в классе Image. 

## Примеры форматированного текста

Свойство textFormat позволяет настраивать формат для отображения текста. 
По умолчанию установлено значение Text.AutoText.
С этим форматом компонент автоматически отобразит стилизованный текст, если таковой будет передан в свойство text.
Text.PlainText позволяет отображать ровно то, что передано в свойство text, игнорируя форматирование и HTML-тэги.
Text.RichText поддерживает подмножество языка HTML 4, а значит позволяет использовать HTML-тэги внутри текста.
Полный список поддерживаемых тэгов можно найти в документации к компоненту Text.

## Image — картинка

Image — QML-компонент для отображения изображения на экране.
Компонент способен отображать любой формат изображений, который поддерживается в Qt,
как растровую графику — PNG, JPEG форматы, так и векторную — SVG формат.

Свойства horizontalAlignment и verticalAlignment позволяют выравнивать изображения по горизонтали
и вертикали соответственно. 
horizontalAlignment может принимать значения Image.AlignLeft, Image.AlignRight и Image.AlignHCenter.
verticalAlignment может принимать значения Image.AlignTop, Image.AlignBottom и Image.AlignVCenter.

## Пример Image

Свойство filleMode может принимать следующие значения:

*	Image.Stretch — масштабирует изображение по всей выделенной под него области;
*	Image.PreserveAspectFit  — масштабирует равномерно без обрезания частей изображения;
*	Image.PreserveAspectCrop — масштабирует изображение равномерно, заполняя выделенную область
	полностью и отрезая части при необходимости;
*	Image.Tile — заполняет всю выделенную область изображением, дублируюя его при необходимости
	по горизонтали и вертикали.
*	Image.TileVertically и Image.TileHorizontally — выполняют ту же задачу, что и Image.Tile,
	с той лишь разницей, что они дублируют изображение только по вертикали или только
	по горизонтали соответственно.

## Способы позиционирования элементов

Одной из основных задач построения пользовательского интерфейса является размещение визуальных
элементов на экране.
Для решения этой задачи существует несколько способов:

*	с помощью абсолютных координат — абсолютное позиционирование;
*	с помощью якорей — относительное позиционирование;
*	с помощью контейнеров.

## Позиционирование вручную

Абсолютное позиционирование подразумевает использование свойств x и y, для указания местоположения
объектов на экране. 
Система координат имеет вид, представленный на слайде.
Точка начала координат (0, 0) находится в левом верхнем углу экрана.
Ось X имеет привычный вид и направлена вправо, а вот ось Y направлена вниз, а не вверх, как это принято
в математике.
При разработке приложений такой способ размещения редко используется.
Причина в том, что на разных устройствах одно и тоже (с точки зрения кода) расположение объектов
может отличаться за счет разного разрешения и соотношения сторон экрана. 

## Пример размещения вручную

На слайде приведён пример абсолютного позиционирования элементов.
Все параметры прямоугольников заданы вручную.

## Использование якорей

Относительное позиционирование объектов подразумевает использование якорей (свойств группы anchors). 
В разработке приложений чаще применяется этот способ позиционирования. 
Он позволяет настроить расположение объектов относительно других объектов, что не зависит
от разрешения и соотношения сторон экранов устройств. 

Anchors — группа свойств для размещения объекта относительно других объектов на экране.
Якоря бывают двух видов:

*	ссылающиеся на элемент: fill, centerIn;
*	ссылающиеся на другой якорь: top, bottom, left, right, anchors.verticalCenter и anchrors.horizontalCenter.

*	Якоря top, bottom, left и right позволяют связать каждую из границ объекта с якорем другого объекта.
*	anchors.centerIn позволяет разместить объект в центре другого объекта.
*	anchors.fill позволяет заполнить другой объект текущим.
*	anchors.verticalCenter и anchrors.horizontalCenter позволяют связать центр вертикали
	или горизонтали объекта с якорем другого объекта.
*	Свойства margins позволяют задать размер границ вокруг объекта.

## Пример использования якорей

На слайде приведён пример позиционирования элементов относительно друг друга.

## Контейнеры

Помимо абсолютного и относительного позиционирования, для размещения элементов на экране
используются еще и контейнеры элементов, такие как Column, Row, Grid.
Контейнеры облегчают жизнь разработчикам, когда необходимо расположить несколько элементов
в определенном порядке. 
Контейнеры имеют ограничения.
Они не поддерживают изменение координат x и y, а также использование якорей для находящихся
внутри элементов.

Column, Row — вертикальный и горизонтальный:

*	spacing : real — промежутки в пикселях между элементами;
*	padding : real — поля вокруг контента;
*	topPadding, bottomPadding, leftPadding, rightPadding : real.

Flow — контейнер размещает свои элементы друг за другом, перенося по мере необходимости:

*	flow : enumeration — способ размещения Flow.LeftToRight, Flow.TopToBottom.

Grid — сетка/таблица:

*	columns, rows : int — число колонок и строк;
*	columnSpacing, rowSpacing : real — промежутки между ними;
*	flow : enumeration — способ размещения Grid.LeftToRight, Grid.TopToBottom.

## Пример вертикального контейнера

Column — контейнер для размещения элементов в столбец.
Column (как и другие контейнеры) обладает свойством spacing, которое задает размер отступа
между элементами в контейнере.
На слайде приведен пример того, как создать столбец с двумя элементами Rectangle в нем. 
Высота и ширина столбца могут быть подсчитаны автоматически из размеров элементов внутри:
высота — как сумма высот элементов, ширина — как максимальная ширина элементов.
В данном примере ширина столбца устанавливается нами, а его высота будет посчитана автоматически,
когда прямоугольники будут созданы.

## Пример горизонтального контейнера

Row — контейнер для размещения элементов в ряд.
Размеры контейнера также будут определены автоматически, с той лишь разницей, что здесь высота
ряда — это максимальная высота среди элементов, а ширина ряда — это сумма ширин элементов в ряду.

## Пример Flow

Flow — контейнер для размещения элементов друг за другом.
Если элементы не умещаются в одну строку, то они переносятся на следующую. Размеры элементов не подгоняются под контейнер автоматически.

## Пример сетки

Grid — контейнер для размещения элементов сеткой.
Положение элементов в сетке определяется автоматически слева направо и сверху вниз. 
По умолчанию Grid имеет 4 столбца и создает автоматически столько строк, сколько нужно, чтобы
разместить все элементы внутри.
Для того, чтобы задать количество столбцов и строк для вручную, используются свойства columns и rows.

## Компоновки

Qt Quick Layouts — это элементы, которые используются для упорядочивания элементов
в пользовательском интерфейсе.
Поскольку Qt Quick Layouts также изменяют размер своих элементов, они хорошо подходят
для пользовательских интерфейсов с изменяемым размером.

Основные типы:

*	ColumnLayout — вертикальный столбец;
*	RowLayout — горизонтальная строка;
*	GridLayout — таблица;
*	StackLayout — отображение верхнего элемента в стеке.

Присоединённые свойства:

*	Layout.fillHeight, Layout.fillWidth : bool — растягивать по направлениям;
*	Layout.margins, Layout.leftMargin, Layout.rightMargin, Layout.topMargin, Layout.bottomMargin : real — отступы;
*	Layout.rowSpan, Layout.columnSpan : int — объединения ячеек в GridLayout;
*	Layout.alignment : Qt.Alignment — отступы внутри ячейки.

Эти свойства можно определять в каждом визуальном элементе, помещённом в компоновщик.

## Пример компоновки в строку и столбец

Тип RowLayout идентичен GridLayout, но имеет только одну строку.

Тип ColumnLayout идентичен GridLayout, но имеет только один столбец.

Они доступны для удобства разработчиков, так как предлагают более чистый API.

## Пример компоновки таблицей

Тип QML GridLayout предоставляет способ динамического расположения элементов в сетке.

Если изменить размер GridLayout, все элементы в макете будут переупорядочены.
Все видимые дочерние элементы элемента GridLayout будут принадлежать макету.

По умолчанию элементы будут расположены в соответствии со свойством flow.
Значение свойства flow по умолчанию — GridLayout.LeftToRight.

Если свойство columns указано, оно будет рассматриваться как максимальное количество столбцов,
которое может иметь макет, прежде чем автоматическое позиционирование вернется к началу
следующей строки.

Свойство rows работает аналогичным образом, но элементы автоматически позиционируются по вертикали.

Вы можете указать, какую ячейку должен занимать элемент, задав свойства Layout.row и Layout.column.
Вы также можете указать диапазон строк или столбцов, задав свойства Layout.rowSpan или Layout.columnSpan.

## Пример компоновки стеком

StackLayout предоставляет стек элементов, в котором одновременно виден только один элемент.

Текущий видимый элемент можно изменить, установив свойство currentIndex.
Индекс соответствует порядку дочерних элементов StackLayout.

В отличие от большинства других макетов, свойства Layout.fillWidth и Layout.fillHeight дочерних
элементов по умолчанию имеют значение true.
Как следствие, дочерние элементы по умолчанию масштабируются в соответствии с размером StackLayout,
если их Layout.maximumWidth или Layout.maximumHeight не препятствуют этому.

## Порядок отрисовки

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

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

## Пример с наложением объектов

В данном примере дочерние элементы отрисовываются один за другим.

## Пример z-координаты

Пример, похожий на описанный ранее, но у красного прямоугольника установлено значение z-координаты
равным 1.
Так как у синего прямоугольника значение этой координаты по умолчанию установлено равным 0,
то он будет отрисован раньше красного.

Стоит заметить, что z-координата влияет на отображение данного элемента только относительно
соседних и родительского.
В данном примере зелёному прямоугольнику установлено значение z-координаты равное -1,
что меньше значения z-координаты синего.
Но эти два прямоугольника не являются соседними, и зелёный не является родительским для синего,
поэтому установка координаты в данном случае не влияет на порядок отрисовки.
