Начало программирования с QML
Добро пожаловать в мир QML, декларативного языка пользовательского интерфейса. В этом руководстве по началу работы мы создадим приложение простого текстового редактора, используя QML. После прочтения данного руководства вы будете готовы разрабатывать свои собственные приложения используя QML и Qt C++.
Установка
First, we would need to install the latest version of Qt that includes Qt Quick, which is Qt 4.7. The installation guide contains installation instructions and requirements for different platforms.
Qt Quick includes a declarative language called QML, the Qt Declarative Module, and QML Viewer.
QML для построения пользовательского интерфейса
Создаваемое приложение является простым текстовым редактором, который будет загружать, сохранять и выполнять некоторые манипуляции с текстом. Руководство состоит из двух частей. Первая часть затрагивает проектирование в QML компоновки и поведения приложения с использованием декларативного языка. Во второй части будет реализованы загрузка и сохранение с использование Qt C++. Используя метаобъектную систему Qt, мы можем сделать видимыми функции C++ в качестве свойств, которые могут использовать элементы QML. Используя QML и Qt C++, мы можем эффективно отделить логику интерфейса от логики приложения.
The final source code is in the examples/tutorials/gettingStarted/gsQml directory. You may need to compile the C++ plugin in the examples/tutorials/gettingStarted/gsQml/ first. This will put the C++ plugin in a directory where the QML files may find it.
To launch the text editor, merely provide the included qmlviewer tool with the QML file as the argument. В части C++ данного руководства предполагается, что читатель владеет базовыми знаниями процедур компиляции Qt.
Уроки руководства:
-
Объявление кнопки и меню
-
Реализация панели меню
-
Создание текстового редактора
-
Украшение текстового редактора
-
Расширение QML с использованием Qt C++
Объявление кнопки и меню
Базовый компонент - кнопка
Начнём создание нашего текстового редактора с создания кнопки. С функциональной точки зрения, кнопка обладает областью, чувствительной к мыши, и меткой. Кнопки выполняют действия когда пользователь нажимают на кнопку.
В QML, основным видимым элементом является элемент Rectangle. Элемент Rectangle имеет свойства для управления внешним видом и местоположением элемента.
import QtQuick 1.0
Rectangle {
id: simplebutton
color: "grey"
width: 150; height: 75
Text{
id: buttonLabel
anchors.centerIn: parent
text: "button label"
}
}
First, the import QtQuick 1.0 allows the qmlviewer tool to import the QML elements we will later use. Эта строка должна присуствовать в каждом файле QML. Заметьте, что версия модулей Qt включается в оператор импорта.
Этот простой прямоугольник обладает уникальным идентификатором, simplebutton, который примыкает к свойству id. Свойства элемента Rectangle привызываются к значениям путём перечисления свойства, следующего за ним двоеточия, а затем - значения. В коде примера цвет grey привязывается к свойству Rectangle'а, color. Подобным же образом мы присваиваем значения width и height элемента Rectangle.
Элемент Text - это нередактируемое текстовое поле. Дадим этому объекту Text имя buttonLabel. Чтобы установить строковое значение в текстовое поле, присвоим значение свойству text. Метка содержится внутри прямоугольника Rectangle и чтобы отцентрировать её в среднем положении мы установим якоря элемента Text к его родителю, который называется simplebutton. Якоря могут быть привязаны к другим якорям элемента, делая возможность присваиваний размещения проще.
Сохраним этот код как SimpleButton.qml. Запущенный qmlviewer с файлом в качестве аргумента выведет на экран серый прямоугольник с текстовой меткой.
Для реализации функциональности нажатия кнопки можно использовать обработку событий в QML. Обработка событий в QML очень проста по сравнению с механизмом сигналов и слотов Qt. Сигналы отправляются и вызывается присоединённый слот.
Rectangle{
id:simplebutton
...
MouseArea{
id: buttonMouseArea
anchors.fill: parent //прикрепим все стороны области мыши к якорям прямоугольника
//onClicked обработает корректные щелчки кнопки мыши
onClicked: console.log(buttonLabel.text + " clicked" )
}
}
Включим элемент MouseArea в наш simplebutton. Элементы MouseArea описывают интерактивную область, где обнаруживаются перемещения мыши. Для нашей кнопки, мы прикрепим всю область MouseArea к её родителю - simplebutton. Синтаксис anchors.fill - это один из способов присвоения определённого свойства с именем fill внутри группы свойств с именем anchors. QML uses anchor-based layouts where items can anchor to another item, creating robust layouts.
The MouseArea has many signal handlers that are called during mouse movements within the specified MouseArea boundaries. Один из них - onClicked - вызывается всякий раз, когда щелкают допустимой кнопкой мыши, по умолчанию - щелчок левой кнопкой. Мы можем привязать действия к обработчику onClicked. В нашем примере console.log() выводит текст всякий раз, когда щелкают по области мыши. Функция console.log() - полезный инструмент для отладки и вывода текста.
Код в SimpleButton.qml достаточен для отображения на экране кнопки и вывода текста всякий раз, когда по кнопке щелкают мышью.
Rectangle {
id: button
...
property color buttonColor: "lightblue"
property color onHoverColor: "gold"
property color borderColor: "white"
signal buttonClick()
onButtonClick: {
console.log(buttonLabel.text + " clicked" )
}
MouseArea{
onClicked: buttonClick()
hoverEnabled: true
onEntered: parent.border.color = onHoverColor
onExited: parent.border.color = borderColor
}
//определяем цвет кнопки, используя условный оператор
color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor
}
Полнофункциональная кнопка находится в Button.qml. В фрагменты кода данной статьи не включён некоторый код, обозначенный эллипсами, поскольку он либо был представлен в предыдущих разделах, либо не имеет отношения к обсуждению текущего кода.
Пользовательские свойства объявляются используя синтаксис свойство тип имя. In the code, the property buttonColor, of type color, is declared and bound to the value "lightblue". The buttonColor is later used in a conditional operation to determine the buttons's fill color. Note that property value assignment is possible using the = equals sign, in addition to value binding using the : colon character. Пользовательские свойства позволяют получить доступ к внутренним элементам извне области видимости Rectangle'а. There are basic QML types such as int, string, real, as well as a type called variant.
Привязав обработчики сигналов onEntered и onExited с цветами, граница кнопки будет становиться жёлтой, когда мышь наводится на кнопку, и возвращаться к предшествующему цвету, когда мышь выходит за пределы области мыши.
Сигнал buttonClick() объявлен в Button.qml путём размещения ключевого слова signal перед именем сигнала. Все сигналы имеют собственные обработчики, создаваемые автоматически, их имена начинаются с on. В результате, onButtonClick - это обработчик buttonClick'а. Затем onButtonClick присваивается действию для выполнения. В нашем примере кнопки, обработчик мыши onClicked просто вызовет onButtonClick, который выведет на экран текст. onButtonClick разрешает внешним объектам легко получить доступ к области мыши Button'а. Например, элементы могут иметь более одного объявления MouseArea и сигнал buttonClick может лучше различать несколько обработчиков сигнала MouseArea.
Теперь мы обладаем базовыми знаниями по реализации в QML элементов, которые могут обрабатывать основные движения мыши. Мы создали метку Text внутри прямоугольника Rectangle, настроили её свойства и реализовали варианты поведения, которые реагируют на движения мыши. Эта идея создания элементов внутри элементов повторяется повсюду в приложении текстового редактора.
Эта кнопка не приносит пользы, пока не используется в качестве компонента для выполнения действия. В следующем разделе мы скоро создадим меню, содержащее несколько таких кнопок.
Создание страницы меню
До этого шага мы рассмотрели, как создавать элементы и определять поведение внутри одного файла QML. В этом разделе мы охватываем импорт элементов QML и то, как повторно использовать некоторые из созданных компонентов для создания других компонентов.
Меню отображают содержимое списка, каждый пункт имеет возможность выполнять действие. В QML меню можно создать несколькими способами. Сначала мы создадим меню, содержащее кнопки, которые в конце концов будут выполнять различные действия. Код меню в FileMenu.qml.
import QtQuick 1.0 \\импорт главного модуля QML Qt
import "folderName" \\импорт содержимого каталога folder
import "script.js" as Script \\импорт файла и присвоение ему имени Script
Вышеприведённый синтаксис показывает, как использовать ключевое слово import. This is required to use JavaScript files, or QML files that are not within the same directory. Since Button.qml is in the same directory as FileMenu.qml, we do not need to import the Button.qml file to use it. We can directly create a Button element by declaring Button{}, similar to a Rectangle{} declaration.
В FileMenu.qml:
Row{
anchors.centerIn: parent
spacing: parent.width/6
Button{
id: loadButton
buttonColor: "lightgrey"
label: "Load"
}
Button{
buttonColor: "grey"
id: saveButton
label: "Save"
}
Button{
id: exitButton
label: "Exit"
buttonColor: "darkgrey"
onButtonClick: Qt.quit()
}
}
В FileMenu.qml мы объявляем три элемента Button. Они объявляются внутри элемента Row, позиционера, который будет помещать свои дочерние элементы в вертикальный ряд. Объявление Button находится в Button.qml, который тот же самый, что и Button.qml, используемый нами в предыдущем разделе. Привязки новых свойств могут быть объявлены внутри вновь созданных кнопок, фактически перезаписывая свойства, установленные в Button.qml. При щелчке по кнопке с именем exitButton она завершит сеанс работы и закроет окно. onButtonClick в Button.qml будет вызываться в добавок к обработчику onButtonClick в exitButton.
Объявление Row объявляется в Rectangle, создавая прямоугольный контейнер для ряда кнопок. Этот дополнительный прямоугольник создаёт косвенный способ упорядочения ряда кнопок внутри меню.
Объявление меню редактирования очень похоже на этот этап. В меню имеются кнопки с метками: Copy, Paste и Select All.
Вооружившись нашим знанием импортирования и настройки предварительно созданных компонентов, мы можем теперь сочетать такие страницы меню для создания панели меню, содержащей кнопки для выбора меню и выглядящей так, как будто мы можем структурировать данные используя QML.
Реализация панели меню
Нашему приложению текстового редактора необходим способ отображения меню с использованием панели меню. Панель меню будет переключать разные меню и пользователь сможет выбрать какое меню отображать. Переключение меню предполагает, что нужны меню более структурированные, чем просто отображать их в ряд. QML использует модели и представления для структурирования данных и отображения структурированных данных.
Использование моделей данных и представлений
QML содержит различные представления данных, отображающих модели данных. Наша панель меню будет отображать меню в списке с заголовком, в котором отображается строка имен меню. Список меню объявляется внутри VisualItemModel. Элемент VisualItemModel содержит пункты, которые уже имеют представления, например, элементы Rectangle и импортированные элементы пользовательского интерфейса. Другим типам моделей, такие как элемент ListModel, нужен делегат для отображения их данных.
Мы объявили два видимых пункта в menuListModel, FileMenu и EditMenu. Настроим эти два меню и выведем их на экран используя ListView. Файл MenuBar.qml содержит объявления QML и меню простого редактирования определено в EditMenu.qml.
VisualItemModel{
id: menuListModel
FileMenu{
width: menuListView.width
height: menuBar.height
color: fileColor
}
EditMenu{
color: editColor
width: menuListView.width
height: menuBar.height
}
}
Элемент ListView выведет на экран модель, соответствующую делегату. Делегат может объявить элементы модели для отображения в элементе Row или в сетке. Наша menuListModel уже имеет видимых элементов, поэтому объявлять делегат нам не нужно.
ListView{
id: menuListView
//Якоря установлены для взаимодействия с якорями окна
anchors.fill:parent
anchors.bottom: parent.bottom
width:parent.width
height: parent.height
//модель содержит данные
model: menuListModel
//контроль перемещением переключателя меню
snapMode: ListView.SnapOneItem
orientation: ListView.Horizontal
boundsBehavior: Flickable.StopAtBounds
flickDeceleration: 5000
highlightFollowsCurrentItem: true
highlightMoveDuration:240
highlightRangeMode: ListView.StrictlyEnforceRange
}
Кроме того, ListView унаследован от Flickable, заставляет список реагировать на перетаскивание мышью и другие жесты. Последняя часть вышепривёденного кода устанавливает свойства Flickable для создания приятного для нашего взгляда движения рывками. В частности, свойство highlightMoveDuration изменяет длительность перехода рывка. Более высокое значение highlightMoveDuration приведёт к более медленному переключению меню.
ListView обслуживает элементы модели через index и каждый видимый элемент модели доступен через index, в том порядке в котором объявлялись. Изменение currentIndex практически изменяет выделенный элемент в ListView. Заголовок нашей панели меню иллюстрирует этот эффект. Имеются две кнопки в ряд, обе при щелчке по ним меняют текущее меню. fileButton, при щелчке по нему, изменяет текущее меню на меню файлов, index будет равно 0 поскольку FileMenu объявлено первым в menuListModel. Аналогично, editButton будет при щелчке по нему изменять текущее меню на EditMenu.
В прямоугольнике labelList z имеет значение 1, означающее что он отображается перед панелью меню. Элементы с более высокими значениями z отображаются перед элементами с более низкими значениями z. По умолчанию значение z равно 0.
Rectangle{
id: labelList
...
z: 1
Row{
anchors.centerIn: parent
spacing:40
Button{
label: "File"
id: fileButton
...
onButtonClick: menuListView.currentIndex = 0
}
Button{
id: editButton
label: "Edit"
...
onButtonClick: menuListView.currentIndex = 1
}
}
}
Созданная панель меню может двигать рывком для доступа к меню или щелчком на имени меню в верхней части. Экраны переключения меню воспринимаются интуитивно и чувствительно.
Создание текстового редактора
Объявление TextArea
Наш текстовый редактор не является текстовым редактором, если в нем нет область редактирования текста. Элемент QML'а, TextEdit, позволяет объявить многострочную область редактирования текста. TextEdit отличается от элемента Text, который не позволяет пользователю непосредственно редактировать текст.
TextEdit{
id: textEditor
anchors.fill:parent
width:parent.width; height:parent.height
color:"midnightblue"
focus: true
wrapMode: TextEdit.Wrap
onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)
}
Редактор обладает набором цветов свойства шрифта и установкой переноса слов текста. Область TextEdit находится внутри области сдвига, которая прокручивает текст если текстовый курсор находится за пределами видимой области. Функция ensureVisible() проверяет, не находится ли прямоугольник курсора за пределами видимых границ и соответственно перемещает текстовую область. Для своих скриптов QML использует синтаксис Javascript, а файлы Javascript, как упоминалось ранее, могут быть импортированы и использованы внутри файла QML.
function ensureVisible(r){
if (contentX >= r.x)
contentX = r.x;
else if (contentX+width <= r.x+r.width)
contentX = r.x+r.width-width;
if (contentY >= r.y)
contentY = r.y;
else if (contentY+height <= r.y+r.height)
contentY = r.y+r.height-height;
}