Getting Started Programming with QML/ru: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
No edit summary
Line 1: Line 1:
'''<font size="24px">Введение в программирование на <span class="caps">QML</span></font>'''
[toc align_right=&quot;yes&amp;quot; depth=&quot;3&amp;quot;]


Добро пожаловать в мир <span class="caps">QML</span>, декларативного языка описания пользовательских интерфейсов. В этом руководстве мы создадим простой текстовый редактор, используя <span class="caps">QML</span>. После прочтения данного руководства, вы будете готовы разрабатывать ваши приложения используя <span class="caps">QML</span> и C++.
p{font-size:24px;font-weight:bold}. Введение в программирование на QML


==<span class="caps">QML</span> как средство для построения пользовательских интерфейсов==
Добро пожаловать в мир QML, декларативного языка описания пользовательских интерфейсов. В этом руководстве мы создадим простой текстовый редактор, используя QML. После прочтения данного руководства, вы будете готовы разрабатывать ваши приложения используя QML и C+''.
<br />h2. QML как средство для построения пользовательских интерфейсов
<br />Мы разработаем простой текстовый редактор, который сможет загружать, сохранять и редактировать текст. Это руководство состоит из двух частей. Первая часть затрагивает разработку внешнего вида приложения и его поведения, используя декларативный язык QML. Во второй части на основе библиотек Qt и языка C''+ будут реализованы функции загрузки и сохранения документов. Используя механизм метаобъектов Qt (&quot;Meta-Object System&amp;quot;:http://doc.qt.nokia.com/4.7/metaobjects.html), мы можем сделать функции на языке C++ доступными в качестве свойств QML элементов. Используя QML и C+'', мы можем эффективно отделять логику интерфейса от логики приложения.
<br />[[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor5_editmenu.png|QML Text Editor]]
<br />Для запуска примеров на QML, существует утилита &quot;qmlviewer&amp;quot;:http://doc.qt.nokia.com/4.7/qmlviewer.html, принимающая QML-файл в качестве аргумента. Для понимания части руководства, затрагивающей C, читателю потребуется знание основ разработки приложений с использованием Qt.
<br />h2. Создание кнопки и меню
<br />h3. Базовый компонент - кнопка
<br />Мы начнем разработку нашего текстового редактора с создания кнопки (button). Функционально кнопка содержит чувствительную к нажатию мыши область и текстовый ярлык. Кнопка выполняет действие, когда пользователь нажимает на нее. В QML базовый визуальный элемент - &quot;Rectangle&amp;quot;:http://doc.qt.nokia.com/4.7/qml-rectangle.html (Прямоугольник). ''Rectangle'' имеет свойства, отвечающие за его внешний вид и расположение.
<br /><code><br />import Qt 4.7
<br />Rectangle{<br /> id:simplebutton<br /> color: &quot;grey&amp;quot;<br /> width: 150<br /> height: 80<br /> Text{<br /> id: buttonLabel<br /> text: &quot;button label&amp;quot;<br /> anchors.centerIn: parent;<br /> }<br /> }<br /></code>
<br />Первая строка: ''import Qt 4.7'' разрешает утилите ''qmlviewer'' импортировать QML-элементы, которые мы будем использовать позже. Эта строка должна присутствовать во всех QML-файлах. Эта строка указывает на то, какая версия библиотек Qt будет использоваться.
<br />Созданный нами прямоугольник имеет уникальный идентификатор ''simplebutton'', который задается в свойстве ''id''. Значение свойств задаются после двоеточия. Так, например, для указания цвета прямоугольника, серый (grey) цвет определяется в свойстве ''color'' (строка ''color: &quot;grey&amp;quot;''). Аналогично, мы можем задать свойства ''width'' (ширина) и ''height'' (высота).
<br />Подэлемент ''Text'' (текст) является неизменяемым текстовым полем. Для него мы установим ''id'' равным ''buttonLabel''. Чтобы установить отображаемый этим элементом текст, мы установим значение свойства ''text''. Текстовая метка находится внутри прямоугольника и для того, чтобы разместить ее в центре нашего компонента, мы свяжем якоря (''anchors'') текстового элемента с его родителем (parent) через использование свойства ''anchors.centerIn'' (центрировать на указанном объекте). Положение элементов можно привязывать друг к другу через использование свойства ''anchors''. Это позволяет упростить и ускорить процесс компоновки элементов на форме.
<br />Мы сохраним код в файл ''SimpleButton.qml''. Запуск ''qmlviewer'' с этим файлом в качестве аргумента (команда &quot;''qmlviewer SimpleButton.qml''&quot;) покажет на экране серый прямоугольник с текстом.
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor1_simplebutton.png|Simple Button]]
<br />Для реализации нажатий на кнопку мы можем использовать обработку событий QML. Она очень похожа на механизм сигналов-слотов в Qt (&quot;Signals and Slots&amp;quot;:http://doc.qt.nokia.com/4.7/signalsandslots.html). С определенным сигналом (''signal'') мы можем связать выполнение заданных действий. Таким образом, при появлении указанного сигнала, запустится функция, называемая слотом (''slot'').
<br /><code><br />Rectangle{<br /> id:simplebutton<br /> …<br /> MouseArea{<br /> id: buttonMouseArea<br /> anchors.fill: parent //область для приема событий от мышки будет занимать всю родительскую область<br /> //сигнал onClicked обрабатывает клики мышкой по области MouseArea<br /> onClicked: console.log(buttonLabel.text'' &quot; clicked&amp;quot; )<br /> }<br /> }<br /></code>


Мы разработаем простой текстовый редактор, который сможет загружать, сохранять и редактировать текст. Это руководство состоит из двух частей. Первая часть затрагивает разработку внешнего вида приложения и его поведения, используя декларативный язык <span class="caps">QML</span>. Во второй части на основе библиотек Qt и языка C++ будут реализованы функции загрузки и сохранения документов. Используя механизм метаобъектов Qt ([http://doc.qt.nokia.com/4.7/metaobjects.html Meta-Object System] ''[doc.qt.nokia.com]''), мы можем сделать функции на языке C++ доступными в качестве свойств <span class="caps">QML</span> элементов. Используя <span class="caps">QML</span> и C++, мы можем эффективно отделять логику интерфейса от логики приложения.
Мы добавляем элемент &quot;MouseArea&amp;quot;:http://doc.qt.nokia.com/4.7/qml-mousearea.html в наш ''simplebutton''. ''MouseArea'' описывает интерактивную область, в которой обрабатываются все события от мышки (клики, перемещение, действия колеса прокрутки). Для нашей кнопки, мы закрепим ''MouseArea'' поверх всего ''simplebutton''. Запись ''anchors.fill'' - это один из способов доступа к специальному свойству ''fill'' внутри группы свойств называемых ''anchors''. QML использует механизм размещения элементов на основе якорей (&quot;anchor based layout&amp;quot;:http://doc.qt.nokia.com/4.7/qml-anchor-layout.html). Это означает, что элементы могут прикрепляться к другим объектам, создавая устойчивое размещение.


[[Image:qml-texteditor5_editmenu.png|QML Text Editor]]
''MouseArea'' имеет множество обработчиков сигналов, вызываемых во время действий мышки в определенных границах. Так, обработчик события ''onClicked'' вызывается каждый раз, когда происходит нажатие на кнопку мыши (на левую кнопку, по умолчанию). Мы можем связать необходимые действие с событием ''onClicked''. В данном случае мы вызываем функцию ''console.log(buttonLabel.text + &quot; clicked&amp;quot;)'' как ответ на нажатие кнопки. ''console.log()'' - функция, которая позволяет выводить на консоль текстовые сообщения (это может быть полезно при отладке приложений). ''buttonLabel.text'' - свойство объекта ''buttonLabel'', содержащее заданный ранее текст.


Для запуска примеров на <span class="caps">QML</span>, существует утилита [http://doc.qt.nokia.com/4.7/qmlviewer.html qmlviewer] ''[doc.qt.nokia.com]'', принимающая <span class="caps">QML</span>-файл в качестве аргумента. Для понимания части руководства, затрагивающей C++, читателю потребуется знание основ разработки приложений с использованием Qt.
Файл ''SimpleButton.qml'' содержит все необходимое для отображения на экране текстовой кнопки и вывода в консоль сообщений о кликах по ней.
 
==Создание кнопки и меню==
 
===Базовый компонент – кнопка===


Мы начнем разработку нашего текстового редактора с создания кнопки (button). Функционально кнопка содержит чувствительную к нажатию мыши область и текстовый ярлык. Кнопка выполняет действие, когда пользователь нажимает на нее. В <span class="caps">QML</span> базовый визуальный элемент – [http://doc.qt.nokia.com/4.7/qml-rectangle.html Rectangle] ''[doc.qt.nokia.com]'' (Прямоугольник). ''Rectangle'' имеет свойства, отвечающие за его внешний вид и расположение.
<code><br />Rectangle {<br /> id:Button<br /> …


Первая строка: ''import Qt 4.7'' разрешает утилите ''qmlviewer'' импортировать <span class="caps">QML</span>-элементы, которые мы будем использовать позже. Эта строка должна присутствовать во всех <span class="caps">QML</span>-файлах. Эта строка указывает на то, какая версия библиотек Qt будет использоваться.
property color buttonColor: &quot;lightblue&amp;quot;<br /> property color onHoverColor: &quot;gold&amp;quot;<br /> property color borderColor: &quot;white&amp;quot;


Созданный нами прямоугольник имеет уникальный идентификатор ''simplebutton'', который задается в свойстве ''id''. Значение свойств задаются после двоеточия. Так, например, для указания цвета прямоугольника, серый (grey) цвет определяется в свойстве ''color'' (строка ''color: “grey”''). Аналогично, мы можем задать свойства ''width'' (ширина) и ''height'' (высота).
signal buttonClick()<br /> onButtonClick: {<br /> console.log(buttonLabel.text + &quot; clicked&amp;quot; )<br /> }


Подэлемент ''Text'' (текст) является неизменяемым текстовым полем. Для него мы установим ''id'' равным ''buttonLabel''. Чтобы установить отображаемый этим элементом текст, мы установим значение свойства ''text''. Текстовая метка находится внутри прямоугольника и для того, чтобы разместить ее в центре нашего компонента, мы свяжем якоря (''anchors'') текстового элемента с его родителем (parent) через использование свойства ''anchors.centerIn'' (центрировать на указанном объекте). Положение элементов можно привязывать друг к другу через использование свойства ''anchors''. Это позволяет упростить и ускорить процесс компоновки элементов на форме.
MouseArea{<br /> onClicked: buttonClick()<br /> hoverEnabled: true<br /> onEntered: parent.border.color = onHoverColor<br /> onExited: parent.border.color = borderColor<br /> }


Мы сохраним код в файл ''SimpleButton.qml''. Запуск ''qmlviewer'' с этим файлом в качестве аргумента (команда “''qmlviewer SimpleButton.qml''”) покажет на экране серый прямоугольник с текстом.
//определяем цвет кнопки с использованием условного оператора<br /> color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor<br /> }<br /></code>
 
[[Image:qml-texteditor1_simplebutton.png|Simple Button]]
 
Для реализации нажатий на кнопку мы можем использовать обработку событий <span class="caps">QML</span>. Она очень похожа на механизм сигналов-слотов в Qt ([http://doc.qt.nokia.com/4.7/signalsandslots.html Signals and Slots] ''[doc.qt.nokia.com]''). С определенным сигналом (''signal'') мы можем связать выполнение заданных действий. Таким образом, при появлении указанного сигнала, запустится функция, называемая слотом (''slot'').
 
Мы добавляем элемент [http://doc.qt.nokia.com/4.7/qml-mousearea.html MouseArea] ''[doc.qt.nokia.com]'' в наш ''simplebutton''. ''MouseArea'' описывает интерактивную область, в которой обрабатываются все события от мышки (клики, перемещение, действия колеса прокрутки). Для нашей кнопки, мы закрепим ''MouseArea'' поверх всего ''simplebutton''. Запись ''anchors.fill'' – это один из способов доступа к специальному свойству ''fill'' внутри группы свойств называемых ''anchors''. <span class="caps">QML</span> использует механизм размещения элементов на основе якорей ([http://doc.qt.nokia.com/4.7/qml-anchor-layout.html anchor based layout] ''[doc.qt.nokia.com]''). Это означает, что элементы могут прикрепляться к другим объектам, создавая устойчивое размещение.
 
''MouseArea'' имеет множество обработчиков сигналов, вызываемых во время действий мышки в определенных границах. Так, обработчик события ''onClicked'' вызывается каждый раз, когда происходит нажатие на кнопку мыши (на левую кнопку, по умолчанию). Мы можем связать необходимые действие с событием ''onClicked''. В данном случае мы вызываем функцию ''console.log(buttonLabel.text + “ clicked”)'' как ответ на нажатие кнопки. ''console.log()'' – функция, которая позволяет выводить на консоль текстовые сообщения (это может быть полезно при отладке приложений). ''buttonLabel.text'' – свойство объекта ''buttonLabel'', содержащее заданный ранее текст.
 
Файл ''SimpleButton.qml'' содержит все необходимое для отображения на экране текстовой кнопки и вывода в консоль сообщений о кликах по ней.


В файле ''Button.qml'' находится описание более функциональной кнопки. Для экономии места и большей наглядности, части кода, описанные ранее или не относящиеся к текущей теме, будут заменяться многоточием (…). В конце статьи вы сможете найти ссылки на конечные файлы.
В файле ''Button.qml'' находится описание более функциональной кнопки. Для экономии места и большей наглядности, части кода, описанные ранее или не относящиеся к текущей теме, будут заменяться многоточием (…). В конце статьи вы сможете найти ссылки на конечные файлы.


Помимо предустановленных свойств, объекты в <span class="caps">QML</span> могут иметь и дополнительные свойства, определенные самим разработчиком. Эти так называемые “пользовательские свойства” (''custom properties'') объявляются с помощью выражения вида: ''property type name'' (''property'' ключевое слово для объявления свойства, ''type'' тип данных свойства, ''name'' имя свойства).
Помимо предустановленных свойств, объекты в QML могут иметь и дополнительные свойства, определенные самим разработчиком. Эти так называемые &quot;пользовательские свойства&amp;quot; (''custom properties'') объявляются с помощью выражения вида: ''property type name'' (''property'' - ключевое слово для объявления свойства, ''type'' - тип данных свойства, ''name'' - имя свойства).


Свойство ''buttonColor'' типа ''color'' объявлено и связано со значением “lightblue”. В дальнейшем это свойство используется в операции определения цвета заливки кнопки. Отметим, что для установки значения свойств можно использовать не только двоеточие, но и более привычный знак равенства (=). Пользовательские свойства, разрешенные внутри элемента будут доступны и снаружи (когда мы будем создавать функции на языке C++). Базовые [http://doc.qt.nokia.com/4.7/qdeclarativebasictypes.html типы <span class="caps">QML</span>] ''[doc.qt.nokia.com]'' это: ''int'', ''string'', ''real'', а также тип, называемый ''variant''.
Свойство ''buttonColor'' типа ''color'' объявлено и связано со значением &quot;lightblue&amp;quot;. В дальнейшем это свойство используется в операции определения цвета заливки кнопки. Отметим, что для установки значения свойств можно использовать не только двоеточие, но и более привычный знак равенства (=). Пользовательские свойства, разрешенные внутри элемента будут доступны и снаружи (когда мы будем создавать функции на языке C+''). Базовые &quot;типы QML&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativebasictypes.html это: ''int'', ''string'', ''real'', а также тип, называемый ''variant''.
<br />В обработчиках сигналов ''onEntered'' (курсор мышки появился над объектом) и ''onExited'' (курсор покинул объект) мы меняем цвет рамки у кнопки. Когда курсор появляется над кнопкой, цвет ее рамки становится золотым (&quot;gold&amp;quot;); когда курсор покидает кнопку, рамка снова станет белой (&quot;white&amp;quot;).
<br />Сигнал ''buttonClick'' объявлен в ''Button.qml'' с помощью ключевого слова ''signal''. Все сигналы имеют свои обработчики, создаваемые автоматически, их имена начинаются с ''on''. В результате, ''onButtonClick'' - это обработчик сигнала ''buttonClick'', в котором выполняются необходимые действия (в данном случае, вывод текста в консоль). Далее мы связываем событие ''onClicked'' с обработчиком ''onButtonClicked''. Такой механизм позволяет легко получить доступ к обработчику ''onButtonClicked'' из различных объектов. Например, элемент может иметь несколько областей типа ''MouseArea'', и мы можем связать сигнал ''buttonClick'' с событиями в каждой из этих областей.
<br />Итак, теперь мы имеем базовые представления о создании элементов с использованием QML, а также мы научились обрабатывать базовые события мыши. Мы создали текстовое поле внутри прямоугольника, настроили его свойства и указали реакцию на действия мыши. Идея создания элементов внутри других элементов применяется во всем текстовом редакторе.
<br />Созданная кнопка бесполезна, если не использовать ее как компонент для выполнения каких-либо действий. В следующем разделе мы создадим меню, содержащее несколько кнопок.
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor1_button.png|Button]]
<br />h3. Создание элементов меню
<br />Мы уже научились создавать элементы и определять их поведение в одном QML-файле. В этом разделе мы научимся импортировать созданные ранее элементы и повторно использовать их при разработке новых компонентов.
<br />Меню представляет собой компонент, содержащий список из нескольких элементов, определяющих различные действия. Существует несколько способов создания меню с использованием QML. Для начала, мы создадим меню из нескольких кнопок. Код, реализующий меню &quot;Файл&amp;quot; (File), можно найти в файле ''FileMenu.qml''.
<br /><code><br /> import Qt 4.7 //импортируем модуль Qt QML<br /> import &quot;folderName&amp;quot; //импортируем содержимое папки<br /> import &quot;script.js&amp;quot; as Script //импортируем код из файла Javascript, назовем этот код именем Script<br /></code>
<br />Представленный выше код показывает, как можно использовать ключевое слово ''import''. Это необходимо для того, чтобы использовать файлы JavaScript или QML, которые расположены в другом каталоге. Так как файл ''Button.qml'' находится в той же папке, что и файл ''FileMenu.qml'', то у нас нет необходимости импортировать файл ''Button.qml'' для работы с ним. Мы можем просто создать элемент класса ''Button'', объявив ''Button{}'' так же как мы ранее использовали объявление ''Rectangle{}''.
<br /><code><br /> В файле FileMenu.qml:
<br /> Row{<br /> anchors.centerIn: parent<br /> spacing: parent.width/6
<br /> Button{<br /> id: loadButton<br /> buttonColor: &quot;lightgrey&amp;quot;<br /> label: &quot;Load&amp;quot;<br /> }<br /> Button{<br /> buttonColor: &quot;grey&amp;quot;<br /> id: saveButton<br /> label: &quot;Save&amp;quot;<br /> }<br /> Button{<br /> id: exitButton<br /> label: &quot;Exit&amp;quot;<br /> buttonColor: &quot;darkgrey&amp;quot;
<br /> onButtonClick: Qt.quit()<br /> }<br /> }<br /></code>
<br />В файле ''FileMenu.qml'' мы создали три элемента класса ''Button''. Все эти кнопки расположены внутри элемента класса ''Row'', который позволяет располагать дочерние элементы в вертикальных столбцах. Определение класса ''Button'' дано в файле ''Button.qml'', описанном в предыдущем разделе. Для новых кнопок мы задаем новые значения свойств, которые перезапишут значения по умолчанию, указанные в файле ''Button.qml''. При клике на кнопку с именем ''exitButton'' приложение закрывается. Стоит заметить, что действия обработчика ''onButtonClick'' из ''Button.qml'' будут вызываться в дополнение к обработчику ''onButtonClick'' в описании ''exitButton''.
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor1_filemenu.png|меню &quot;Файл&quot;]]
<br />Класс Row создает прямоугольный контейнер для расположения кнопок по столбцам. Этот дополнительный прямоугольник позволяет организовать группу кнопок в виде простого меню.
<br />Меню &quot;Правка&amp;quot; (''Edit'') определяется аналогичным образом как и меню &quot;Файл&amp;quot; и содержит кнопки с ярлыками &quot;Копировать&amp;quot; (Copy), &quot;Вставить&amp;quot; (Paste) и &quot;Выбрать все&amp;quot; (Select All).
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor1_editmenu.png|меню &quot;Правка&quot;]]
<br />Освоив импорт и повторное использование ранее созданных элементов, мы теперь можем перейти к созданию панели меню. Также нам предстоит освоить способы структурирования данных в QML.
<br />h2. Создание панели меню
<br />Для нашего текстового редактора необходимо найти способ отображения различных меню. Поэтому в данном разделе мы займемся реализацией специальной панели (''Menu Bar''), которая позволит нам переключаться между различным дочерними меню. Это означает, что нам необходимо определить структуру, позволяющую не просто отображать кнопки в строку, но и переключаться между несколькими меню. Для подобных задач в QML реализован механизм &quot;модель-представление&amp;quot; (''Model-View''), позволяющий отделить структурированные данные от их отображения на экране.
<br />h3. Использование моделей и представлений
<br />В QML имеется несколько представлений (&quot;data view&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativemodels.html) для отображения моделей данных (&quot;data model&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativemodels.html). Наша панель будет отображать строку со списком меню, содержащим названия меню. Список меню определен внутри модели &quot;VisualItemModel&amp;quot;:http://doc.qt.nokia.com/4.7/qml-visualitemmodel.html. Элемент класса ''VisualItemModel'' содержит объекты, которые уже имеют свои представления (''view'') и могут отображать свои данные самостоятельно, например, с помощью рассмотренного ранее объекта ''Rectangle''. Остальные типы моделей (например, &quot;ListModel&amp;quot;:http://doc.qt.nokia.com/4.7/qml-listmodel.html) требуют наличие объектов-делегатов (''delegate'').
<br />Мы определим два визуальных элемента (''FileMenu'' и ''EditMenu'') внутри модели ''menuListModel'', а затем настроим оба меню и отобразим их с использованием &quot;ListView&amp;quot;:http://doc.qt.nokia.com/4.7/qml-listview.html. QML-файл ''MenuBar.qml'' содержит описания этих меню, а в файле ''EditMenu.qml'' можно найти реализацию простого меню &quot;Правка&amp;quot; (Edit).
<br /><code><br /> VisualItemModel{<br /> id: menuListModel<br /> FileMenu{<br /> width: menuListView.width<br /> height: menuBar.height<br /> color: fileColor<br /> }<br /> EditMenu{<br /> color: editColor<br /> width: menuListView.width<br /> height: menuBar.height<br /> }<br /> }<br /></code>
<br />Компонент &quot;ListView&amp;quot;:http://doc.qt.nokia.com/4.7/qml-listview.html может отображать элементы опираясь на делегат (''delegate''). С помощью делегата может задать, чтобы данные отображались либо в строку с использованием элемента класса ''Row'', либо в виде таблицы. Наша ''menuListModel'' уже содержит отображаемые элементы, поэтому нет необходимости использовать делегат.
<br /><code><br /> ListView{<br /> id: menuListView
<br /> //якоря (anchors) привязаны к размерам родительского компонента<br /> anchors.fill:parent<br /> anchors.bottom: parent.bottom<br /> width:parent.width<br /> height: parent.height
<br /> //model содержит данные<br /> model: menuListModel
<br /> //контролируем жесты мышкой для смены меню<br /> snapMode: ListView.SnapOneItem<br /> orientation: ListView.Horizontal<br /> boundsBehavior: Flickable.StopAtBounds<br /> flickDeceleration: 5000<br /> highlightFollowsCurrentItem: true<br /> highlightMoveDuration:240<br /> highlightRangeMode: ListView.StrictlyEnforceRange<br /> }<br /></code>
<br />''ListView'' является потомком класса &quot;Flickable&amp;quot;:http://doc.qt.nokia.com/4.7/qml-flickable.html, что позволяет списку реагировать на жесты мышкой. Последняя часть кода устанавливает свойства для ''Flickable''. Это позволяет контролировать жесты мышкой для смены меню на нашем компоненте. Так, свойство ''highlightMoveDuration'' устанавливает длительность анимации при реакции на жесты - чем больше ''highlightMoveDuration'', тем медленнее будет происходить переключение меню.
<br />''ListView'' позволяет получить доступ ко всем элементам модели с использованием индекса, присвоенного элементу при добавлении в список или определении в QML-файле. Изменение свойства ''currentIndex'' вызывает смену текущего выбранного элемента на ''ListView''. Что можно видеть в заголовке нашей панели с меню. В заголовке расположено две кнопки, при клике на которые отображается соответствующее меню. При клике на кнопку ''fileButton'' выбирается меню &quot;Файл&amp;quot; (File), индекс которого равен 0, так как этот элемент был первым определен в описании ''menuListModel''. Аналогично, кнопка ''editButton'' позволяет отобразить меню &quot;Правка&amp;quot; (Edit).
<br />Положение элементов в QML можно описывать не только с помощью якорей, отвечающий за размещение по осям ''x'' и ''y'', но и с помощью свойства ''z'', которое позволяет размещать одни элементы поверх других, как в слоеном пироге. Чем больше ''z'', тем выше располагается слой элемента в стеке слоев. По умолчанию свойство ''z'' равно 0, поэтому все элементы располагаются в одном слое. Для прямоугольника ''labelList'' мы установили значение ''z'' равным 1, что позволяет отображать этот объект поверх всех остальных элементов, у которых ''z'' не менялось и равно 0.
<br /><code><br /> Rectangle{<br /> id: labelList<br /> …<br /> z: 1<br /> Row{<br /> anchors.centerIn: parent<br /> spacing:40<br /> Button{<br /> label: &quot;File&amp;quot;<br /> id: fileButton<br /> …<br /> onButtonClick: menuListView.currentIndex = 0<br /> }<br /> Button{<br /> id: editButton<br /> label: &quot;Edit&amp;quot;<br /> …<br /> onButtonClick: menuListView.currentIndex = 1<br /> }<br /> }<br /> }<br /></code>
<br />Панель меню (menu bar), которую мы только что создали, позволяет переключать меню не только с помощью жестов мышки (в данном случае - перетаскивание), но через нажатие на соответствующие кнопки вверху панели.
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor2_menubar.png|Панель меню]]
<br />h2. Разработка текстового редактора
<br />h3. Объявление TextArea
<br />Наш текстовый редактор не будет текстовым редактором, если в нем не будет редактируемого тектового поля. Элемент QML &quot;TextEdit&amp;quot;:http://doc.qt.nokia.com/4.7/qml-textedit.html позволяет описать многострочное редактируемое поле. &quot;TextEdit&amp;quot;:http://doc.qt.nokia.com/4.7/qml-textedit.html отличается от элемента &quot;Text&amp;quot;:http://doc.qt.nokia.com/4.7/qml-text.html, который не позволяет пользователю напрямую редактировать текст.
<br /><code><br />TextEdit{<br /> id: textEditor<br /> anchors.fill:parent<br /> width:parent.width; height:parent.height<br /> color:&quot;midnightblue&amp;quot;<br /> focus: true
<br /> wrapMode: TextEdit.Wrap
<br /> onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)<br /> }<br /></code>
<br />У редактора установлены свойства: цвет шрифта и режим переноса. ''TextEdit'' находиться внутри ''Flickable'' области, которая будет прокручивать текст, если кусор находиться за пределами видимости. Функция ''ensureVisible()'' проверяет, если курсор находится за пределом видимых границ, то она соответственно перемещает текстовую область. QML использует синтаксис Javascript для выполнения этого скрипта, и, как уже упоминалось ранее, файлы Javascript могут быть импортированы и использованы в QML-файле.
<br /><code><br />function ensureVisible®{<br /> if (contentX &gt;= r.x)<br /> contentX = r.x;<br /> else if (contentX+width &lt;= r.x+r.width)<br /> contentX = r.x+r.width-width;<br /> if (contentY &gt;= r.y)<br /> contentY = r.y;<br /> else if (contentY+height &lt;= r.y+r.height)<br /> contentY = r.y+r.height-height;<br /> }<br /></code>
<br />h3. Объединение компонентов для текстового редактора
<br />Сейчас мы готовы разместить визуальные элементы для нашего текстового редактора, используя QML. Текстовый редактор имеет два компонента, главное меню, которое мы создали, и текстовое поле. QML позволяет нам использовать компоненты повторно. Поэтому, чтобы упростить наш код, мы импортируем компоненты и настроим их расположение. Окно нашего текстового редактора будет разбито на две части. Одну треть окна займет меню, а оставшиеся две трети - текстовое поле. Меню будет отображаться поверх остальных элементов.
<br /><code><br />Rectangle{
<br /> id: screen<br /> width: 1000; height: 1000
<br /> //экран разделяется между MenuBar и TextArea. 1/3 экрана отдается под MenuBar<br /> property int partition: height/3
<br /> MenuBar{<br /> id:menuBar<br /> height: partition<br /> width:parent.width<br /> z: 1<br /> }
<br /> TextArea{<br /> id:textArea<br /> anchors.bottom:parent.bottom<br /> y: partition<br /> color: &quot;white&amp;quot;<br /> height: partition*2<br /> width:parent.width<br /> }<br /> }<br /></code>
<br />Благодаря повторному использованию компонентов, код нашего ''TextEditor'' выглядит достаточно простым. Мы можем настроить основное приложение, не заботясь о свойствах, поведение которых уже описано. Создание компонентов пользовательского интерфейса и размещение компонентов приложения может стать очень простой задачей при использовании этого подхода.
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor3_texteditor.png|Simple Editor ]]


В обработчиках сигналов ''onEntered'' (курсор мышки появился над объектом) и ''onExited'' (курсор покинул объект) мы меняем цвет рамки у кнопки. Когда курсор появляется над кнопкой, цвет ее рамки становится золотым (“gold”); когда курсор покидает кнопку, рамка снова станет белой (“white”).
<br />h2. Оформление текстового редактора
<br />h3. Реализация выдвижной панели
<br />Наш текстовый редактор выглядит очень просто, поэтому мы попробуем его украсить. Используя QML, мы можем объявить переходы (''transitions'') и добавить анимацию нашему редактору. Меню занимает одну треть окна и будет здорово, если оно будет отображаться только тогда, когда нам это нужно.
<br />Мы можем добавить выдвижную панель, которая будет сворачивать или разворачивать меню по клику. В нашей реализации, мы сделаем узкий прямоугольник, который будет реагировать на нажатия мыши. Панель также как и приложение будет иметь два состояния: &quot;открыта&amp;quot; и &quot;закрыта&amp;quot;.
<br />Элемент ''drawer'' - это узкая горизонтальная полоска. Вложенный элемент &quot;Image&amp;quot;:http://doc.qt.nokia.com/4.7/qml-image.html отображает картинку со стрелкой в центре панели. ''drawer'' меняет состояние всего приложения с помощью идентификатора ''screen'', каждый раз когда пользователь кликнул по нему.
<br /><code><br />Rectangle{<br /> id:drawer<br /> height:15
<br /> Image{<br /> id: arrowIcon<br /> source: &quot;images/arrow.png&amp;quot;<br /> anchors.horizontalCenter: parent.horizontalCenter<br /> }
<br /> MouseArea{<br /> id: drawerMouseArea<br /> anchors.fill:parent<br /> onClicked:{<br /> if (screen.state  &amp;quot;DRAWER_CLOSED&amp;quot;)&amp;#123;
                    screen.state = &amp;quot;DRAWER_OPEN&amp;quot;
                &amp;#125;
                else if (screen.state  &quot;DRAWER_OPEN&amp;quot;){<br /> screen.state = &quot;DRAWER_CLOSED&amp;quot;<br /> }<br /> }<br /> …<br /> }<br /> }<br /></code>
<br />''state'' - это простая коллекция настроек, она объявляется в элементе &quot;State&amp;quot;:http://doc.qt.nokia.com/4.7/qml-state.html. Группы состояний могут быть связаны в свойство ''states''. В нашем приложении существуют два состояния ''DRAWER_CLOSED'' и ''DRAWER_OPEN''. Настройки элемента объявляются в элементах &quot;PropertyChanges&amp;quot;:http://doc.qt.nokia.com/4.7/qml-propertychanges.html. В состоянии ''DRAWER_OPEN'' у всех четырех элементов будут изменены свойства. ''menuBar'' изменит значение свойства ''y'' на 0, ''textArea'' опуститься ниже, а стрелка на панели изменит направление.
<br /><code><br />states:[<br /> State {<br /> name: &quot;DRAWER_OPEN&amp;quot;<br /> PropertyChanges { target: menuBar; y: 0}<br /> PropertyChanges { target: textArea; y: partition'' drawer.height}<br /> PropertyChanges { target: drawer; y: partition}<br /> PropertyChanges { target: arrowIcon; rotation: 180}<br /> },<br /> State {<br /> name: &quot;DRAWER_CLOSED&amp;quot;<br /> PropertyChanges { target: menuBar; y:<s>height; }<br /> PropertyChanges { target: textArea; y: drawer.height; height: screen.height</s> drawer.height}<br /> PropertyChanges { target: drawer; y: 0 }<br /> PropertyChanges { target: arrowIcon; rotation: 0 }<br /> }<br /> ]<br /></code>


Сигнал ''buttonClick'' объявлен в ''Button.qml'' с помощью ключевого слова ''signal''. Все сигналы имеют свои обработчики, создаваемые автоматически, их имена начинаются с ''on''. В результате, ''onButtonClick'' – это обработчик сигнала ''buttonClick'', в котором выполняются необходимые действия (в данном случае, вывод текста в консоль). Далее мы связываем событие ''onClicked'' с обработчиком ''onButtonClicked''. Такой механизм позволяет легко получить доступ к обработчику ''onButtonClicked'' из различных объектов. Например, элемент может иметь несколько областей типа ''MouseArea'', и мы можем связать сигнал ''buttonClick'' с событиями в каждой из этих областей.
Изменения состояний нуждаются в более плавном переходе. Переходы между состояниями объявляются в элементе &quot;Transition&amp;quot;:http://doc.qt.nokia.com/4.7/qml-transition.html, который можно связать со свойством ''transitions'' нашего элемента. Наш редактор находится в режиме перехода каждый раз, когда его состояние меняется с ''DRAWER_OPEN'' на ''DRAWER_CLOSED'' и наоборот. Важно заметить, что ''Transition'' нуждается в объявленных свойствах ''from'' и ''to'' - начальном и конечном состоянии, но для наших переходов мы можем использовать обобщающий символ ''', означающий, что переход применим для всех состояний.
<br />Во время перехода мы можем добавить анимацию для изменения свойств. Наш ''menuBar'' меняет позицию с ''y:0'' на _y:<s>partition_ и мы можем анимировать этот переход используя элемент &quot;NumberAnimation&amp;quot;:http://doc.qt.nokia.com/4.7/qml-numberanimation.html. Мы объявляем временной интервал и переходную кривую (''easing curve'') для свойств, которые будут изменяться при анимации. Переходные кривые контролируют коэффициенты анимации и поведение интерполяции во время переходов между состояниями. Кривая, которую мы выбрали</s> &quot;Easing.OutQuint&amp;quot;:http://doc.qt.nokia.com/4.7/qml-propertyanimation.html#easing.type-prop, она замедляет движение в конце анимации.
<br />Пожалуйста, прочитайте статью &quot;QML Animation&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativeanimation.html для получения более подробного описания различных типов анимации.
<br /><code><br />transitions: [<br /> Transition {<br /> to: &quot;'''&quot;<br /> NumberAnimation { target: textArea; properties: &quot;y, height&amp;quot;; duration: 100; easing.type:Easing.OutExpo }<br /> NumberAnimation { target: menuBar; properties: &quot;y&amp;quot;; duration: 100; easing.type: Easing.OutExpo }<br /> NumberAnimation { target: drawer; properties: &quot;y&amp;quot;; duration: 100; easing.type: Easing.OutExpo }<br /> }<br /> ]<br /></code>


Итак, теперь мы имеем базовые представления о создании элементов с использованием <span class="caps">QML</span>, а также мы научились обрабатывать базовые события мыши. Мы создали текстовое поле внутри прямоугольника, настроили его свойства и указали реакцию на действия мыши. Идея создания элементов внутри других элементов применяется во всем текстовом редакторе.
Другой способ анимации изменения свойств, это объявление элемента &quot;Behavior&amp;quot;:http://doc.qt.nokia.com/4.7/qml-behavior.html. Переход работает только во время изменения состояний и &quot;Behavior&amp;quot;:http://doc.qt.nokia.com/4.7/qml-behavior.html может установить анимацию для основных изменяемых свойств. В нашем редакторе, стрелка имеет анимацию типа ''NumberAnimation'', что позволяет поворачивать стрелку при запуске анимации. Это делается через изменение свойства ''rotation''.


Созданная кнопка бесполезна, если не использовать ее как компонент для выполнения каких-либо действий. В следующем разделе мы создадим меню, содержащее несколько кнопок.
<code><br />In TextEditor.qml:


[[Image:qml-texteditor1_button.png|Button]]
Behavior{<br /> NumberAnimation{property: &quot;rotation&amp;quot;;easing.type: Easing.OutExpo }<br /> }<br /></code>


===Создание элементов меню===
Вернемся к нашим компонентам. Мы можем улучшить их внешний вид, используя полученные знания о состояниях и переходах. В файле ''Button.qml'', мы можем добавить изменение свойств ''color'' и ''scale'' когда кнопка нажата. Цвета изменяются с помощью &quot;ColorAnimation&amp;quot;:http://doc.qt.nokia.com/4.7/qml-coloranimation.html, а масштаб с помощью &quot;NumberAnimation&amp;quot;:http://doc.qt.nokia.com/4.7/qml-numberanimation.html. Запись ''on propertyName'' полезна когда требуется применить анимацию только на одно свойство.


Мы уже научились создавать элементы и определять их поведение в одном <span class="caps">QML</span>-файле. В этом разделе мы научимся импортировать созданные ранее элементы и повторно использовать их при разработке новых компонентов.
<code><br />In Button.qml:<br /> …


Меню представляет собой компонент, содержащий список из нескольких элементов, определяющих различные действия. Существует несколько способов создания меню с использованием <span class="caps">QML</span>. Для начала, мы создадим меню из нескольких кнопок. Код, реализующий меню “Файл” (File), можно найти в файле ''FileMenu.qml''.
color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor<br /> Behavior on color { ColorAnimation{ duration: 55} }


Представленный выше код показывает, как можно использовать ключевое слово ''import''. Это необходимо для того, чтобы использовать файлы JavaScript или <span class="caps">QML</span>, которые расположены в другом каталоге. Так как файл ''Button.qml'' находится в той же папке, что и файл ''FileMenu.qml'', то у нас нет необходимости импортировать файл ''Button.qml'' для работы с ним. Мы можем просто создать элемент класса ''Button'', объявив ''Button{}'' так же как мы ранее использовали объявление ''Rectangle{}''.
scale: buttonMouseArea.pressed ? 1.1 : 1.00<br /> Behavior on scale { NumberAnimation{ duration: 55} }<br /></code>


В файле ''FileMenu.qml'' мы создали три элемента класса ''Button''. Все эти кнопки расположены внутри элемента класса ''Row'', который позволяет располагать дочерние элементы в вертикальных столбцах. Определение класса ''Button'' дано в файле ''Button.qml'', описанном в предыдущем разделе. Для новых кнопок мы задаем новые значения свойств, которые перезапишут значения по умолчанию, указанные в файле ''Button.qml''. При клике на кнопку с именем ''exitButton'' приложение закрывается. Стоит заметить, что действия обработчика ''onButtonClick'' из ''Button.qml'' будут вызываться в дополнение к обработчику ''onButtonClick'' в описании ''exitButton''.
Также мы можем улучшить внешний вид наших QML-компонентов, добавив такие цветовые эффекты как градиенты и изменение прозрачности. Объявление элемента &quot;Gradient&amp;quot;:http://doc.qt.nokia.com/4.7/qml-gradient.html переопределяет свойство ''color''. Вы можете объявить цвет градиента используя элемент &quot;GradientStop&amp;quot;:http://doc.qt.nokia.com/4.7/qml-gradientstop.html. Позиция (''position'') элементов ''GradientStop'' может принимать значения между 0.0 и 1.0.


[[Image:qml-texteditor1_filemenu.png|меню ]]
<code><br />В MenuBar.qml<br /> gradient: Gradient {<br /> GradientStop { position: 0.0; color: &quot;#8C8F8C&amp;quot; }<br /> GradientStop { position: 0.17; color: &quot;#6A6D6A&amp;quot; }<br /> GradientStop { position: 0.98;color: &quot;#3F3F3F&amp;quot; }<br /> GradientStop { position: 1.0; color: &quot;#0e1B20&amp;quot; }<br /> }<br /></code>


Класс Row создает прямоугольный контейнер для расположения кнопок по столбцам. Этот дополнительный прямоугольник позволяет организовать группу кнопок в виде простого меню.
Этот градиент используется для придания объема панели меню. Первый цвет начинается с 0.0 и последний заканчивается на 1.0


Меню “Правка” (''Edit'') определяется аналогичным образом как и меню “Файл” и содержит кнопки с ярлыками “Копировать” (Copy), “Вставить” (Paste) и “Выбрать все” (Select All).
=== Что делать дальше? ===


[[Image:qml-texteditor1_editmenu.png|меню ]]
Мы закончили разработку пользовательского интерфейса очень простого текстового редактора. Продолжаем двигаться дальше, пользовательский интерфейс готов, и мы можем разрабатывать логику приложения, используя обычный Qt и C+''. QML хорошо подходит как инструмент для построения прототипов и отделения логики приложения от графического пользовательского интерфейса.
<br />p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor4_texteditor.png|Text Editor]]
<br />h2. Расширяем возможности QML с помощью C''+


Освоив импорт и повторное использование ранее созданных элементов, мы теперь можем перейти к созданию панели меню. Также нам предстоит освоить способы структурирования данных в <span class="caps">QML</span>.
Теперь, когда мы имеем макет нашего приложения, мы можем приступить к реализации возможностей текстового редактора на C+''. Использование QML вместе с C''+ позволяет нам реализовать логику приложения с помощью Qt. Мы можем создать QML-контекст в приложении C++ используя классы &quot;Qt Declarative&amp;quot;:http://doc.qt.nokia.com/4.7/qtbinding.html и отображать элементы QML при помощи &quot;QDeclarativeView&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativeview.html, который основан на &quot;Graphics View Framework&amp;quot;:http://doc.qt.nokia.com/4.7/graphicsview.html. Или же, мы можем экспортировать C++ код в плагин, который сможет использоваться в ''qmlviewer''. Для нашего приложения мы разработаем функции загрузки и сохранения файла на C++ и экспортируем их как плагин. Это позволит запускать QML-приложение непосредственно в ''qmlviewer'', а также полноценно работать с дизайнером QML в Qt Creator.


==Создание панели меню==
=== Делаем C++ классы доступными в QML ===


Для нашего текстового редактора необходимо найти способ отображения различных меню. Поэтому в данном разделе мы займемся реализацией специальной панели (''Menu Bar''), которая позволит нам переключаться между различным дочерними меню. Это означает, что нам необходимо определить структуру, позволяющую не просто отображать кнопки в строку, но и переключаться между несколькими меню. Для подобных задач в <span class="caps">QML</span> реализован механизм “модель-представление” (''Model-View''), позволяющий отделить структурированные данные от их отображения на экране.
Мы будем реализовывать загрузку и сохранение файла используя Qt и C+''. C''+ классы и функции могут использоваться в QML только после того, как будут зарегистрированы в нем. Класс должен быть собран как Qt-плагин, а QML-приложение должно знать месторасположение плагина.


===Использование моделей и представлений===
Для нашего приложения, нам потребуется создать следующие элементы:<br /># Класс ''Directory'', который хранит список файлов (обьекты типа ''File'') и отвечает за операции с каталогами<br /># Класс ''File'', наследник &quot;QObject&amp;quot;:http://doc.qt.nokia.com/4.7/qobject.html, который хранит имя файла<br /># Класс плагина, который будет регистрироваться QML-контексте<br /># Файл проекта Qt (с расширением .pro), для описания настроек сборки плагина<br /># Файл ''qmldir'', который сообщает ''qmlviewer'' о том, где искать наш плагин


В <span class="caps">QML</span> имеется несколько представлений ([http://doc.qt.nokia.com/4.7/qdeclarativemodels.html data view] ''[doc.qt.nokia.com]'') для отображения моделей данных ([http://doc.qt.nokia.com/4.7/qdeclarativemodels.html data model] ''[doc.qt.nokia.com]''). Наша панель будет отображать строку со списком меню, содержащим названия меню. Список меню определен внутри модели [http://doc.qt.nokia.com/4.7/qml-visualitemmodel.html VisualItemModel] ''[doc.qt.nokia.com]''. Элемент класса ''VisualItemModel'' содержит объекты, которые уже имеют свои представления (''view'') и могут отображать свои данные самостоятельно, например, с помощью рассмотренного ранее объекта ''Rectangle''. Остальные типы моделей (например, [http://doc.qt.nokia.com/4.7/qml-listmodel.html ListModel] ''[doc.qt.nokia.com]'') требуют наличие объектов-делегатов (''delegate'').
=== Сборка плагина ===


Мы определим два визуальных элемента (''FileMenu'' и ''EditMenu'') внутри модели ''menuListModel'', а затем настроим оба меню и отобразим их с использованием [http://doc.qt.nokia.com/4.7/qml-listview.html ListView] ''[doc.qt.nokia.com]''. <span class="caps">QML</span>-файл ''MenuBar.qml'' содержит описания этих меню, а в файле ''EditMenu.qml'' можно найти реализацию простого меню “Правка” (Edit).
Чтобы собрать плагин, нам необходимо добавить указания на исходники, заголовки, и модули Qt в наш файл проекта. Весь код на C++ и файлы проекта находятся в директории ''filedialog''.


Компонент [http://doc.qt.nokia.com/4.7/qml-listview.html ListView] ''[doc.qt.nokia.com]'' может отображать элементы опираясь на делегат (''delegate''). С помощью делегата может задать, чтобы данные отображались либо в строку с использованием элемента класса ''Row'', либо в виде таблицы. Наша ''menuListModel'' уже содержит отображаемые элементы, поэтому нет необходимости использовать делегат.
<code><br />В cppPlugins.pro:


''ListView'' является потомком класса [http://doc.qt.nokia.com/4.7/qml-flickable.html Flickable] ''[doc.qt.nokia.com]'', что позволяет списку реагировать на жесты мышкой. Последняя часть кода устанавливает свойства для ''Flickable''. Это позволяет контролировать жесты мышкой для смены меню на нашем компоненте. Так, свойство ''highlightMoveDuration'' устанавливает длительность анимации при реакции на жесты – чем больше ''highlightMoveDuration'', тем медленнее будет происходить переключение меню.
TEMPLATE = lib<br /> CONFIG ''= qt plugin<br /> QT''= declarative


''ListView'' позволяет получить доступ ко всем элементам модели с использованием индекса, присвоенного элементу при добавлении в список или определении в <span class="caps">QML</span>-файле. Изменение свойства ''currentIndex'' вызывает смену текущего выбранного элемента на ''ListView''. Что можно видеть в заголовке нашей панели с меню. В заголовке расположено две кнопки, при клике на которые отображается соответствующее меню. При клике на кнопку ''fileButton'' выбирается меню “Файл” (File), индекс которого равен 0, так как этот элемент был первым определен в описании ''menuListModel''. Аналогично, кнопка ''editButton'' позволяет отобразить меню “Правка” (Edit).
DESTDIR ''= ../plugins<br /> OBJECTS_DIR = tmp<br /> MOC_DIR = tmp
<br /> TARGET = FileDialog
<br /> HEADERS''= directory.h  file.h  dialogPlugin.h


Положение элементов в <span class="caps">QML</span> можно описывать не только с помощью якорей, отвечающий за размещение по осям ''x'' и ''y'', но и с помощью свойства ''z'', которое позволяет размещать одни элементы поверх других, как в слоеном пироге. Чем больше ''z'', тем выше располагается слой элемента в стеке слоев. По умолчанию свойство ''z'' равно 0, поэтому все элементы располагаются в одном слое. Для прямоугольника ''labelList'' мы установили значение ''z'' равным 1, что позволяет отображать этот объект поверх всех остальных элементов, у которых ''z'' не менялось и равно 0.
SOURCES += directory.cpp  file.cpp  dialogPlugin.cpp<br /></code>


Панель меню (menu bar), которую мы только что создали, позволяет переключать меню не только с помощью жестов мышки (в данном случае – перетаскивание), но через нажатие на соответствующие кнопки вверху панели.
Мы включаем в сборку модуль ''declarative'' и указываем, что мы хотим собрать плагин (TEMPLATE = lib, CONFIG = plugin ). Также мы указываем, что собранный плагин должен быть помещен в директорию ''plugins'', расположенную в родительском каталоге.


[[Image:qml-texteditor2_menubar.png|Панель меню]]
=== Регистрация класса в QML ===


==Разработка текстового редактора==
<code><br /> В dialogPlugin.h:


===Объявление TextArea===
#include &lt;QtDeclarative/QDeclarativeExtensionPlugin&amp;gt;


Наш текстовый редактор не будет текстовым редактором, если в нем не будет редактируемого тектового поля. Элемент <span class="caps">QML</span> [http://doc.qt.nokia.com/4.7/qml-textedit.html TextEdit] ''[doc.qt.nokia.com]'' позволяет описать многострочное редактируемое поле. [http://doc.qt.nokia.com/4.7/qml-textedit.html TextEdit] ''[doc.qt.nokia.com]'' отличается от элемента [http://doc.qt.nokia.com/4.7/qml-text.html Text] ''[doc.qt.nokia.com]'', который не позволяет пользователю напрямую редактировать текст.
class DialogPlugin : public QDeclarativeExtensionPlugin<br /> {<br /> Q_OBJECT


У редактора установлены свойства: цвет шрифта и режим переноса. ''TextEdit'' находиться внутри ''Flickable'' области, которая будет прокручивать текст, если кусор находиться за пределами видимости. Функция ''ensureVisible()'' проверяет, если курсор находится за пределом видимых границ, то она соответственно перемещает текстовую область. <span class="caps">QML</span> использует синтаксис Javascript для выполнения этого скрипта, и, как уже упоминалось ранее, файлы Javascript могут быть импортированы и использованы в <span class="caps">QML</span>-файле.
public:<br /> void registerTypes(const char *uri);


===Объединение компонентов для текстового редактора===
};<br /></code>


Сейчас мы готовы разместить визуальные элементы для нашего текстового редактора, используя <span class="caps">QML</span>. Текстовый редактор имеет два компонента, главное меню, которое мы создали, и текстовое поле. <span class="caps">QML</span> позволяет нам использовать компоненты повторно. Поэтому, чтобы упростить наш код, мы импортируем компоненты и настроим их расположение. Окно нашего текстового редактора будет разбито на две части. Одну треть окна займет меню, а оставшиеся две трети – текстовое поле. Меню будет отображаться поверх остальных элементов.
Наш класс плагина ''DialogPlugin'' является наследником &quot;QDeclarativeExtensionPlugin&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativeextensionplugin.html. Нам необходимо переопределить виртуальную функцию &quot;registerTypes()&quot;:http://doc.qt.nokia.com/4.7/qdeclarativeextensionplugin.html#registerTypes. Файл ''dialogPlugin.cpp'' выглядит следующим образом:


Благодаря повторному использованию компонентов, код нашего ''TextEditor'' выглядит достаточно простым. Мы можем настроить основное приложение, не заботясь о свойствах, поведение которых уже описано. Создание компонентов пользовательского интерфейса и размещение компонентов приложения может стать очень простой задачей при использовании этого подхода.
<code><br />DialogPlugin.cpp:


[[Image:qml-texteditor3_texteditor.png|Simple Editor ]]
#include &quot;dialogPlugin.h&amp;quot;<br /> #include &quot;directory.h&amp;quot;<br /> #include &quot;file.h&amp;quot;<br /> #include &lt;QtDeclarative/qdeclarative.h&amp;gt;


==Оформление текстового редактора==
void DialogPlugin::registerTypes(const char '''uri){
<br /> qmlRegisterType&amp;lt;Directory&amp;gt;(uri, 1, 0, &quot;Directory&amp;quot;);<br /> qmlRegisterType&amp;lt;File&amp;gt;(uri, 1, 0,&quot;File&amp;quot;);<br /> }
<br /> Q_EXPORT_PLUGIN2(FileDialog, DialogPlugin);<br /></code>
<br />Функция &quot;registerTypes()&quot;:http://doc.qt.nokia.com/4.7/qdeclarativeextensionplugin.html#registerTypes регистрирует наши классы ''File'' И ''Directory'' в QML. Эта функция требует названия классов для их прототипов, номера старшей и младшей версий классов и их имена.
<br />Нам необходимо экспортировать плагин с помощью макроса &quot;Q_EXPORT_PLUGIN2&amp;quot;:http://doc.qt.nokia.com/4.7/qtplugin.html#Q_EXPORT_PLUGIN2#q-export-plugin2. Обратите внимание, что в файле dialogPlugin.h мы имеем макрос &quot;Q_OBJECT&amp;quot;:http://doc.qt.nokia.com/4.7/qobject.html#Q_OBJECT в начале нашего класса. Также нам надо запустить ''qmake'' для генерации мета-информации о наших классах.
<br />h3. Создание QML-свойств в C++ классе
<br />Мы можем создавать QML-элементы и определять их свойства, используя C '''' и систему мета-информации Qt (&quot;Meta-Object System&amp;quot;:http://doc.qt.nokia.com/4.7/metaobjects.html). Мы можем сообщить Qt о свойствах наших объектов, используя для этого сигналы и слоты, тогда эти свойства будут доступны в QML.
<br />Текстовый редактор должен иметь возможность загружать и сохранять файлы. Как правило, эти возможности содержатся в файловом диалоге. К счастью, мы можем использовать &quot;QDir&amp;quot;:http://doc.qt.nokia.com/4.7/qdir.html, &quot;QFile&amp;quot;:http://doc.qt.nokia.com/4.7/qfile.html и &quot;QTextStream&amp;quot;:http://doc.qt.nokia.com/4.7/qtextstream.html для реализации чтения директории и операций ввода/вывода.
<br /><code><br /> class Directory : public QObject{
<br /> Q_OBJECT
<br /> Q_PROPERTY(int filesCount READ filesCount CONSTANT)<br /> Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)<br /> Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)<br /> Q_PROPERTY(QDeclarativeListProperty&amp;lt;File&amp;gt; files READ files CONSTANT )
<br /> …<br /></code>
<br />Класс ''Directory'' использует систему мета-информации Qt для регистрации свойств, необходимых для работы с файлами. Класс ''Directory'' экспортируется как плагин и может использоваться как элемент в контексте QML. Каждое из перечисленных свойств, использующих макрос &quot;Q_PROPERTY&amp;quot;:http://doc.qt.nokia.com/4.7/qobject.html#Q_PROPERTY, является свойством QML.
<br />&quot;Q_PROPERTY&amp;quot;:http://doc.qt.nokia.com/4.7/qobject.html#Q_PROPERTY объявляет свойство, а также его функции чтения и записи. Например, свойство filename, имеющее тип &quot;QString&amp;quot;:http://doc.qt.nokia.com/4.7/qstring.html, читается при помощи функции filename() и устанавливается при помощи функции setFilename(). А каждый раз когда значение этого свойства меняется, генерируется сигнал filenameChanged(). Функции чтения и записи свойств обьявлены как ''public'' в файле заголовка.
<br />Точно так же у нас есть другие свойства, объявленные в соответствии с их использованием. Свойство ''filesCount'' указывает количество файлов в директории. Свойство ''filename'' содержит имя текущего выбранного файла. Содержимое файла для чтения и записи хранится в свойстве ''fileContent''.
<br /><code><br />Q_PROPERTY(QDeclarativeListProperty&amp;lt;File&amp;gt; files READ files CONSTANT )<br /></code>
<br />Свойство ''files'' содержит список всех отфильтрованных файлов в директории. Класс ''Directory'' реализован так, чтобы отображать только корректные текстовые файлы; в данном случае, корректными считаются только файлы с расширением &quot;.txt&amp;quot;. Объекты класса &quot;QList&amp;quot;:http://doc.qt.nokia.com/4.7/qlist.html могут использоваться в QML после объявления их с ключевым словом ''QDeclarativeListProperty'' в коде на C+''. Заметим, что класс, указанный в качестве параметр-класса, должен быть потомком ''QObject'', то есть класс ''File'' также должен наследовать класс ''QObject''. В классе ''Directory'' список объектов типа ''File'' хранится в переменной ''m_fileList'' типа &quot;QList&amp;quot;:http://doc.qt.nokia.com/4.7/qlist.html.
<br /><code><br /> class File : public QObject{
<br /> Q_OBJECT<br /> Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
<br /> …<br /> };<br /></code>
<br />Свойства могут использоваться в QML как свойства элементов ''Directory''. Заметим, что нам не следует создавать свойство ''id'' для объектов на языке C.
<br /><code><br /> Directory{<br /> id: directory
<br /> filesCount<br /> filename<br /> fileContent<br /> files
<br /> files[0].name<br /> }<br /></code>
<br />Поскольку QML использует синтаксис и структуру языка Javascript, мы можем пройти по списку файлов как по массиву и узнать их свойства. Например, чтобы получить имя первого файла, нам достаточно обратиться к свойству ''files[0].name''.
<br />Обычные функции C''+ также доступны из QML. Функции загрузки и сохранения файлов реализованы на языке C++ и определены с макросом ''Q_INVOKABLE''. Также функцию можно объявить как слот (''slot'') и тогда она тоже будет доступна в QML.
<br /><code><br /> В файле Directory.h:
<br /> Q_INVOKABLE void saveFile&amp;amp;#40;&amp;#41;;<br /> Q_INVOKABLE void loadFile&amp;amp;#40;&amp;#41;;<br /></code>
<br />Класс ''Directory'' также должен сообщать другим объектам о том, что содержимое каталога изменилось. Для этого объект данного класса генерирует определенный сигнал (''signal''). Как уже отмечалось ранее, сигналы в QML имеют соответствующие обработчики, начинающиеся с приставки ''on''. В данном случае, сигнал называется ''directoryChanged'' и генерируется каждый раз при обновлении содержимого каталога. При обновлении заново загружается список файлов в каталоге и создается список корректных текстовых файлов. Для того, чтобы объекты QML могли получать этот сигнал, в них необходимо реализовать обработчик сигнала ''onDirectoryChanged''.
<br />Более подробно стоит рассмотреть свойства объектов на C++. Свойства списка используют обратный вызов (''callback'') для получения и изменения содержимого списка. Свойства списка имеют тип ''QDeclarativeListProperty&amp;lt;File&amp;gt;''. Каждый раз при обращении к списку, функция доступа должна возвращать ''QDeclarativeListProperty&amp;lt;File&amp;gt;''. Так как параметр-класс ''File'' является наследником ''QObject'', то при создании свойств ''QDeclarativeListProperty'' необходимо передать в конструктор ссылки на функции доступа и модификаторы. Также класс списка (в нашем случае ''QList'') должен быть списком ссылок на объекты ''File''.
<br />Ниже приведено определение конструктора ''QDeclarativeListProperty'' из класса ''Directory'':
<br /><code><br /> QDeclarativeListProperty ( QObject''' object, void * data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0 )<br /> QDeclarativeListProperty&amp;lt;File&amp;gt;( this, &amp;m_fileList, &amp;appendFiles, &amp;filesSize, &amp;fileAt, &amp;clearFilesPtr );<br /></code>


===Реализация выдвижной панели===
В качестве аргументов, конструктору передаются ссылки на функции, которые позволяют добавлять элементы в список, узнавать количество элементов, получать элемент по индексу и очищать список. Однако, обязательной является только функция добавления элементов в список. Стоит заметить, что указатели должны ссылаться на функции, соответствующие описанию функций &quot;AppendFunction&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AppendFunction-typedef, &quot;CountFunction&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#CountFunction-typedef, &quot;AtFunction&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AtFunction-typedef или &quot;ClearFunction&amp;quot;:http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#ClearFunction-typedef.


Наш текстовый редактор выглядит очень просто, поэтому мы попробуем его украсить. Используя <span class="caps">QML</span>, мы можем объявить переходы (''transitions'') и добавить анимацию нашему редактору. Меню занимает одну треть окна и будет здорово, если оно будет отображаться только тогда, когда нам это нужно.
<code><br /> void appendFiles(QDeclarativeListProperty&amp;lt;File&amp;gt; * property, File * file)<br /> File* fileAt(QDeclarativeListProperty&amp;lt;File&amp;gt; * property, int index)<br /> int filesSize(QDeclarativeListProperty&amp;lt;File&amp;gt; * property)<br /> void clearFilesPtr(QDeclarativeListProperty&amp;lt;File&amp;gt; *property)<br /></code>


Мы можем добавить выдвижную панель, которая будет сворачивать или разворачивать меню по клику. В нашей реализации, мы сделаем узкий прямоугольник, который будет реагировать на нажатия мыши. Панель также как и приложение будет иметь два состояния: “открыта” и “закрыта”.
Чтобы упростить нашу диалоговую форму, класс ''Directory'' отфильтровывает некорректные файлы, имеющие расширение, отличное от ''.txt''. То есть пользователь в списке увидит только файлы с расширением ''.txt''. Также проверяется, чтобы сохраняемый файл также имел расширение ''.txt''. ''Directory'' использует класс &quot;QTextStream&amp;quot;:http://doc.qt.nokia.com/4.7/qtextstream.html для чтения данных и записи в файл.


Элемент ''drawer'' – это узкая горизонтальная полоска. Вложенный элемент [http://doc.qt.nokia.com/4.7/qml-image.html Image] ''[doc.qt.nokia.com]'' отображает картинку со стрелкой в центре панели. ''drawer'' меняет состояние всего приложения с помощью идентификатора ''screen'', каждый раз когда пользователь кликнул по нему.
Используя наш элемент ''Directory'', мы можем получить список файлов, определить количество текстовых файлов в каталоге приложения, получить имя файла и его содержимое в виде строки &quot;QString&amp;quot;:http://doc.qt.nokia.com/4.7/qstring.html, а также получить информацию об изменении содержимого каталога.


''state'' – это простая коллекция настроек, она объявляется в элементе [http://doc.qt.nokia.com/4.7/qml-state.html State] ''[doc.qt.nokia.com]''. Группы состояний могут быть связаны в свойство ''states''. В нашем приложении существуют два состояния ''<span class="caps">DRAWER</span>_CLOSED'' и ''<span class="caps">DRAWER</span>_OPEN''. Настройки элемента объявляются в элементах [http://doc.qt.nokia.com/4.7/qml-propertychanges.html PropertyChanges] ''[doc.qt.nokia.com]''. В состоянии ''<span class="caps">DRAWER</span>_OPEN'' у всех четырех элементов будут изменены свойства. ''menuBar'' изменит значение свойства ''y'' на 0, ''textArea'' опуститься ниже, а стрелка на панели изменит направление.
Для создания плагина необходимо выполнить команду ''qmake'' для файла проекта ''cppPlugins.pro''. Чтобы собрать бинарный файл и поместить его в каталог с плагинами (_plugins), необходимо выполнить команду ''make''.


Изменения состояний нуждаются в более плавном переходе. Переходы между состояниями объявляются в элементе [http://doc.qt.nokia.com/4.7/qml-transition.html Transition] ''[doc.qt.nokia.com]'', который можно связать со свойством ''transitions'' нашего элемента. Наш редактор находится в режиме перехода каждый раз, когда его состояние меняется с ''<span class="caps">DRAWER</span>_OPEN'' на ''<span class="caps">DRAWER</span>_CLOSED'' и наоборот. Важно заметить, что ''Transition'' нуждается в объявленных свойствах ''from'' и ''to'' – начальном и конечном состоянии, но для наших переходов мы можем использовать обобщающий символ *, означающий, что переход применим для всех состояний.
=== Импорт плагина в QML ===


Во время перехода мы можем добавить анимацию для изменения свойств. Наш ''menuBar'' меняет позицию с ''y:0'' на ''y:-partition'' и мы можем анимировать этот переход используя элемент [http://doc.qt.nokia.com/4.7/qml-numberanimation.html NumberAnimation] ''[doc.qt.nokia.com]''. Мы объявляем временной интервал и переходную кривую (''easing curve'') для свойств, которые будут изменяться при анимации. Переходные кривые контролируют коэффициенты анимации и поведение интерполяции во время переходов между состояниями. Кривая, которую мы выбрали – [http://doc.qt.nokia.com/4.7/qml-propertyanimation.html#easing.type-prop Easing.OutQuint] ''[doc.qt.nokia.com]'', она замедляет движение в конце анимации.
Утилита ''qmlviewer'' импортирует файлы, расположенные в той же директории, что и разрабатываемое QML-приложение. Мы также можем создать файл ''qmldir'' содержащий пути до QML-файлов, которые мы хотим импортировать. Также в файле ''qmldir'' мы можем хранить информацию о расположении плагинов и других ресурсов.


Пожалуйста, прочитайте статью [http://doc.qt.nokia.com/4.7/qdeclarativeanimation.html <span class="caps">QML</span> Animation] ''[doc.qt.nokia.com]'' для получения более подробного описания различных типов анимации.
<code><br /> В qmldir:


Другой способ анимации изменения свойств, это объявление элемента [http://doc.qt.nokia.com/4.7/qml-behavior.html Behavior] ''[doc.qt.nokia.com]''. Переход работает только во время изменения состояний и [http://doc.qt.nokia.com/4.7/qml-behavior.html Behavior] ''[doc.qt.nokia.com]'' может установить анимацию для основных изменяемых свойств. В нашем редакторе, стрелка имеет анимацию типа ''NumberAnimation'', что позволяет поворачивать стрелку при запуске анимации. Это делается через изменение свойства ''rotation''.
Button ./Button.qml<br /> FileDialog ./FileDialog.qml<br /> TextArea ./TextArea.qml<br /> TextEditor ./TextEditor.qml<br /> EditMenu ./EditMenu.qml


Вернемся к нашим компонентам. Мы можем улучшить их внешний вид, используя полученные знания о состояниях и переходах. В файле ''Button.qml'', мы можем добавить изменение свойств ''color'' и ''scale'' когда кнопка нажата. Цвета изменяются с помощью [http://doc.qt.nokia.com/4.7/qml-coloranimation.html ColorAnimation] ''[doc.qt.nokia.com]'', а масштаб с помощью [http://doc.qt.nokia.com/4.7/qml-numberanimation.html NumberAnimation] ''[doc.qt.nokia.com]''. Запись ''on propertyName'' полезна когда требуется применить анимацию только на одно свойство.
plugin FileDialog plugins<br /></code>


Также мы можем улучшить внешний вид наших <span class="caps">QML</span>-компонентов, добавив такие цветовые эффекты как градиенты и изменение прозрачности. Объявление элемента [http://doc.qt.nokia.com/4.7/qml-gradient.html Gradient] ''[doc.qt.nokia.com]'' переопределяет свойство ''color''. Вы можете объявить цвет градиента используя элемент [http://doc.qt.nokia.com/4.7/qml-gradientstop.html GradientStop] ''[doc.qt.nokia.com]''. Позиция (''position'') элементов ''GradientStop'' может принимать значения между 0.0 и 1.0.
Плагин, который мы создали, называется ''FileDialog'', что указывается в поле ''TARGET'' в файле описания проекта. Скомпилированный плагин располагается в каталоге ''plugins''.


Этот градиент используется для придания объема панели меню. Первый цвет начинается с 0.0 и последний заканчивается на 1.0
=== Интеграция файлового диалога в меню ===


===Что делать дальше?===
Наш элемент ''FileMenu'' должен отображать элемент ''FileDialog'', содержащий список текстовых файлов в директории, что позволяет пользователю выбрать файл, просто кликнув на него в списке. Также необходимо назначить действия для кнопок сохранения, загрузки и создания нового документа. ''FileMenu'' содержит поле ввода, позволяющее пользователю ввести имя файла с помощью клавиатуры.


Мы закончили разработку пользовательского интерфейса очень простого текстового редактора. Продолжаем двигаться дальше, пользовательский интерфейс готов, и мы можем разрабатывать логику приложения, используя обычный Qt и C++. <span class="caps">QML</span> хорошо подходит как инструмент для построения прототипов и отделения логики приложения от графического пользовательского интерфейса.
Элемент ''Directory'', используемый в файле ''FileMenu.qml'', уведомляет элемент ''FileDialog'' о том, что необходимо обновить список отображаемых файлов. Этот сигнал обрабатывается в функции ''onDirectoryChanged''.


[[Image:qml-texteditor4_texteditor.png|Text Editor]]
<code><br />In FileMenu.qml:


==Расширяем возможности <span class="caps">QML</span> с помощью C++==
Directory{<br /> id:directory<br /> filename: textInput.text<br /> onDirectoryChanged: fileDialog.notifyRefresh()<br /> }<br /></code>


Теперь, когда мы имеем макет нашего приложения, мы можем приступить к реализации возможностей текстового редактора на C++. Использование <span class="caps">QML</span> вместе с C++ позволяет нам реализовать логику приложения с помощью Qt. Мы можем создать <span class="caps">QML</span>-контекст в приложении C++ используя классы [http://doc.qt.nokia.com/4.7/qtbinding.html Qt Declarative] ''[doc.qt.nokia.com]'' и отображать элементы <span class="caps">QML</span> при помощи [http://doc.qt.nokia.com/4.7/qdeclarativeview.html QDeclarativeView] ''[doc.qt.nokia.com]'', который основан на [http://doc.qt.nokia.com/4.7/graphicsview.html Graphics View Framework] ''[doc.qt.nokia.com]''. Или же, мы можем экспортировать C++ код в плагин, который сможет использоваться в ''qmlviewer''. Для нашего приложения мы разработаем функции загрузки и сохранения файла на C++ и экспортируем их как плагин. Это позволит запускать <span class="caps">QML</span>-приложение непосредственно в ''qmlviewer'', а также полноценно работать с дизайнером <span class="caps">QML</span> в Qt Creator.
Чтобы упростить разработку нашего приложения, мы не будем скрывать наш файловый диалог. И как отмечалось ранее, наш файловый диалог будет отображать в списке только текстовые файлы с расширением ''.txt''.


===Делаем C++ классы доступными в <span class="caps">QML</span>===
<code><br />В FileDialog.qml:


Мы будем реализовывать загрузку и сохранение файла используя Qt и C++. C++ классы и функции могут использоваться в <span class="caps">QML</span> только после того, как будут зарегистрированы в нем. Класс должен быть собран как Qt-плагин, а <span class="caps">QML</span>-приложение должно знать месторасположение плагина.
signal notifyRefresh()<br /> onNotifyRefresh: dirView.model = directory.files<br /></code>


Для нашего приложения, нам потребуется создать следующие элементы:
Компонент ''FileDialog'' будет отображать содержимое текущего каталога, используя список под названием ''files''. Для отображения элементов этот список использует компонент(представление) ''GridView'', который отображает данные в виде таблицы с использованием делегатов. Делегат отвечает за внешний вид модели, и наш файловый диалог просто отобразит текстовую таблицу, с расположенными в центре именами файлов. При клике по имени файла, появится прямоугольник, обрамляющий выбранный элемент. Также наш FileDialog будет обновлять список файлов при получении соответствующего сигнала.


# Класс ''Directory'', который хранит список файлов (обьекты типа ''File'') и отвечает за операции с каталогами
<code><br />В FileMenu.qml:
# Класс ''File'', наследник [http://doc.qt.nokia.com/4.7/qobject.html QObject] ''[doc.qt.nokia.com]'', который хранит имя файла
# Класс плагина, который будет регистрироваться <span class="caps">QML</span>-контексте
# Файл проекта Qt (с расширением .pro), для описания настроек сборки плагина
# Файл ''qmldir'', который сообщает ''qmlviewer'' о том, где искать наш плагин


===Сборка плагина===
Button{<br /> id: newButton<br /> label: &quot;New&amp;quot;<br /> onButtonClick:{<br /> textArea.textContent = &quot;&quot;<br /> }<br /> }<br /> Button{<br /> id: loadButton<br /> label: &quot;Load&amp;quot;<br /> onButtonClick:{<br /> directory.filename = textInput.text<br /> directory.loadFile&amp;amp;#40;&amp;#41;<br /> textArea.textContent = directory.fileContent<br /> }<br /> }<br /> Button{<br /> id: saveButton<br /> label: &quot;Save&amp;quot;<br /> onButtonClick:{<br /> directory.fileContent = textArea.textContent<br /> directory.filename = textInput.text<br /> directory.saveFile&amp;amp;#40;&amp;#41;<br /> }<br /> }<br /> Button{<br /> id: exitButton<br /> label: &quot;Exit&amp;quot;<br /> onButtonClick:{<br /> Qt.quit()<br /> }<br /> }<br /></code>
 
Чтобы собрать плагин, нам необходимо добавить указания на исходники, заголовки, и модули Qt в наш файл проекта. Весь код на C++ и файлы проекта находятся в директории ''filedialog''.
 
Мы включаем в сборку модуль ''declarative'' и указываем, что мы хотим собрать плагин (<span class="caps">TEMPLATE</span> = lib, <span class="caps">CONFIG</span> = plugin ). Также мы указываем, что собранный плагин должен быть помещен в директорию ''plugins'', расположенную в родительском каталоге.
 
===Регистрация класса в <span class="caps">QML</span>===
 
Наш класс плагина ''DialogPlugin'' является наследником [http://doc.qt.nokia.com/4.7/qdeclarativeextensionplugin.html QDeclarativeExtensionPlugin] ''[doc.qt.nokia.com]''. Нам необходимо переопределить виртуальную функцию [http://doc.qt.nokia.com/4.7/qdeclarativeextensionplugin.html#registerTypes registerTypes()] ''[doc.qt.nokia.com]''. Файл ''dialogPlugin.cpp'' выглядит следующим образом:
 
Функция [http://doc.qt.nokia.com/4.7/qdeclarativeextensionplugin.html#registerTypes registerTypes()] ''[doc.qt.nokia.com]'' регистрирует наши классы ''File'' И ''Directory'' в <span class="caps">QML</span>. Эта функция требует названия классов для их прототипов, номера старшей и младшей версий классов и их имена.
 
Нам необходимо экспортировать плагин с помощью макроса [http://doc.qt.nokia.com/4.7/qtplugin.html#Q_EXPORT_PLUGIN2#q-export-plugin2 Q_EXPORT_PLUGIN2] ''[doc.qt.nokia.com]''. Обратите внимание, что в файле dialogPlugin.h мы имеем макрос [http://doc.qt.nokia.com/4.7/qobject.html#Q_OBJECT Q_OBJECT] ''[doc.qt.nokia.com]'' в начале нашего класса. Также нам надо запустить ''qmake'' для генерации мета-информации о наших классах.
 
===Создание <span class="caps">QML</span>-свойств в C++ классе===
 
Мы можем создавать <span class="caps">QML</span>-элементы и определять их свойства, используя C ++ и систему мета-информации Qt ([http://doc.qt.nokia.com/4.7/metaobjects.html Meta-Object System] ''[doc.qt.nokia.com]''). Мы можем сообщить Qt о свойствах наших объектов, используя для этого сигналы и слоты, тогда эти свойства будут доступны в <span class="caps">QML</span>.
 
Текстовый редактор должен иметь возможность загружать и сохранять файлы. Как правило, эти возможности содержатся в файловом диалоге. К счастью, мы можем использовать [http://doc.qt.nokia.com/4.7/qdir.html QDir] ''[doc.qt.nokia.com]'', [http://doc.qt.nokia.com/4.7/qfile.html QFile] ''[doc.qt.nokia.com]'' и [http://doc.qt.nokia.com/4.7/qtextstream.html QTextStream] ''[doc.qt.nokia.com]'' для реализации чтения директории и операций ввода/вывода.
 
Класс ''Directory'' использует систему мета-информации Qt для регистрации свойств, необходимых для работы с файлами. Класс ''Directory'' экспортируется как плагин и может использоваться как элемент в контексте <span class="caps">QML</span>. Каждое из перечисленных свойств, использующих макрос [http://doc.qt.nokia.com/4.7/qobject.html#Q_PROPERTY Q_PROPERTY] ''[doc.qt.nokia.com]'', является свойством <span class="caps">QML</span>.
 
[http://doc.qt.nokia.com/4.7/qobject.html#Q_PROPERTY Q_PROPERTY] ''[doc.qt.nokia.com]'' объявляет свойство, а также его функции чтения и записи. Например, свойство filename, имеющее тип [http://doc.qt.nokia.com/4.7/qstring.html QString] ''[doc.qt.nokia.com]'', читается при помощи функции filename() и устанавливается при помощи функции setFilename(). А каждый раз когда значение этого свойства меняется, генерируется сигнал filenameChanged(). Функции чтения и записи свойств обьявлены как ''public'' в файле заголовка.
 
Точно так же у нас есть другие свойства, объявленные в соответствии с их использованием. Свойство ''filesCount'' указывает количество файлов в директории. Свойство ''filename'' содержит имя текущего выбранного файла. Содержимое файла для чтения и записи хранится в свойстве ''fileContent''.
 
Свойство ''files'' содержит список всех отфильтрованных файлов в директории. Класс ''Directory'' реализован так, чтобы отображать только корректные текстовые файлы; в данном случае, корректными считаются только файлы с расширением “.txt”. Объекты класса [http://doc.qt.nokia.com/4.7/qlist.html QList] ''[doc.qt.nokia.com]'' могут использоваться в <span class="caps">QML</span> после объявления их с ключевым словом ''QDeclarativeListProperty'' в коде на C++. Заметим, что класс, указанный в качестве параметр-класса, должен быть потомком ''QObject'', то есть класс ''File'' также должен наследовать класс ''QObject''. В классе ''Directory'' список объектов типа ''File'' хранится в переменной ''m_fileList'' типа [http://doc.qt.nokia.com/4.7/qlist.html QList] ''[doc.qt.nokia.com]''.
 
Свойства могут использоваться в <span class="caps">QML</span> как свойства элементов ''Directory''. Заметим, что нам не следует создавать свойство ''id'' для объектов на языке C++.
 
Обычные функции C++ также доступны из <span class="caps">QML</span>. Функции загрузки и сохранения файлов реализованы на языке C++ и определены с макросом ''Q_INVOKABLE''. Также функцию можно объявить как слот (''slot'') и тогда она тоже будет доступна в <span class="caps">QML</span>.
 
Класс ''Directory'' также должен сообщать другим объектам о том, что содержимое каталога изменилось. Для этого объект данного класса генерирует определенный сигнал (''signal''). Как уже отмечалось ранее, сигналы в <span class="caps">QML</span> имеют соответствующие обработчики, начинающиеся с приставки ''on''. В данном случае, сигнал называется ''directoryChanged'' и генерируется каждый раз при обновлении содержимого каталога. При обновлении заново загружается список файлов в каталоге и создается список корректных текстовых файлов. Для того, чтобы объекты <span class="caps">QML</span> могли получать этот сигнал, в них необходимо реализовать обработчик сигнала ''onDirectoryChanged''.
 
Более подробно стоит рассмотреть свойства объектов на C++. Свойства списка используют обратный вызов (''callback'') для получения и изменения содержимого списка. Свойства списка имеют тип ''QDeclarativeListProperty&lt;File&gt;''. Каждый раз при обращении к списку, функция доступа должна возвращать ''QDeclarativeListProperty&lt;File&gt;''. Так как параметр-класс ''File'' является наследником ''QObject'', то при создании свойств ''QDeclarativeListProperty'' необходимо передать в конструктор ссылки на функции доступа и модификаторы. Также класс списка (в нашем случае ''QList'') должен быть списком ссылок на объекты ''File''.
 
Ниже приведено определение конструктора ''QDeclarativeListProperty'' из класса ''Directory'':
 
В качестве аргументов, конструктору передаются ссылки на функции, которые позволяют добавлять элементы в список, узнавать количество элементов, получать элемент по индексу и очищать список. Однако, обязательной является только функция добавления элементов в список. Стоит заметить, что указатели должны ссылаться на функции, соответствующие описанию функций [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AppendFunction-typedef AppendFunction] ''[doc.qt.nokia.com]'', [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#CountFunction-typedef CountFunction] ''[doc.qt.nokia.com]'', [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AtFunction-typedef AtFunction] ''[doc.qt.nokia.com]'' или [http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#ClearFunction-typedef ClearFunction] ''[doc.qt.nokia.com]''.
 
Чтобы упростить нашу диалоговую форму, класс ''Directory'' отфильтровывает некорректные файлы, имеющие расширение, отличное от ''.txt''. То есть пользователь в списке увидит только файлы с расширением ''.txt''. Также проверяется, чтобы сохраняемый файл также имел расширение ''.txt''. ''Directory'' использует класс [http://doc.qt.nokia.com/4.7/qtextstream.html QTextStream] ''[doc.qt.nokia.com]'' для чтения данных и записи в файл.
 
Используя наш элемент ''Directory'', мы можем получить список файлов, определить количество текстовых файлов в каталоге приложения, получить имя файла и его содержимое в виде строки [http://doc.qt.nokia.com/4.7/qstring.html QString] ''[doc.qt.nokia.com]'', а также получить информацию об изменении содержимого каталога.
 
Для создания плагина необходимо выполнить команду ''qmake'' для файла проекта ''cppPlugins.pro''. Чтобы собрать бинарный файл и поместить его в каталог с плагинами (''plugins), необходимо выполнить команду _make''.
 
===Импорт плагина в <span class="caps">QML</span>===
 
Утилита ''qmlviewer'' импортирует файлы, расположенные в той же директории, что и разрабатываемое <span class="caps">QML</span>-приложение. Мы также можем создать файл ''qmldir'' содержащий пути до <span class="caps">QML</span>-файлов, которые мы хотим импортировать. Также в файле ''qmldir'' мы можем хранить информацию о расположении плагинов и других ресурсов.
 
Плагин, который мы создали, называется ''FileDialog'', что указывается в поле ''<span class="caps">TARGET</span>'' в файле описания проекта. Скомпилированный плагин располагается в каталоге ''plugins''.
 
===Интеграция файлового диалога в меню===
 
Наш элемент ''FileMenu'' должен отображать элемент ''FileDialog'', содержащий список текстовых файлов в директории, что позволяет пользователю выбрать файл, просто кликнув на него в списке. Также необходимо назначить действия для кнопок сохранения, загрузки и создания нового документа. ''FileMenu'' содержит поле ввода, позволяющее пользователю ввести имя файла с помощью клавиатуры.
 
Элемент ''Directory'', используемый в файле ''FileMenu.qml'', уведомляет элемент ''FileDialog'' о том, что необходимо обновить список отображаемых файлов. Этот сигнал обрабатывается в функции ''onDirectoryChanged''.
 
Чтобы упростить разработку нашего приложения, мы не будем скрывать наш файловый диалог. И как отмечалось ранее, наш файловый диалог будет отображать в списке только текстовые файлы с расширением ''.txt''.
 
Компонент ''FileDialog'' будет отображать содержимое текущего каталога, используя список под названием ''files''. Для отображения элементов этот список использует компонент(представление) ''GridView'', который отображает данные в виде таблицы с использованием делегатов. Делегат отвечает за внешний вид модели, и наш файловый диалог просто отобразит текстовую таблицу, с расположенными в центре именами файлов. При клике по имени файла, появится прямоугольник, обрамляющий выбранный элемент. Также наш FileDialog будет обновлять список файлов при получении соответствующего сигнала.


Теперь можно соединить элементы меню ''FileMenu'' с соответствующими действиями. При нажатии на кнопку ''saveButton'', текст будет передан из ''TextEdit'' в свойство ''fileContent'' элемента ''directory'', а имя редактируемого файла будет скопировано из поля ввода. И после этого будет вызвана функция ''saveFile()'' для сохраниения файла. Кнопка ''loadButton'' похожа по функциональности. Кнопка ''newButton'' очищает содержимое ''TextEdit''.
Теперь можно соединить элементы меню ''FileMenu'' с соответствующими действиями. При нажатии на кнопку ''saveButton'', текст будет передан из ''TextEdit'' в свойство ''fileContent'' элемента ''directory'', а имя редактируемого файла будет скопировано из поля ввода. И после этого будет вызвана функция ''saveFile&amp;amp;#40;&amp;#41;'' для сохраниения файла. Кнопка ''loadButton'' похожа по функциональности. Кнопка ''newButton'' очищает содержимое ''TextEdit''.


Аналогично, кнопки ''EditMenu'' будут связаны с функциями ''TextEdit'', выполняющими копирование, вставку и выбор всего текста в редакторе.
Аналогично, кнопки ''EditMenu'' будут связаны с функциями ''TextEdit'', выполняющими копирование, вставку и выбор всего текста в редакторе.


[[Image:qml-texteditor5_filemenu.png|File Menu]]
p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor5_filemenu.png|File Menu]]


==Текстовый редактор завершен==
== Текстовый редактор завершен ==


[[Image:qml-texteditor5_newfile.png|New File]]
p=. [[Image:http://doc.qt.nokia.com/4.7/images/qml-texteditor5_newfile.png|New File]]


Итак, созданное приложение может использоваться как простой текстовый редактор, способное редактировать текст и сохранять его в файл.
Итак, созданное приложение может использоваться как простой текстовый редактор, способное редактировать текст и сохранять его в файл.


==Исходный код==
== Исходный код ==


Полный исходный код этого приложения можно взять [http://qt.gitorious.org/qt/qt/trees/4.7/examples/tutorials/gettingStarted/gsQml здесь] ''[qt.gitorious.org]''.
Полный исходный код этого приложения можно взять &quot;здесь&amp;quot;:http://qt.gitorious.org/qt/qt/trees/4.7/examples/tutorials/gettingStarted/gsQml.

Revision as of 14:17, 23 February 2015

[toc align_right="yes&quot; depth="3&quot;]

p{font-size:24px;font-weight:bold}. Введение в программирование на QML

Добро пожаловать в мир QML, декларативного языка описания пользовательских интерфейсов. В этом руководстве мы создадим простой текстовый редактор, используя QML. После прочтения данного руководства, вы будете готовы разрабатывать ваши приложения используя QML и C+.
h2. QML как средство для построения пользовательских интерфейсов
Мы разработаем простой текстовый редактор, который сможет загружать, сохранять и редактировать текст. Это руководство состоит из двух частей. Первая часть затрагивает разработку внешнего вида приложения и его поведения, используя декларативный язык QML. Во второй части на основе библиотек Qt и языка C+ будут реализованы функции загрузки и сохранения документов. Используя механизм метаобъектов Qt ("Meta-Object System&quot;:http://doc.qt.nokia.com/4.7/metaobjects.html), мы можем сделать функции на языке C++ доступными в качестве свойств QML элементов. Используя QML и C+, мы можем эффективно отделять логику интерфейса от логики приложения.
QML Text Editor
Для запуска примеров на QML, существует утилита "qmlviewer&quot;:http://doc.qt.nokia.com/4.7/qmlviewer.html, принимающая QML-файл в качестве аргумента. Для понимания части руководства, затрагивающей C, читателю потребуется знание основ разработки приложений с использованием Qt.
h2. Создание кнопки и меню
h3. Базовый компонент - кнопка
Мы начнем разработку нашего текстового редактора с создания кнопки (button). Функционально кнопка содержит чувствительную к нажатию мыши область и текстовый ярлык. Кнопка выполняет действие, когда пользователь нажимает на нее. В QML базовый визуальный элемент - "Rectangle&quot;:http://doc.qt.nokia.com/4.7/qml-rectangle.html (Прямоугольник). Rectangle имеет свойства, отвечающие за его внешний вид и расположение.


<br />import Qt 4.7
<br />Rectangle{<br /> id:simplebutton<br /> color: &quot;grey&amp;quot;<br /> width: 150<br /> height: 80<br /> Text{<br /> id: buttonLabel<br /> text: &quot;button label&amp;quot;<br /> anchors.centerIn: parent;<br /> }<br /> }<br />


Первая строка: import Qt 4.7 разрешает утилите qmlviewer импортировать QML-элементы, которые мы будем использовать позже. Эта строка должна присутствовать во всех QML-файлах. Эта строка указывает на то, какая версия библиотек Qt будет использоваться.
Созданный нами прямоугольник имеет уникальный идентификатор simplebutton, который задается в свойстве id. Значение свойств задаются после двоеточия. Так, например, для указания цвета прямоугольника, серый (grey) цвет определяется в свойстве color (строка color: "grey&quot;). Аналогично, мы можем задать свойства width (ширина) и height (высота).
Подэлемент Text (текст) является неизменяемым текстовым полем. Для него мы установим id равным buttonLabel. Чтобы установить отображаемый этим элементом текст, мы установим значение свойства text. Текстовая метка находится внутри прямоугольника и для того, чтобы разместить ее в центре нашего компонента, мы свяжем якоря (anchors) текстового элемента с его родителем (parent) через использование свойства anchors.centerIn (центрировать на указанном объекте). Положение элементов можно привязывать друг к другу через использование свойства anchors. Это позволяет упростить и ускорить процесс компоновки элементов на форме.
Мы сохраним код в файл SimpleButton.qml. Запуск qmlviewer с этим файлом в качестве аргумента (команда "qmlviewer SimpleButton.qml") покажет на экране серый прямоугольник с текстом.
p=. Simple Button
Для реализации нажатий на кнопку мы можем использовать обработку событий QML. Она очень похожа на механизм сигналов-слотов в Qt ("Signals and Slots&quot;:http://doc.qt.nokia.com/4.7/signalsandslots.html). С определенным сигналом (signal) мы можем связать выполнение заданных действий. Таким образом, при появлении указанного сигнала, запустится функция, называемая слотом (slot).


<br />Rectangle{<br /> id:simplebutton<br /> <br /> MouseArea{<br /> id: buttonMouseArea<br /> anchors.fill: parent //область для приема событий от мышки будет занимать всю родительскую область<br /> //сигнал onClicked обрабатывает клики мышкой по области MouseArea<br /> onClicked: console.log(buttonLabel.text'' &quot; clicked&amp;quot; )<br /> }<br /> }<br />

Мы добавляем элемент "MouseArea&quot;:http://doc.qt.nokia.com/4.7/qml-mousearea.html в наш simplebutton. MouseArea описывает интерактивную область, в которой обрабатываются все события от мышки (клики, перемещение, действия колеса прокрутки). Для нашей кнопки, мы закрепим MouseArea поверх всего simplebutton. Запись anchors.fill - это один из способов доступа к специальному свойству fill внутри группы свойств называемых anchors. QML использует механизм размещения элементов на основе якорей ("anchor based layout&quot;:http://doc.qt.nokia.com/4.7/qml-anchor-layout.html). Это означает, что элементы могут прикрепляться к другим объектам, создавая устойчивое размещение.

MouseArea имеет множество обработчиков сигналов, вызываемых во время действий мышки в определенных границах. Так, обработчик события onClicked вызывается каждый раз, когда происходит нажатие на кнопку мыши (на левую кнопку, по умолчанию). Мы можем связать необходимые действие с событием onClicked. В данном случае мы вызываем функцию console.log(buttonLabel.text + " clicked&quot;) как ответ на нажатие кнопки. console.log() - функция, которая позволяет выводить на консоль текстовые сообщения (это может быть полезно при отладке приложений). buttonLabel.text - свойство объекта buttonLabel, содержащее заданный ранее текст.

Файл SimpleButton.qml содержит все необходимое для отображения на экране текстовой кнопки и вывода в консоль сообщений о кликах по ней.

<br />Rectangle {<br /> id:Button<br /> 

property color buttonColor: &quot;lightblue&amp;quot;<br /> property color onHoverColor: &quot;gold&amp;quot;<br /> property color borderColor: &quot;white&amp;quot;

signal buttonClick()<br /> onButtonClick: {<br /> console.log(buttonLabel.text + &quot; clicked&amp;quot; )<br /> }

MouseArea{<br /> onClicked: buttonClick()<br /> hoverEnabled: true<br /> onEntered: parent.border.color = onHoverColor<br /> onExited: parent.border.color = borderColor<br /> }

//определяем цвет кнопки с использованием условного оператора<br /> color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor<br /> }<br />

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

Помимо предустановленных свойств, объекты в QML могут иметь и дополнительные свойства, определенные самим разработчиком. Эти так называемые "пользовательские свойства&quot; (custom properties) объявляются с помощью выражения вида: property type name (property - ключевое слово для объявления свойства, type - тип данных свойства, name - имя свойства).

Свойство buttonColor типа color объявлено и связано со значением "lightblue&quot;. В дальнейшем это свойство используется в операции определения цвета заливки кнопки. Отметим, что для установки значения свойств можно использовать не только двоеточие, но и более привычный знак равенства (=). Пользовательские свойства, разрешенные внутри элемента будут доступны и снаружи (когда мы будем создавать функции на языке C+). Базовые "типы QML&quot;:http://doc.qt.nokia.com/4.7/qdeclarativebasictypes.html это: int, string, real, а также тип, называемый variant.
В обработчиках сигналов onEntered (курсор мышки появился над объектом) и onExited (курсор покинул объект) мы меняем цвет рамки у кнопки. Когда курсор появляется над кнопкой, цвет ее рамки становится золотым ("gold&quot;); когда курсор покидает кнопку, рамка снова станет белой ("white&quot;).
Сигнал buttonClick объявлен в Button.qml с помощью ключевого слова signal. Все сигналы имеют свои обработчики, создаваемые автоматически, их имена начинаются с on. В результате, onButtonClick - это обработчик сигнала buttonClick, в котором выполняются необходимые действия (в данном случае, вывод текста в консоль). Далее мы связываем событие onClicked с обработчиком onButtonClicked. Такой механизм позволяет легко получить доступ к обработчику onButtonClicked из различных объектов. Например, элемент может иметь несколько областей типа MouseArea, и мы можем связать сигнал buttonClick с событиями в каждой из этих областей.
Итак, теперь мы имеем базовые представления о создании элементов с использованием QML, а также мы научились обрабатывать базовые события мыши. Мы создали текстовое поле внутри прямоугольника, настроили его свойства и указали реакцию на действия мыши. Идея создания элементов внутри других элементов применяется во всем текстовом редакторе.
Созданная кнопка бесполезна, если не использовать ее как компонент для выполнения каких-либо действий. В следующем разделе мы создадим меню, содержащее несколько кнопок.
p=. Button
h3. Создание элементов меню
Мы уже научились создавать элементы и определять их поведение в одном QML-файле. В этом разделе мы научимся импортировать созданные ранее элементы и повторно использовать их при разработке новых компонентов.
Меню представляет собой компонент, содержащий список из нескольких элементов, определяющих различные действия. Существует несколько способов создания меню с использованием QML. Для начала, мы создадим меню из нескольких кнопок. Код, реализующий меню "Файл&quot; (File), можно найти в файле FileMenu.qml.


<br /> import Qt 4.7 //импортируем модуль Qt QML<br /> import &quot;folderName&amp;quot; //импортируем содержимое папки<br /> import &quot;script.js&amp;quot; as Script //импортируем код из файла Javascript, назовем этот код именем Script<br />


Представленный выше код показывает, как можно использовать ключевое слово import. Это необходимо для того, чтобы использовать файлы JavaScript или QML, которые расположены в другом каталоге. Так как файл Button.qml находится в той же папке, что и файл FileMenu.qml, то у нас нет необходимости импортировать файл Button.qml для работы с ним. Мы можем просто создать элемент класса Button, объявив Button{} так же как мы ранее использовали объявление Rectangle{}.


<br /> В файле FileMenu.qml:
<br /> Row{<br /> anchors.centerIn: parent<br /> spacing: parent.width/6
<br /> Button{<br /> id: loadButton<br /> buttonColor: &quot;lightgrey&amp;quot;<br /> label: &quot;Load&amp;quot;<br /> }<br /> Button{<br /> buttonColor: &quot;grey&amp;quot;<br /> id: saveButton<br /> label: &quot;Save&amp;quot;<br /> }<br /> Button{<br /> id: exitButton<br /> label: &quot;Exit&amp;quot;<br /> buttonColor: &quot;darkgrey&amp;quot;
<br /> onButtonClick: Qt.quit()<br /> }<br /> }<br />


В файле FileMenu.qml мы создали три элемента класса Button. Все эти кнопки расположены внутри элемента класса Row, который позволяет располагать дочерние элементы в вертикальных столбцах. Определение класса Button дано в файле Button.qml, описанном в предыдущем разделе. Для новых кнопок мы задаем новые значения свойств, которые перезапишут значения по умолчанию, указанные в файле Button.qml. При клике на кнопку с именем exitButton приложение закрывается. Стоит заметить, что действия обработчика onButtonClick из Button.qml будут вызываться в дополнение к обработчику onButtonClick в описании exitButton.
p=. меню "Файл"
Класс Row создает прямоугольный контейнер для расположения кнопок по столбцам. Этот дополнительный прямоугольник позволяет организовать группу кнопок в виде простого меню.
Меню "Правка&quot; (Edit) определяется аналогичным образом как и меню "Файл&quot; и содержит кнопки с ярлыками "Копировать&quot; (Copy), "Вставить&quot; (Paste) и "Выбрать все&quot; (Select All).
p=. меню "Правка"
Освоив импорт и повторное использование ранее созданных элементов, мы теперь можем перейти к созданию панели меню. Также нам предстоит освоить способы структурирования данных в QML.
h2. Создание панели меню
Для нашего текстового редактора необходимо найти способ отображения различных меню. Поэтому в данном разделе мы займемся реализацией специальной панели (Menu Bar), которая позволит нам переключаться между различным дочерними меню. Это означает, что нам необходимо определить структуру, позволяющую не просто отображать кнопки в строку, но и переключаться между несколькими меню. Для подобных задач в QML реализован механизм "модель-представление&quot; (Model-View), позволяющий отделить структурированные данные от их отображения на экране.
h3. Использование моделей и представлений
В QML имеется несколько представлений ("data view&quot;:http://doc.qt.nokia.com/4.7/qdeclarativemodels.html) для отображения моделей данных ("data model&quot;:http://doc.qt.nokia.com/4.7/qdeclarativemodels.html). Наша панель будет отображать строку со списком меню, содержащим названия меню. Список меню определен внутри модели "VisualItemModel&quot;:http://doc.qt.nokia.com/4.7/qml-visualitemmodel.html. Элемент класса VisualItemModel содержит объекты, которые уже имеют свои представления (view) и могут отображать свои данные самостоятельно, например, с помощью рассмотренного ранее объекта Rectangle. Остальные типы моделей (например, "ListModel&quot;:http://doc.qt.nokia.com/4.7/qml-listmodel.html) требуют наличие объектов-делегатов (delegate).
Мы определим два визуальных элемента (FileMenu и EditMenu) внутри модели menuListModel, а затем настроим оба меню и отобразим их с использованием "ListView&quot;:http://doc.qt.nokia.com/4.7/qml-listview.html. QML-файл MenuBar.qml содержит описания этих меню, а в файле EditMenu.qml можно найти реализацию простого меню "Правка&quot; (Edit).


<br /> VisualItemModel{<br /> id: menuListModel<br /> FileMenu{<br /> width: menuListView.width<br /> height: menuBar.height<br /> color: fileColor<br /> }<br /> EditMenu{<br /> color: editColor<br /> width: menuListView.width<br /> height: menuBar.height<br /> }<br /> }<br />


Компонент "ListView&quot;:http://doc.qt.nokia.com/4.7/qml-listview.html может отображать элементы опираясь на делегат (delegate). С помощью делегата может задать, чтобы данные отображались либо в строку с использованием элемента класса Row, либо в виде таблицы. Наша menuListModel уже содержит отображаемые элементы, поэтому нет необходимости использовать делегат.


<br /> ListView{<br /> id: menuListView
<br /> //якоря (anchors) привязаны к размерам родительского компонента<br /> anchors.fill:parent<br /> anchors.bottom: parent.bottom<br /> width:parent.width<br /> height: parent.height
<br /> //model содержит данные<br /> model: menuListModel
<br /> //контролируем жесты мышкой для смены меню<br /> snapMode: ListView.SnapOneItem<br /> orientation: ListView.Horizontal<br /> boundsBehavior: Flickable.StopAtBounds<br /> flickDeceleration: 5000<br /> highlightFollowsCurrentItem: true<br /> highlightMoveDuration:240<br /> highlightRangeMode: ListView.StrictlyEnforceRange<br /> }<br />


ListView является потомком класса "Flickable&quot;:http://doc.qt.nokia.com/4.7/qml-flickable.html, что позволяет списку реагировать на жесты мышкой. Последняя часть кода устанавливает свойства для Flickable. Это позволяет контролировать жесты мышкой для смены меню на нашем компоненте. Так, свойство highlightMoveDuration устанавливает длительность анимации при реакции на жесты - чем больше highlightMoveDuration, тем медленнее будет происходить переключение меню.
ListView позволяет получить доступ ко всем элементам модели с использованием индекса, присвоенного элементу при добавлении в список или определении в QML-файле. Изменение свойства currentIndex вызывает смену текущего выбранного элемента на ListView. Что можно видеть в заголовке нашей панели с меню. В заголовке расположено две кнопки, при клике на которые отображается соответствующее меню. При клике на кнопку fileButton выбирается меню "Файл&quot; (File), индекс которого равен 0, так как этот элемент был первым определен в описании menuListModel. Аналогично, кнопка editButton позволяет отобразить меню "Правка&quot; (Edit).
Положение элементов в QML можно описывать не только с помощью якорей, отвечающий за размещение по осям x и y, но и с помощью свойства z, которое позволяет размещать одни элементы поверх других, как в слоеном пироге. Чем больше z, тем выше располагается слой элемента в стеке слоев. По умолчанию свойство z равно 0, поэтому все элементы располагаются в одном слое. Для прямоугольника labelList мы установили значение z равным 1, что позволяет отображать этот объект поверх всех остальных элементов, у которых z не менялось и равно 0.


<br /> Rectangle{<br /> id: labelList<br /> <br /> z: 1<br /> Row{<br /> anchors.centerIn: parent<br /> spacing:40<br /> Button{<br /> label: &quot;File&amp;quot;<br /> id: fileButton<br /> <br /> onButtonClick: menuListView.currentIndex = 0<br /> }<br /> Button{<br /> id: editButton<br /> label: &quot;Edit&amp;quot;<br /> <br /> onButtonClick: menuListView.currentIndex = 1<br /> }<br /> }<br /> }<br />


Панель меню (menu bar), которую мы только что создали, позволяет переключать меню не только с помощью жестов мышки (в данном случае - перетаскивание), но через нажатие на соответствующие кнопки вверху панели.
p=. Панель меню
h2. Разработка текстового редактора
h3. Объявление TextArea
Наш текстовый редактор не будет текстовым редактором, если в нем не будет редактируемого тектового поля. Элемент QML "TextEdit&quot;:http://doc.qt.nokia.com/4.7/qml-textedit.html позволяет описать многострочное редактируемое поле. "TextEdit&quot;:http://doc.qt.nokia.com/4.7/qml-textedit.html отличается от элемента "Text&quot;:http://doc.qt.nokia.com/4.7/qml-text.html, который не позволяет пользователю напрямую редактировать текст.


<br />TextEdit{<br /> id: textEditor<br /> anchors.fill:parent<br /> width:parent.width; height:parent.height<br /> color:&quot;midnightblue&amp;quot;<br /> focus: true
<br /> wrapMode: TextEdit.Wrap
<br /> onCursorRectangleChanged: flickArea.ensureVisible(cursorRectangle)<br /> }<br />


У редактора установлены свойства: цвет шрифта и режим переноса. TextEdit находиться внутри Flickable области, которая будет прокручивать текст, если кусор находиться за пределами видимости. Функция ensureVisible() проверяет, если курсор находится за пределом видимых границ, то она соответственно перемещает текстовую область. QML использует синтаксис Javascript для выполнения этого скрипта, и, как уже упоминалось ранее, файлы Javascript могут быть импортированы и использованы в QML-файле.


<br />function ensureVisible®{<br /> if (contentX &gt;= r.x)<br /> contentX = r.x;<br /> else if (contentX+width &lt;= r.x+r.width)<br /> contentX = r.x+r.width-width;<br /> if (contentY &gt;= r.y)<br /> contentY = r.y;<br /> else if (contentY+height &lt;= r.y+r.height)<br /> contentY = r.y+r.height-height;<br /> }<br />


h3. Объединение компонентов для текстового редактора
Сейчас мы готовы разместить визуальные элементы для нашего текстового редактора, используя QML. Текстовый редактор имеет два компонента, главное меню, которое мы создали, и текстовое поле. QML позволяет нам использовать компоненты повторно. Поэтому, чтобы упростить наш код, мы импортируем компоненты и настроим их расположение. Окно нашего текстового редактора будет разбито на две части. Одну треть окна займет меню, а оставшиеся две трети - текстовое поле. Меню будет отображаться поверх остальных элементов.


<br />Rectangle{
<br /> id: screen<br /> width: 1000; height: 1000
<br /> //экран разделяется между MenuBar и TextArea. 1/3 экрана отдается под MenuBar<br /> property int partition: height/3
<br /> MenuBar{<br /> id:menuBar<br /> height: partition<br /> width:parent.width<br /> z: 1<br /> }
<br /> TextArea{<br /> id:textArea<br /> anchors.bottom:parent.bottom<br /> y: partition<br /> color: &quot;white&amp;quot;<br /> height: partition*2<br /> width:parent.width<br /> }<br /> }<br />


Благодаря повторному использованию компонентов, код нашего TextEditor выглядит достаточно простым. Мы можем настроить основное приложение, не заботясь о свойствах, поведение которых уже описано. Создание компонентов пользовательского интерфейса и размещение компонентов приложения может стать очень простой задачей при использовании этого подхода.
p=. Simple Editor


h2. Оформление текстового редактора
h3. Реализация выдвижной панели
Наш текстовый редактор выглядит очень просто, поэтому мы попробуем его украсить. Используя QML, мы можем объявить переходы (transitions) и добавить анимацию нашему редактору. Меню занимает одну треть окна и будет здорово, если оно будет отображаться только тогда, когда нам это нужно.
Мы можем добавить выдвижную панель, которая будет сворачивать или разворачивать меню по клику. В нашей реализации, мы сделаем узкий прямоугольник, который будет реагировать на нажатия мыши. Панель также как и приложение будет иметь два состояния: "открыта&quot; и "закрыта&quot;.
Элемент drawer - это узкая горизонтальная полоска. Вложенный элемент "Image&quot;:http://doc.qt.nokia.com/4.7/qml-image.html отображает картинку со стрелкой в центре панели. drawer меняет состояние всего приложения с помощью идентификатора screen, каждый раз когда пользователь кликнул по нему.


<br />Rectangle{<br /> id:drawer<br /> height:15
<br /> Image{<br /> id: arrowIcon<br /> source: &quot;images/arrow.png&amp;quot;<br /> anchors.horizontalCenter: parent.horizontalCenter<br /> }
<br /> MouseArea{<br /> id: drawerMouseArea<br /> anchors.fill:parent<br /> onClicked:{<br /> if (screen.state  &amp;quot;DRAWER_CLOSED&amp;quot;)&amp;#123;
                     screen.state = &amp;quot;DRAWER_OPEN&amp;quot;
                 &amp;#125;
                 else if (screen.state  &quot;DRAWER_OPEN&amp;quot;){<br /> screen.state = &quot;DRAWER_CLOSED&amp;quot;<br /> }<br /> }<br /> <br /> }<br /> }<br />


state - это простая коллекция настроек, она объявляется в элементе "State&quot;:http://doc.qt.nokia.com/4.7/qml-state.html. Группы состояний могут быть связаны в свойство states. В нашем приложении существуют два состояния DRAWER_CLOSED и DRAWER_OPEN. Настройки элемента объявляются в элементах "PropertyChanges&quot;:http://doc.qt.nokia.com/4.7/qml-propertychanges.html. В состоянии DRAWER_OPEN у всех четырех элементов будут изменены свойства. menuBar изменит значение свойства y на 0, textArea опуститься ниже, а стрелка на панели изменит направление.


<br />states:[<br /> State {<br /> name: &quot;DRAWER_OPEN&amp;quot;<br /> PropertyChanges { target: menuBar; y: 0}<br /> PropertyChanges { target: textArea; y: partition'' drawer.height}<br /> PropertyChanges { target: drawer; y: partition}<br /> PropertyChanges { target: arrowIcon; rotation: 180}<br /> },<br /> State {<br /> name: &quot;DRAWER_CLOSED&amp;quot;<br /> PropertyChanges { target: menuBar; y:<s>height; }<br /> PropertyChanges { target: textArea; y: drawer.height; height: screen.height</s> drawer.height}<br /> PropertyChanges { target: drawer; y: 0 }<br /> PropertyChanges { target: arrowIcon; rotation: 0 }<br /> }<br /> ]<br />

Изменения состояний нуждаются в более плавном переходе. Переходы между состояниями объявляются в элементе "Transition&quot;:http://doc.qt.nokia.com/4.7/qml-transition.html, который можно связать со свойством transitions нашего элемента. Наш редактор находится в режиме перехода каждый раз, когда его состояние меняется с DRAWER_OPEN на DRAWER_CLOSED и наоборот. Важно заметить, что Transition нуждается в объявленных свойствах from и to - начальном и конечном состоянии, но для наших переходов мы можем использовать обобщающий символ , означающий, что переход применим для всех состояний.
Во время перехода мы можем добавить анимацию для изменения свойств. Наш menuBar меняет позицию с y:0 на _y:partition_ и мы можем анимировать этот переход используя элемент "NumberAnimation&quot;:http://doc.qt.nokia.com/4.7/qml-numberanimation.html. Мы объявляем временной интервал и переходную кривую (easing curve) для свойств, которые будут изменяться при анимации. Переходные кривые контролируют коэффициенты анимации и поведение интерполяции во время переходов между состояниями. Кривая, которую мы выбрали "Easing.OutQuint&quot;:http://doc.qt.nokia.com/4.7/qml-propertyanimation.html#easing.type-prop, она замедляет движение в конце анимации.
Пожалуйста, прочитайте статью "QML Animation&quot;:http://doc.qt.nokia.com/4.7/qdeclarativeanimation.html для получения более подробного описания различных типов анимации.


<br />transitions: [<br /> Transition {<br /> to: &quot;'''&quot;<br /> NumberAnimation { target: textArea; properties: &quot;y, height&amp;quot;; duration: 100; easing.type:Easing.OutExpo }<br /> NumberAnimation { target: menuBar; properties: &quot;y&amp;quot;; duration: 100; easing.type: Easing.OutExpo }<br /> NumberAnimation { target: drawer; properties: &quot;y&amp;quot;; duration: 100; easing.type: Easing.OutExpo }<br /> }<br /> ]<br />

Другой способ анимации изменения свойств, это объявление элемента "Behavior&quot;:http://doc.qt.nokia.com/4.7/qml-behavior.html. Переход работает только во время изменения состояний и "Behavior&quot;:http://doc.qt.nokia.com/4.7/qml-behavior.html может установить анимацию для основных изменяемых свойств. В нашем редакторе, стрелка имеет анимацию типа NumberAnimation, что позволяет поворачивать стрелку при запуске анимации. Это делается через изменение свойства rotation.

<br />In TextEditor.qml:

Behavior{<br /> NumberAnimation{property: &quot;rotation&amp;quot;;easing.type: Easing.OutExpo }<br /> }<br />

Вернемся к нашим компонентам. Мы можем улучшить их внешний вид, используя полученные знания о состояниях и переходах. В файле Button.qml, мы можем добавить изменение свойств color и scale когда кнопка нажата. Цвета изменяются с помощью "ColorAnimation&quot;:http://doc.qt.nokia.com/4.7/qml-coloranimation.html, а масштаб с помощью "NumberAnimation&quot;:http://doc.qt.nokia.com/4.7/qml-numberanimation.html. Запись on propertyName полезна когда требуется применить анимацию только на одно свойство.

<br />In Button.qml:<br /> 

color: buttonMouseArea.pressed ? Qt.darker(buttonColor, 1.5) : buttonColor<br /> Behavior on color { ColorAnimation{ duration: 55} }

scale: buttonMouseArea.pressed ? 1.1 : 1.00<br /> Behavior on scale { NumberAnimation{ duration: 55} }<br />

Также мы можем улучшить внешний вид наших QML-компонентов, добавив такие цветовые эффекты как градиенты и изменение прозрачности. Объявление элемента "Gradient&quot;:http://doc.qt.nokia.com/4.7/qml-gradient.html переопределяет свойство color. Вы можете объявить цвет градиента используя элемент "GradientStop&quot;:http://doc.qt.nokia.com/4.7/qml-gradientstop.html. Позиция (position) элементов GradientStop может принимать значения между 0.0 и 1.0.

<br />В MenuBar.qml<br /> gradient: Gradient {<br /> GradientStop { position: 0.0; color: &quot;#8C8F8C&amp;quot; }<br /> GradientStop { position: 0.17; color: &quot;#6A6D6A&amp;quot; }<br /> GradientStop { position: 0.98;color: &quot;#3F3F3F&amp;quot; }<br /> GradientStop { position: 1.0; color: &quot;#0e1B20&amp;quot; }<br /> }<br />

Этот градиент используется для придания объема панели меню. Первый цвет начинается с 0.0 и последний заканчивается на 1.0

Что делать дальше?

Мы закончили разработку пользовательского интерфейса очень простого текстового редактора. Продолжаем двигаться дальше, пользовательский интерфейс готов, и мы можем разрабатывать логику приложения, используя обычный Qt и C+. QML хорошо подходит как инструмент для построения прототипов и отделения логики приложения от графического пользовательского интерфейса.
p=. Text Editor
h2. Расширяем возможности QML с помощью C+

Теперь, когда мы имеем макет нашего приложения, мы можем приступить к реализации возможностей текстового редактора на C+. Использование QML вместе с C+ позволяет нам реализовать логику приложения с помощью Qt. Мы можем создать QML-контекст в приложении C++ используя классы "Qt Declarative&quot;:http://doc.qt.nokia.com/4.7/qtbinding.html и отображать элементы QML при помощи "QDeclarativeView&quot;:http://doc.qt.nokia.com/4.7/qdeclarativeview.html, который основан на "Graphics View Framework&quot;:http://doc.qt.nokia.com/4.7/graphicsview.html. Или же, мы можем экспортировать C++ код в плагин, который сможет использоваться в qmlviewer. Для нашего приложения мы разработаем функции загрузки и сохранения файла на C++ и экспортируем их как плагин. Это позволит запускать QML-приложение непосредственно в qmlviewer, а также полноценно работать с дизайнером QML в Qt Creator.

Делаем C++ классы доступными в QML

Мы будем реализовывать загрузку и сохранение файла используя Qt и C+. C+ классы и функции могут использоваться в QML только после того, как будут зарегистрированы в нем. Класс должен быть собран как Qt-плагин, а QML-приложение должно знать месторасположение плагина.

Для нашего приложения, нам потребуется создать следующие элементы:
# Класс Directory, который хранит список файлов (обьекты типа File) и отвечает за операции с каталогами
# Класс File, наследник "QObject&quot;:http://doc.qt.nokia.com/4.7/qobject.html, который хранит имя файла
# Класс плагина, который будет регистрироваться QML-контексте
# Файл проекта Qt (с расширением .pro), для описания настроек сборки плагина
# Файл qmldir, который сообщает qmlviewer о том, где искать наш плагин

Сборка плагина

Чтобы собрать плагин, нам необходимо добавить указания на исходники, заголовки, и модули Qt в наш файл проекта. Весь код на C++ и файлы проекта находятся в директории filedialog.

<br />В cppPlugins.pro:

TEMPLATE = lib<br /> CONFIG ''= qt plugin<br /> QT''= declarative

DESTDIR ''= ../plugins<br /> OBJECTS_DIR = tmp<br /> MOC_DIR = tmp
<br /> TARGET = FileDialog
<br /> HEADERS''= directory.h  file.h  dialogPlugin.h

SOURCES += directory.cpp  file.cpp  dialogPlugin.cpp<br />

Мы включаем в сборку модуль declarative и указываем, что мы хотим собрать плагин (TEMPLATE = lib, CONFIG = plugin ). Также мы указываем, что собранный плагин должен быть помещен в директорию plugins, расположенную в родительском каталоге.

Регистрация класса в QML

<br /> В dialogPlugin.h:

#include &lt;QtDeclarative/QDeclarativeExtensionPlugin&amp;gt;

class DialogPlugin : public QDeclarativeExtensionPlugin<br /> {<br /> Q_OBJECT

public:<br /> void registerTypes(const char *uri);

};<br />

Наш класс плагина DialogPlugin является наследником "QDeclarativeExtensionPlugin&quot;:http://doc.qt.nokia.com/4.7/qdeclarativeextensionplugin.html. Нам необходимо переопределить виртуальную функцию "registerTypes()":http://doc.qt.nokia.com/4.7/qdeclarativeextensionplugin.html#registerTypes. Файл dialogPlugin.cpp выглядит следующим образом:

<br />DialogPlugin.cpp:

#include &quot;dialogPlugin.h&amp;quot;<br /> #include &quot;directory.h&amp;quot;<br /> #include &quot;file.h&amp;quot;<br /> #include &lt;QtDeclarative/qdeclarative.h&amp;gt;

void DialogPlugin::registerTypes(const char '''uri){
<br /> qmlRegisterType&amp;lt;Directory&amp;gt;(uri, 1, 0, &quot;Directory&amp;quot;);<br /> qmlRegisterType&amp;lt;File&amp;gt;(uri, 1, 0,&quot;File&amp;quot;);<br /> }
<br /> Q_EXPORT_PLUGIN2(FileDialog, DialogPlugin);<br />


Функция "registerTypes()":http://doc.qt.nokia.com/4.7/qdeclarativeextensionplugin.html#registerTypes регистрирует наши классы File И Directory в QML. Эта функция требует названия классов для их прототипов, номера старшей и младшей версий классов и их имена.
Нам необходимо экспортировать плагин с помощью макроса "Q_EXPORT_PLUGIN2&quot;:http://doc.qt.nokia.com/4.7/qtplugin.html#Q_EXPORT_PLUGIN2#q-export-plugin2. Обратите внимание, что в файле dialogPlugin.h мы имеем макрос "Q_OBJECT&quot;:http://doc.qt.nokia.com/4.7/qobject.html#Q_OBJECT в начале нашего класса. Также нам надо запустить qmake для генерации мета-информации о наших классах.
h3. Создание QML-свойств в C++ классе
Мы можем создавать QML-элементы и определять их свойства, используя C ' и систему мета-информации Qt ("Meta-Object System&quot;:http://doc.qt.nokia.com/4.7/metaobjects.html). Мы можем сообщить Qt о свойствах наших объектов, используя для этого сигналы и слоты, тогда эти свойства будут доступны в QML.
Текстовый редактор должен иметь возможность загружать и сохранять файлы. Как правило, эти возможности содержатся в файловом диалоге. К счастью, мы можем использовать "QDir&quot;:http://doc.qt.nokia.com/4.7/qdir.html, "QFile&quot;:http://doc.qt.nokia.com/4.7/qfile.html и "QTextStream&quot;:http://doc.qt.nokia.com/4.7/qtextstream.html для реализации чтения директории и операций ввода/вывода.


<br /> class Directory : public QObject{
<br /> Q_OBJECT
<br /> Q_PROPERTY(int filesCount READ filesCount CONSTANT)<br /> Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)<br /> Q_PROPERTY(QString fileContent READ fileContent WRITE setFileContent NOTIFY fileContentChanged)<br /> Q_PROPERTY(QDeclarativeListProperty&amp;lt;File&amp;gt; files READ files CONSTANT )
<br /> <br />


Класс Directory использует систему мета-информации Qt для регистрации свойств, необходимых для работы с файлами. Класс Directory экспортируется как плагин и может использоваться как элемент в контексте QML. Каждое из перечисленных свойств, использующих макрос "Q_PROPERTY&quot;:http://doc.qt.nokia.com/4.7/qobject.html#Q_PROPERTY, является свойством QML.
"Q_PROPERTY&quot;:http://doc.qt.nokia.com/4.7/qobject.html#Q_PROPERTY объявляет свойство, а также его функции чтения и записи. Например, свойство filename, имеющее тип "QString&quot;:http://doc.qt.nokia.com/4.7/qstring.html, читается при помощи функции filename() и устанавливается при помощи функции setFilename(). А каждый раз когда значение этого свойства меняется, генерируется сигнал filenameChanged(). Функции чтения и записи свойств обьявлены как public в файле заголовка.
Точно так же у нас есть другие свойства, объявленные в соответствии с их использованием. Свойство filesCount указывает количество файлов в директории. Свойство filename содержит имя текущего выбранного файла. Содержимое файла для чтения и записи хранится в свойстве fileContent.


<br />Q_PROPERTY(QDeclarativeListProperty&amp;lt;File&amp;gt; files READ files CONSTANT )<br />


Свойство files содержит список всех отфильтрованных файлов в директории. Класс Directory реализован так, чтобы отображать только корректные текстовые файлы; в данном случае, корректными считаются только файлы с расширением ".txt&quot;. Объекты класса "QList&quot;:http://doc.qt.nokia.com/4.7/qlist.html могут использоваться в QML после объявления их с ключевым словом QDeclarativeListProperty в коде на C+. Заметим, что класс, указанный в качестве параметр-класса, должен быть потомком QObject, то есть класс File также должен наследовать класс QObject. В классе Directory список объектов типа File хранится в переменной m_fileList типа "QList&quot;:http://doc.qt.nokia.com/4.7/qlist.html.


<br /> class File : public QObject{
<br /> Q_OBJECT<br /> Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
<br /> <br /> };<br />


Свойства могут использоваться в QML как свойства элементов Directory. Заметим, что нам не следует создавать свойство id для объектов на языке C.


<br /> Directory{<br /> id: directory
<br /> filesCount<br /> filename<br /> fileContent<br /> files
<br /> files[0].name<br /> }<br />


Поскольку QML использует синтаксис и структуру языка Javascript, мы можем пройти по списку файлов как по массиву и узнать их свойства. Например, чтобы получить имя первого файла, нам достаточно обратиться к свойству files[0].name.
Обычные функции C+ также доступны из QML. Функции загрузки и сохранения файлов реализованы на языке C++ и определены с макросом Q_INVOKABLE. Также функцию можно объявить как слот (slot) и тогда она тоже будет доступна в QML.


<br /> В файле Directory.h:
<br /> Q_INVOKABLE void saveFile&amp;amp;#40;&amp;#41;;<br /> Q_INVOKABLE void loadFile&amp;amp;#40;&amp;#41;;<br />


Класс Directory также должен сообщать другим объектам о том, что содержимое каталога изменилось. Для этого объект данного класса генерирует определенный сигнал (signal). Как уже отмечалось ранее, сигналы в QML имеют соответствующие обработчики, начинающиеся с приставки on. В данном случае, сигнал называется directoryChanged и генерируется каждый раз при обновлении содержимого каталога. При обновлении заново загружается список файлов в каталоге и создается список корректных текстовых файлов. Для того, чтобы объекты QML могли получать этот сигнал, в них необходимо реализовать обработчик сигнала onDirectoryChanged.
Более подробно стоит рассмотреть свойства объектов на C++. Свойства списка используют обратный вызов (callback) для получения и изменения содержимого списка. Свойства списка имеют тип QDeclarativeListProperty&lt;File&gt;. Каждый раз при обращении к списку, функция доступа должна возвращать QDeclarativeListProperty&lt;File&gt;. Так как параметр-класс File является наследником QObject, то при создании свойств QDeclarativeListProperty необходимо передать в конструктор ссылки на функции доступа и модификаторы. Также класс списка (в нашем случае QList) должен быть списком ссылок на объекты File.
Ниже приведено определение конструктора QDeclarativeListProperty из класса Directory:


<br /> QDeclarativeListProperty ( QObject''' object, void * data, AppendFunction append, CountFunction count = 0, AtFunction at = 0, ClearFunction clear = 0 )<br /> QDeclarativeListProperty&amp;lt;File&amp;gt;( this, &amp;m_fileList, &amp;appendFiles, &amp;filesSize, &amp;fileAt, &amp;clearFilesPtr );<br />

В качестве аргументов, конструктору передаются ссылки на функции, которые позволяют добавлять элементы в список, узнавать количество элементов, получать элемент по индексу и очищать список. Однако, обязательной является только функция добавления элементов в список. Стоит заметить, что указатели должны ссылаться на функции, соответствующие описанию функций "AppendFunction&quot;:http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AppendFunction-typedef, "CountFunction&quot;:http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#CountFunction-typedef, "AtFunction&quot;:http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#AtFunction-typedef или "ClearFunction&quot;:http://doc.qt.nokia.com/4.7/qdeclarativelistproperty.html#ClearFunction-typedef.

<br /> void appendFiles(QDeclarativeListProperty&amp;lt;File&amp;gt; * property, File * file)<br /> File* fileAt(QDeclarativeListProperty&amp;lt;File&amp;gt; * property, int index)<br /> int filesSize(QDeclarativeListProperty&amp;lt;File&amp;gt; * property)<br /> void clearFilesPtr(QDeclarativeListProperty&amp;lt;File&amp;gt; *property)<br />

Чтобы упростить нашу диалоговую форму, класс Directory отфильтровывает некорректные файлы, имеющие расширение, отличное от .txt. То есть пользователь в списке увидит только файлы с расширением .txt. Также проверяется, чтобы сохраняемый файл также имел расширение .txt. Directory использует класс "QTextStream&quot;:http://doc.qt.nokia.com/4.7/qtextstream.html для чтения данных и записи в файл.

Используя наш элемент Directory, мы можем получить список файлов, определить количество текстовых файлов в каталоге приложения, получить имя файла и его содержимое в виде строки "QString&quot;:http://doc.qt.nokia.com/4.7/qstring.html, а также получить информацию об изменении содержимого каталога.

Для создания плагина необходимо выполнить команду qmake для файла проекта cppPlugins.pro. Чтобы собрать бинарный файл и поместить его в каталог с плагинами (_plugins), необходимо выполнить команду make.

Импорт плагина в QML

Утилита qmlviewer импортирует файлы, расположенные в той же директории, что и разрабатываемое QML-приложение. Мы также можем создать файл qmldir содержащий пути до QML-файлов, которые мы хотим импортировать. Также в файле qmldir мы можем хранить информацию о расположении плагинов и других ресурсов.

<br /> В qmldir:

Button ./Button.qml<br /> FileDialog ./FileDialog.qml<br /> TextArea ./TextArea.qml<br /> TextEditor ./TextEditor.qml<br /> EditMenu ./EditMenu.qml

plugin FileDialog plugins<br />

Плагин, который мы создали, называется FileDialog, что указывается в поле TARGET в файле описания проекта. Скомпилированный плагин располагается в каталоге plugins.

Интеграция файлового диалога в меню

Наш элемент FileMenu должен отображать элемент FileDialog, содержащий список текстовых файлов в директории, что позволяет пользователю выбрать файл, просто кликнув на него в списке. Также необходимо назначить действия для кнопок сохранения, загрузки и создания нового документа. FileMenu содержит поле ввода, позволяющее пользователю ввести имя файла с помощью клавиатуры.

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

<br />In FileMenu.qml:

Directory{<br /> id:directory<br /> filename: textInput.text<br /> onDirectoryChanged: fileDialog.notifyRefresh()<br /> }<br />

Чтобы упростить разработку нашего приложения, мы не будем скрывать наш файловый диалог. И как отмечалось ранее, наш файловый диалог будет отображать в списке только текстовые файлы с расширением .txt.

<br />В FileDialog.qml:

signal notifyRefresh()<br /> onNotifyRefresh: dirView.model = directory.files<br />

Компонент FileDialog будет отображать содержимое текущего каталога, используя список под названием files. Для отображения элементов этот список использует компонент(представление) GridView, который отображает данные в виде таблицы с использованием делегатов. Делегат отвечает за внешний вид модели, и наш файловый диалог просто отобразит текстовую таблицу, с расположенными в центре именами файлов. При клике по имени файла, появится прямоугольник, обрамляющий выбранный элемент. Также наш FileDialog будет обновлять список файлов при получении соответствующего сигнала.

<br />В FileMenu.qml:

Button{<br /> id: newButton<br /> label: &quot;New&amp;quot;<br /> onButtonClick:{<br /> textArea.textContent = &quot;&quot;<br /> }<br /> }<br /> Button{<br /> id: loadButton<br /> label: &quot;Load&amp;quot;<br /> onButtonClick:{<br /> directory.filename = textInput.text<br /> directory.loadFile&amp;amp;#40;&amp;#41;<br /> textArea.textContent = directory.fileContent<br /> }<br /> }<br /> Button{<br /> id: saveButton<br /> label: &quot;Save&amp;quot;<br /> onButtonClick:{<br /> directory.fileContent = textArea.textContent<br /> directory.filename = textInput.text<br /> directory.saveFile&amp;amp;#40;&amp;#41;<br /> }<br /> }<br /> Button{<br /> id: exitButton<br /> label: &quot;Exit&amp;quot;<br /> onButtonClick:{<br /> Qt.quit()<br /> }<br /> }<br />

Теперь можно соединить элементы меню FileMenu с соответствующими действиями. При нажатии на кнопку saveButton, текст будет передан из TextEdit в свойство fileContent элемента directory, а имя редактируемого файла будет скопировано из поля ввода. И после этого будет вызвана функция saveFile&amp;#40;&#41; для сохраниения файла. Кнопка loadButton похожа по функциональности. Кнопка newButton очищает содержимое TextEdit.

Аналогично, кнопки EditMenu будут связаны с функциями TextEdit, выполняющими копирование, вставку и выбор всего текста в редакторе.

p=. File Menu

Текстовый редактор завершен

p=. New File

Итак, созданное приложение может использоваться как простой текстовый редактор, способное редактировать текст и сохранять его в файл.

Исходный код

Полный исходный код этого приложения можно взять "здесь&quot;:http://qt.gitorious.org/qt/qt/trees/4.7/examples/tutorials/gettingStarted/gsQml.