- Misc - -

Введение в Qt Quick 2 и QML

Решил тут ради интереса немного почитать про такую библиотеку, как Qt [1]. Заинтересовала она меня именно в плане создания GUI, так как в C++ с этим достаточно туго, а Qt - одна из немногих кроссплатформенных библиотек, позволяющая удобно делать современные гибкие графические интерфейсы к программам. Раньше с ней совершенно никакого опыта работы я не имел, поэтому пару недель занимался неспешным чтением документации.

В какой-то из версий Qt (если не ошибаюсь, в 4.7) появилась возможность делать интерфейсы на декларативном языке QML 1.0 (и модуль QtDeclarative, определяющий всякие штуки для рисования интерфейсов), а в 5-й версии Qt появился QML 2.0 [2] и QtQuick 2.0 [3] (бывший QtDeclarative). Их я и стал изучать. В этой статье я расскажу об этих модулях в меру своих знаний, покажу пару несложных примеров их использования, а кто дочитает до конца статьи, получит еще один бонусный пример, который я написал, пока изучал библиотеку. :)

Для того, чтобы понять идеологию и принципы работы интерфейса, написанного на QML, нужно почитать документацию, но я поясню упрощенно: вы пишете объявления различных объектов (таких, как прямоугольники, кнопки, окна и т.д.), описываете, как они должны быть расположены (координаты фиксировать не нужно, очень легко построить "резиновый" (т.е. автоматически подстраивающийся при изменении размеров окна) интерфейс, как в WPF), а Qt за вас всё отрисовывает и контролирует. QML тесно интегрирован с JavaScript, в результате очень удобно писать обработчики различных событий, привязки и просто вспомогательные функции. Их можно делать как прямо внутри QML-файлов, так и выносить в отдельные JS-файлы. В целом, QML и Qt Quick мне понравились гораздо больше, чем XAML и WPF. Во-первых, QML кажется мне проще, нагляднее и легче читается, чем XAML, во-вторых, позволяет писать кроссплатформенные интерфейсы под множество платформ (например, я смог скомпилировать небольшое приложение под Windows Phone 8.1 и запустить у себя на телефоне, при этом оно работало и на обычной Windows 7), в-третьих, очень тесно связан с JavaScript, что позволяет сделать полноценное приложение, вообще не написав ни строчки кода на C++. Есть, конечно, и минусы. С моей точки зрения, это необходимость таскать со своим приложением DLL-файлы от Qt, которые занимают немало места. С другой стороны, это оправдано, ведь библиотека кроссплатформенная. Можно, конечно, собрать всё статически, но тогда вы нарушите лицензию LGPL, да и приложение все равно будет занимать прилично места. В C# библиотека .NET просто устанавливается в Windows, но у пользователя изначально тоже может отсутствовать нужная версия. В Qt же есть специальная утилита, позволяющая собрать все необходимые для написанного приложения библиотеки в один каталог, чтобы потом заархивировать всё необходимое в инсталлятор (о ней я расскажу в конце статьи). Увы, есть в Qt и баги, на которые я наткнулся (пришлось выписать их в багтрекер), но их, к счастью, исправляют за разумное время. Также, возможно, QML имеет несколько более скудные возможности по сравнению с WPF, но я этого пока не заметил. Да и кастомные компоненты в QML делать чрезвычайно просто. Вынося вердикт, скажу, что плюсы библиотеки сильно перевешивают ее минусы, и в будущем я планирую продолжить ее изучение и использование.

А пока что, чтобы не быть голословным, приведу простой пример, как с этим вообще работать (скриншоты будут с Windows, но то же самое совсем не сложно будет проделать и на другой ОС, например, Ubuntu или Mac OS).

1. Для начала нужно скачать онлайн-инсталлятор Qt последней версии [4]. Можно скачать и оффлайн-инсталлятор на выбор. Для Windows доступны сборки с OpenGL и с ANGLE (это переходник с DirectX под интерфейс OpenGL). Разработчики Qt утверждают, что ANGLE под Windows сейчас работает стабильнее (по крайней мере, в версии 5.4.1, для которой я всё и описываю), особенно на старых системах, поэтому можно установить сборку с его использованием.

2. Теперь инсталлируем Qt. У вас уже должна быть установлена какая-нибудь Visual Studio. У меня есть 2013, поэтому для нее я сборку и скачивал.

3. Запускаем Qt Creator, в нем и будем вести разработку. Можно, конечно, разрабатывать и в Visual Studio, установив для этого специальный аддон Qt для Visual Studio, но в этом случае у вас не будет кроссплатформенных файлов проектов, которые можно открыть в программе Qt Creator на другой ОС, кроме того, будет меньше удобств при работе непосредственно с Qt и QML.

В Qt огромное количество различных примеров, по которым можно учиться. Например, можно просмотреть все примеры по теме Qt Quick. Для этого нужно выбрать секцию "Examples" в Qt Creator и в строку поиска ввести "quick":

[5]

Но мы сейчас создадим простейшее приложение с использованием Qt Quick на языке QML с нуля. Для этого выбираем секцию "Projects" и нажимаем "New project". Выбираем тип проекта "Application", "Qt Quick Application":

[6]

Этот тип приложения подразумевает программу на C++ с использованием QML. Необходимый код на C++ за нас сгенерируется автоматически, поэтому можно будет написать приложение на чистом QML и JavaScript. Нажимаем "Choose", вводим имя проекта (я задал mytest) и указываем, в какой директории его создавать, нажимаем "Next". Далее будет предложено выбрать набор компонентов, с использованием которых мы будем делать наш интерфейс. Здесь можно выбрать Qt Quick последней версии (2.4), этот выбор в любом случае никак не будет вас ограничивать и нужен только для того, чтобы Qt Creator смог сгенерировать набор импортов в файлах интерфейса, которые он создаст, и сами файлы по умолчанию. Итак, нажимаем "Next". Теперь выбираем настройки сборки (версия библиотеки Qt и Visual Studio). Если вы скачивали только одну версию Qt для какой-то конкретной версии Visual Studio, то у вас будет доступен единственный выбор:

[7]

Нажимаем "Next", затем "Finish", и Qt Creator генерирует тестовый проект для нас. Этот проект будет содержать один простой файл с кодом на C++ и пару файлов на QML:

[8]

Можно собрать и запустить проект, нажав Ctrl+R. Это обычный "Hello, World":

Перейдем к написанию собственного кода. Сделаем, например, нашу собственную кастомную кнопку и расположим несколько таких кнопок с текстом определенным образом в окне приложения. Сейчас проект содержит файл main.qml (он запускается из C++) и MainForm.ui.qml. Файлы *.ui.qml появились в Qt недавно, по идеологии Qt их рекомендуется редактировать в графическом дизайнере интерфейсов (QML Puppet), который вы можете открыть, дважды кликнув на названии файла:

[9]

Более того, в файлах *.ui.qml нельзя использовать JavaScript, исключительно QML. Мне не очень понравилась эта идеология, и, как мне кажется, гораздо удобнее писать интерфейсы вручную без дизайнера (по крайней мере, несложные). Дизайнер интерфейсов может быть полезен только для дизайнеров интерфейсов :D. Поэтому я удаляю этот файл из проекта, нажимая Del и ставя флажок "Delete this file permanently". После этого нужно правой кнопкой мыши кликнуть на названии проекта (mytest в моем случае) и выбрать "Run qmake", иначе проект не соберется. Это нужно иногда делать при добавлении новых файлов в проект или удалении файлов из проекта. (Кстати, если в окне выбора версии Qt Quick при создании проекта выбрать 2.3 или более раннюю, то файл *.ui.qml создаваться не будет). Теперь откроем файл main.qml и напишем туда следующий код:

Этот код содержит пару импортов [10] (непосредственно QtQuick и QtQuick.Window для создания элемента Window [11]). Нажмем Ctrl+R или кнопку "Run" и увидим пустое окно с белым фоном. Что ж, основное окно подготовили, теперь можно приступать к созданию кастомной кнопки. Забегая вперед, скажу, что в Qt Quick есть библиотека Controls [12], которая уже содержит набор готовых контролов (таких как кнопки, поля ввода и т.д.), поддерживающих кастомные стили и стили по умолчанию для разных ОС. Но я покажу, как сделать свой собственный, чтобы вы могли оценить простоту создания новых компонентов. Итак, кликаем правой кнопкой мышки на файле ресурсов [13] qml.qrc, выбираем "Add New...", далее выбираем Qt -> QML File (Qt Quick 2):

[14]

Нажимаем "Choose...", вводим имя файла (например, MyButton), нажимаем "Next". Имя нашего компонента будет совпадать с именем файла (при этом, если первая буква имени файла строчная, например, myControl, то компонент будет всё равно называться с заглавной - MyControl). Далее можно указать, в какой каталог в ресурсах положить этот файл, но здесь ничего менять не будем, нажимаем "Finish". После этого в наш проект добавится файл MyButton.qml и сразу откроется. Мы увидим следующий код:

Здесь, как видно, просто объявляется прямоугольник [15] с заданной шириной и высотой. Версию QtQuick в директиве import можно изменить на более новую (2.3). Давайте теперь сделаем нашу кнопку желтой по умолчанию, со скругленными углами и рамкой, а также уберем фиксированную ширину и высоту, так как они пока что будут задаваться из кода, который будет эту самую кнопку создавать:

Эта кнопка пока не обрабатывает события клика и не умеет отображать текст. Но сейчас мы поместим ее на основную форму, чтобы удобнее было совершенствовать её дальше и отлаживаться. Для этого отредактируем файл main.qml, который до сих пор содержал только пустое окно:

Этот код должен быть достаточно понятен. Если вы решите поподробнее прочитать про какие-то элементы (например, ColumnLayout [16], anchors [17] или прикрепленное свойство Layout [18], то их описание с примерами использования очень легко находится в гугле). После компиляции и запуска мы увидим приложение с тремя кнопками разного цвета:

Наши кнопки пока что не поддерживают отображение текста и не отслеживают события кликов (хотя уже автоматически подстраиваются под размер окна). Нужно это исправить. Начнем с текста. Вернемся к файлу MyButton.qml:

Здесь мы добавили свойство [19] text, пробросив его в дочерний элемент Text [20], а также значение ширины и высоты кнопки по умолчанию. Эти значения будут использоваться, если кнопка не вписана в лейаут (который сам задает размер элемента). Проверим, что у нас получилось, добавив текст к кнопкам в main.qml:

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

Уже лучше! Теперь оживим кнопки, чтобы они реагировали на клик мышкой. Вернемся к файлу MyButton.qml. Сразу после свойства text добавим сигнал [21]:

А для обработки клика мышью существует специальный элемент MouseArea [22]. Добавим его в конец кода нашей кнопки:

Как видно, этот элемент (он невидимый) будет заполнять всю кнопку. При клике на нем мы будем вызывать наш сигнал clicked. Что ж, теперь осталось навесить какой-нибудь обработчик этого сигнала. Сделаем, чтобы при нажатии на кнопку "Hello, world!" открывалось соответствующее окно, а при нажатии на "Quit" приложение закрывалось. Открываем main.qml и редактируем код. Для начала добавим импорт в начало кода:

Это необходимо, чтобы создать диалоговое окно [23]. Теперь добавляем само окно в конец кода Window:

Теперь навесим обработчик на наш сигнал "clicked":

Чтобы добавить обработчик сигнала, следует написать "on" + название сигнала ("clicked") с заглавной буквы. Теперь добавим такой же обработчик для последней кнопки:

Компилируем, запускаем и видим, что всё отлично работает:

Итак, в несколько простых шагов мы написали приложение с собственными кнопками! Конечно, можно их совершенствовать и дальше: добавить навигацию по нажатию TAB [24] и Space/Enter, какую-нибудь анимацию [25] при наведении курсора, комбинации клавиш быстрого доступа, и всё это делается тоже достаточно просто. Однако, с этим я предлагаю вам разобраться самим, а основные принципы работы с Qt Quick и QML я изложил.

Осталось только собрать приложение, чтобы можно было его кому-нибудь передать. Для этого сначала выберем конфигурацию сборки "Release" в Qt Creator:

Затем соберем приложение (Ctrl+B). Теперь откроем каталог, в который Qt Creator собрал программу. У меня это каталог на уровень выше mytest: ../build-mytest-Desktop_Qt_5_4_1_MSVC2013_32bit-Release/release. Здесь мы увидим mytest.exe, но если попробуем запустить, то увидим ошибку (не хватает библиотек). К счастью, под Windows в Qt есть специальная утилита, которая поможет нам с этим. Она называется windeployqt.exe [26] и входит в состав библиотеки Qt. Добавим путь к ней в переменную окружения PATH (если вы еще этого не сделали). У меня она находится в каталоге C:\Qt\Qt5.4.1_angle\5.4\msvc2013\bin, но у вас, вероятно, будет лежать в другом. Далее открываем консоль (cmd), переходим в каталог с файлом mytest.exe и выполняем следующую команду:

После этого в директории с файлом mytest.exe появятся все необходимые библиотеки Qt, от которых зависит программа, включая файлы, относящися к QML. Кроме того, здесь же окажется и файл vcredist_x86.exe - он необходим тем, у кого не установлены C++ Runtime от VC2013. Смотрим, сколько же весит всё это добро... 73 мегабайта! Да, в этом есть определенное неудобство, но с этим ничего не поделаешь. Есть, конечно, подозрение, что windeployqt перестраховывается и кидает больше файлов, чем необходимо приложению для работы, но проверять это у меня особого желания не было. Еще можно собрать Qt с меньшим количеством опций, получив таким образом более компактный набор библиотек. При желании вы сможете это сделать самостоятельно.

Итак, если вы дочитали до этого места статьи, то я вас точно заинтересовал! Поэтому я выкладываю небольшой бонус - это тетрис, написанный мной полностью на QML и JavaScript в процессе изучения Qt Quick. Он определенно имеет не самые качественные исходники, но для ознакомления с некоторыми воможностями Qt Quick вполне подойдет.

Скачать код проекта с кнопками и его собранный вариант [27]
Скачать код тетриса и его собранный вариант [28]