Часы на кривых Безье для рабочего стола KDE Plasma 5: Bezier Clock

В графическом окружении рабочего стола KDE Plasma 5 для дистрибутивов GNU/Linux появилась замечательная возможность создания анимированных обоев с использованием технологии Qt Quick и декларативного скриптового языка QML. Благодаря переходу KDE Plasma 5 на современный и богатый возможностями фреймворк Qt 5, в новой версии Qt Quick 2.0 анимированные обои стали не только бережнее относиться к ресурсам компьютера, но и получили поддержку множества новых переходов, эффектов, шейдеров и даже системы частиц. Помимо этого увеличился FPS и повысилась плавность самой анимации.



Анимированные обои Bezier Clock на рабочем столе KDE Plasma 5.

Так как я уже достаточно давно являюсь счастливым пользователем KDE, я решил реализовать что-нибудь интересное для этого окружения, такое, чтобы радовался глаз. Создать анимированные обои с красивой анимацией было бы весьма кстати, поскольку стандартные совсем скучные и не демонстрируют мощь и функциональность технологии Qt Quick, а я как раз хотел опробовать язык программирования QML в разработке прикладных приложений.

Несколько лет назад, читая популярный в рунете IT-ресурс Хабрахабр я наткнулся на интересный проект: Часы на кривых Безье, где разработчик Jack Frigaard реализовал с помощью библиотеки для языка программирования JavaScriptProcessing.js анимацию текущего времени. Используя цифры, отрисованные кривыми Безье и наборы простейших интерполяций, разработчику удалось получить очень интересную и занимательную трансформацию одних чисел в другие.



Сайт Jack’а Frigaard’а с часами на кривых Безье.

Мне настолько сильно понравилась эта анимация, что я вспомнил об этом проекте и решил использовать его в качестве базы для своих анимированных обоев в KDE Plasma 5. К моему огромному сожалению, ссылки из поста на Хабрахабре спустя два года стали мёртвыми, но я нашёл на хостинге Github Pages оригинальный сайт автора и его часы: Bézier Clock. На этой странице щедрый Jack Frigaard поделился с общественностью исходным кодом своего проекта, что несказанно облегчило реализацию моей идеи. По сути конечная цель свелась лишь к портированию Processing-кода на стек технологий Qt Quick и QML, а работающий прототип был создан всего за один вечер и две чашки крепкого кофе.

Содержание:

1. Обзор оригинального кода Bézier Clock
2. Особенности создания анимированных обоев в KDE Plasma 5
3. Портирование Bézier Clock на Qt Quick и QML
4. Создание окна настройки параметров Bezier Clock
5. Создание standalone-приложения Bezier Clock
6. Сборка пакетов для GNU/Linux и их установка
7. Заключение, полезные ссылки и ресурсы

1. Обзор оригинального кода Bézier Clock

Как уже было сказано выше, Jack Frigaard для своего проекта использовал библиотеку Processing.js, которая является портом языка визуализации данных Processing на язык программирования JavaScript. Библиотека интерпретирует код программы в файле с расширением *.pde и отображает требуемое изображение на экране. По заявлению разработчика на его официальном сайте, исходный код Bézier Clock получился очень простым и понятным, что на деле полностью соответствует действительности.

В программе содержится всего два класса: BezierDigit и BezierDigitAnimator. Первый класс описывает цифры и кривые Безье из которых они составлены, а второй отвечает за анимацию. В глобальном методе setup() настраивается контекст, создаются десять экземпляров класса BezierDigit, в соответствии с цифрами от нуля до девяти, затем создаются шесть объектов класса BezierDigitAnimator, по одному на каждую отображаемую цифру. Глобальный метод draw() вызывается каждый раз при обновлении кадра, именно он и формирует конечное изображение на экран. В этом методе в переменную d сохраняется текущая дата и время, из которой выделяются часы, минуты и секунды, потом они передаются объектам-аниматорам в их единственный метод update(). В нём происходит определение типа анимации (линейная, квадратичная, кубическая или синусоидальная) в соответствии с выбранными параметрами, а затем вспомогательным методом bezierVertexFromArrayListsRatios() рисуется текущая кривая Безье на экран. Внутри своего тела этот метод активно использует функцию lerp(), которая занимается линейной интерполяцией переданных в неё координат. Опционально в методах update() объектов класса BezierDigitAnimator рисуется ещё одна кривая Безье для тени и несколько прямых и примитивов для отображения контрольных линий.

Для переключения чисел используется глобальный вспомогательный метод getNextInt(), а метод getAnimStartRatio() предназначен для вычисления начала отсчёта анимации при повторном вызове функции draw().

Методы mousePressed() и keyPressed() отвечают за изменение основных параметров приложения во время работы.

Вся красивая анимация этих часов заключается в своевременной интерполяции пяти контрольных точек кривых Безье между заранее заданными значениями.

<< Перейти к содержанию

2. Особенности создания анимированных обоев в KDE Plasma 5

Для того, чтобы приложение появилось в списке обоев в настройках рабочего стола KDE Plasma 5, каталог проекта нужно поместить в пользовательскую директорию ~/.local/share/plasma/wallpapers/ или системный каталог /usr/share/plasma/wallpapers/. Название директории, в которой находится обоина, должно быть задано в Java-стиле вида domain.organization.name, например, у меня это ru.exlmoto.bezierclock.

Стандартные анимированные обои KDE Plasma 5 доступны в системном каталоге /usr/share/plasma/wallpapers/. Поскольку QML является интерпретируемым языком, их код доступен в открытом и читаемом виде. Структура директории с обоиной довольно простая и выглядит следующим образом:

В директории contents/config/ находится конфигурационный файл main.xml с дефолтными значениями параметров. В директории contents/ui/ располагаются скрипты на языке программирования QML, из которых config.qml отвечает за содержимое виджета, который будет отображён в настройках рабочего стола при выборе соответствующей обоины, а в main.qml находится точка входа в приложение и, соответственно, все инструкции, которые определяют изображение и анимацию. В файлах с расширением *.desktop содержится различная мета-информация пакета с обоиной.



Структуры директорий с анимированными обоями в системном каталоге KDE Plasma 5, на скриншоте файловый менеджер KDE — Dolphin.

Мой файл с мета-информацией metadata.desktop для Bezier Clock выглядит вот так:

Прочитать настройки из конфигурационного файла достаточно легко, для этого в KDE Plasma 5 имеются специальные системные Qt Quick-пакеты с необходимым набором API.

Например, в конфиге main.xml определён параметр Color типа Color. Тип параметра может быть любым: String, Int, Bool и другие популярные типы.

Для того, чтобы получить значение параметра из QML, нужно импортировать системный пакет org.kde.plasma.core и использовать элемент wallpaper.configuration для получения необходимых данных:

Чтобы записать изменяемое значение в конфиг, нужно тоже импортировать пакет org.kde.plasma.core и прокинуть псевдоним на требуемое свойство со специальным префиксом вида cfg_OptionName:

Тогда при изменении параметра, он будет автоматически сохранён в пользовательскую кофигурацию.

Следует помнить, что базовые элементы файлов config.qml и main.qml в качестве id должны иметь root, чтобы KDE Plasma 5 мог с ними работать, например, растягивать их контекст на весь экран.

<< Перейти к содержанию

3. Портирование Bézier Clock на Qt Quick и QML

Особенностью языка программирования QML является то, что он был создан декларативным. То есть вместо стандартного императивного подхода, который описывает как нужно решить задачу, в декларативном QML нужно описать что представляет из себя задача и её ожидаемый результат. Главный файл QML-приложения (в моём случае main.qml), определяет взаимодействия и всю иерархию элементов, дерево которых строится при загрузке QML-файла. Для каждого элемента определяется набор его типизированных свойств, в качестве значений которых могут быть использованы не только константы, но и выражения языка программирования JavaScript. Программы на QML могут использовать вспомогательные функции из внешних JavaScript-файлов или библиотек. Если в ООП базовым элементом является класс, по которому строится объект, то в QML базовым элементом будет сам элемент, являющийся строительным блоком QML-программы. Элементы бывают графическими и поведенческими, они объединяются вместе в QML-файлах для построения сложных компонентов. На этом заканчивается мой краткий экскурс в особенности языка программирования QML.



GIF-анимация, демонстрирующая работу Bezier Clock на KDE Plasma 5.

Как было сказано выше, в QML отсутствуют классы, но есть элементы, поэтому я начал портирование с преобразования классов BezierDigit и BezierDigitAnimator в компоненты BezierDigit.qml и BezierAnimator.qml соответственно. В качестве их базового элемента я выбрал компонент Item. QML-элементы внутри можно расширять методами на JavaScript, чем я воспользовался и портировал в них код с Processing. Для файла BezierDigit.qml получилось следующее:

Инициализацию этих цифр кривыми Безье я поручил элементу BezierDigits.qml, вот его код:

К сожалению, в официальной документации сказано, что порядок вызова кода у компонентов в свойствах Component.onCompleted не определён. Поэтому я не решился сделать массив variant, содержащий элементы BezierDigit и стал использовать несколько громоздкий прямой доступ к каждому элементу. Если у меня появится свободное время, я уточню этот момент у знакомых Qt Quick/QML-программистов.

Выше я отметил, что запуск анимированной обоины начинается с файла main.qml, он получился у меня достаточно компактным:

Элемент BezierSettings отвечает за чтение настроек из конфига в свои свойства, это сделано специально для разделения кода анимированной обоины для KDE Plasma 5 и standalone-приложения на Qt Quick/QML, о чём я упомяну позже.

Компонент BezierSetup забирает из свойств BezierSettings необходимые опции и создаёт элемент BezierInit:

BezierInit в своём теле инициализирует элементы-аниматоры и компонент BezierDigits, который обобщает все цифры:

Далее в main.qml создаётся главный элемент анимированных обоев — BezierClock, который содержит в себе элемент канваса для отрисовки происходящего на экране, компонент, отвечающий за вывод оверлея счётчика FPS и таймер, в котором происходит обновление канваса в соответствии со значением FPS:

Для различных оверлеев я создал базовый элемент UiBaseOverlay, с таким содержанием:

От него я и унаследовал UiFpsOverlay, который содержит ещё один таймер, тикающий раз в секунду:

Оба таймера, главный и предназначенный для FPS, запускаются в свойстве Component.onCompleted элемента BezierClock. Это свойство передаёт своё управление JavaScript-коду тогда, когда компонент загрузился и выстроено дерево его элементов. Константы 2300 и 600 являются оригинальным размером контекста часов без какого-либо масштабирования.

В элементе BezierCanvas определён метод render(), являющийся аналогом метода draw() в Processing. Этот метод дёргает функции update() элементов-аниматоров и рисует итоговый кадр на канвас.

Канвас в QML имеет множество методов, аналогичных канвасу в HTML5, а потому портирование различных приложений с QML на HTML5 и обратно не представляет особой сложности.

Некоторые компоненты я расширил JavaScript-файлами JsCanvasFunctions.js и JsCoreFunctions.js. Первый является компонентно-зависимым, а потому в его функциях можно получать доступ к свойствам QML-элемента. Второй, напротив, является библиотекой, и его можно использовать в различных QML-элементах. Для определения библиотеки имеется специальная директива .pragma library, которую необходимо определить в самом начале файла:

В JsCoreFunctions.js я реализовал функции, доступные в Processing, но отсутствующие в JavaScript, такие, как sq() и lerp().

Мои эксперименты показали, что отрисовка канваса размером на весь экран требует значительных ресурсов CPU, поэтому я придумал небольшую хитрость: я рисую часы не в полный экран, а в небольшой прямоугольник, который находится в центре другого статичного полноэкранного прямоугольника. Поскольку цвета у прямоугольников одинаковые, этого не заметно, но зато FPS значительно повысился и перестал проседать.



Зелёный прямоугольник статичен, а на красный рендерится изображение Bezier Clock.

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

<< Перейти к содержанию

4. Создание окна настройки параметров Bezier Clock

Как было отмечено выше, в настройках рабочего стола при выборе необходимой анимированной обоины отображается форма из файла config.qml, внутри которого описаны все необходимые элементы управления. Я решил сделать Bezier Clock максимально настраиваемым и определил более двадцати параметров, которые можно изменить в соответствии со своим вкусом и предпочтениями. Само собой, такое количество опций сильно раздувает окно настройки, но писать интерфейсы на языке программирования QML — одно удовольствие! Кажется, что это идеальный язык для такого рода задач.



Окно настройки параметров Bezier Clock в KDE Plasma 5.

Нужно определить колонки со строками и просто заполнить их компонентами в соответствии с необходимой иерархией. Элементы можно кастомизировать, совмещать и наследовать, в сочетании с мощью QML это даёт огромный простор для полёта фантазии. Например, для создания компонента выбора цвета UiColorBox, который отображает внутри себя кнопку с цветным прямоугольником и строку справа, требуется следующий код:

Похожим образом реализованы элементы UiComboBox и UiSpinBox.

Настройки сохраняются в пользовательской конфигурации посредством проброшенных псевдонимов на свойства с префиксом cfg_OptionName, KDE Plasma 5 при изменении настроек на форме тут же перезаписывает их в хранилище и делает доступной кнопку Apply. Часть файла config.qml, демонстрирующая чтение настроек и то, насколько просто описывается отображаемый интерфейс:

Поскольку вариантов конфигурирования Bezier Clock получилось огромное количество, пользователь может что-то сломать и забыть то, как вернуть всё в первоначальное состояние. Для этой цели я сделал кнопку Reset to Default, а функцию сброса настроек на те, что были по умолчанию, вынес в отдельный файл JsConfigUiHelper.js:

В качестве небольшого украшения я добавил на форму собственноручно созданную иконку и информацию о создателях.

<< Перейти к содержанию

5. Создание standalone-приложения Bezier Clock

Программы, созданные с помощью технологии Qt Quick и языка программирования QML, легко переносимы на другие системы. Созданную анимированную обоину можно сделать обычным прикладным приложением и развернуть окно на весь экран там, где нет KDE Plasma 5, но есть Qt 5, например, на MS Windows или macOS. Тот код, который используется только для standalone-приложения, я вынес в отдельный каталог qml/NonKDE/ и использовал разделение. К примеру, отдельной программой используются файлы с постфиксом Qt: BezierSettingsQt.qml и mainQt.qml, а анимированная обоина для KDE Plasma 5 использует BezierSettings.qml и main.qml. Получается, что в случае standalone-приложения настройки в общий элемент BezierSetup загружаются компонентом BezierSettingsQt, а в случае анимированной обоины этой работой занимается BezierSettings. Таким образом можно легко разделять платформозависимый код.



Standalone-приложение Bezier Clock, запущенное на операционной системе MS Windows 10, на скриншоте показан исчезающий оверлей со справкой (превью, увеличение по клику).

Окно с настройкой параметров в standalone-приложении я решил не делать и вместо этого перенес изменение опций на клавиши клавиатуры и кнопки мышки. Для того, чтобы пользователь имел представление о том, каким образом изменился тот или иной параметр, я добавил простейшую систему полупрозрачных исчезающих оверлеев. Их смысл таков: пользователь нажимает на кнопку и видит сообщение о том, какой параметр изменился и как он изменился. Кроме того, с помощью такого же метода я организовал справку по клавишам, которые обрабатывает приложение. Для создания оверлея я просто унаследовался от общего базового элемента UiBaseOverlay и создал компонент UiOptionsOverlay, в котором создал поведенческий элемент NumberAnimation регулирующий свойство opacity и отвечающий за полное исчезновение оверлея с экрана в течении необходимого времени:

Файл mainQt.qml, отвечающий за главное окно программы, выглядит следующим образом:

В элементе MouseArea происходит обработка нажатий на кнопки мышки, а в свойстве Keys.onPressed — обработка клавиш клавиатуры. Различные функции я выделил во вспомогательный JavaScript-файл JsMainUiHelper.js, например, в нём содержатся методы обработки событий, отображения оверлеев overlayOptions и overlayHelp, а также функции генерации случайных цветов. Файл получился весьма громоздким, вот его небольшая часть кода:

Настройки приложения определены в BezierSettingsQt, и по сравнению с BezierSettings расширены опцией, отвечающей за работу окна в полноэкранном режиме. В Qt Quick существует специальный пакет Qt.labs.settings для работы с настройками. Элемент settings обеспечивает кроссплатформенное сохранение и чтение необходимых параметров из внутреннего хранилища системы. Настройки читаются при каждом запуске приложения и сохраняются после выхода из приложения свойством Component.onDestruction корневого элемента mainQt.qml.

В standalone-приложении необходимый исходный код на языках QML и JavaScript вкомпиливается прямо в исполнительный бинарник. Работу по упаковке исходного кода выполняет специальный компилятор ресурсов rcc, который получает список необходимых компонентов из файла qml.qrc. Главный компилируемый файл main.cpp на языке программирования C++ выглядит следующим образом:

Собрать и запустить standalone-приложение достаточно просто. Необходимо перейти в каталог с исходным кодом и выполнить следующие команды в терминале:

Таким образом развернуть Bezier Clock можно на любых системах, для которых доступен Qt 5.

<< Перейти к содержанию

6. Сборка пакетов для GNU/Linux и их установка

Для удобной установки Bezier Clock в дистрибутивы GNU/Linux, я решил создать два варианта пакетов: первый подходит для любого дистрибутива и уставливает обоину в пользовательскую директорию ~/.local/share/plasma/wallpapers/, а второй является установочным пакетом для дистрибутива Arch Linux и уставливает файлы в системный каталог /usr/share/plasma/wallpapers/.

Первый вариант пакета представляет собой обычный архив формата TAR.XZ и установочный скрипт package.sh, выполнение которого развернёт пакет в пользовательскую директорию. Он выглядит следующим образом:

Скрипт, будучи запущенный с опцией -u, удаляет файлы Bezier Clock из пользовательской директории. В теории он должен работать на любых дистрибутивах GNU/Linux с KDE Plasma 5. Помимо опций установки и удаления этот скрипт как раз и собирает пакет bezier-clock-v1.0.tar.xz, если его запустить в каталоге исходного кода util/ с опцией сборки пакета -p.

Поскольку сейчас я использую замечательный дистрибутив GNU/Linux под названием Arch Linux, я решил попрактиковаться в создании пакетов и собрать Bezier Clock именно для этого дистрибутива. Для этого в Arch Linux нужно установить некоторые зависимости и систему сборки absArch Build System, после чего нужно выполнить синхронизацию дерева abs с сервером и можно приступать к созданию пакета.

Чтобы в строке Packager при выполнении команды запроса информации о пакете:

Отображалось ваше имя и почта, необходимо задать эти данные в файле /etc/makepkg.conf в переменной PACKAGER раздела PACKAGE OUTPUT.

Для создания пакета потребуется написать лишь два файла: рецепт любой системы сборки и сборочный скрипт PKGBUILD. В качестве сборочной системы я выбрал CMake, так как именно её использует KDE Plasma 5 и в ней имеются все необходимые скрипты и переменные для этого окружения. Пример рецепта CMake для установки своих элементов (обоев, виджетов, апплетов) в KDE Plasma 5 опубликован в официальной документации. Я его немного отредактировал и получилось следующее:

Сборочный скрипт PKGBUILD имеет специальные функции prepare(), build() и package(). Первая готовит окружение для сборки, вторая выполняет саму сборку, а третья делает пакет. Мой PKGBUILD выглядит следующим образом:

Для сборки пакета достаточно выполнить команду:

В той директории, где находится PKGBUILD. Умный abs сам скачает исходный код, проверит его хеш-сумму, разархивирует, соберёт и создаст пакет bezier-clock-v1.0-1-any.pkg.tar.xz, который будет удобно устанавливать в систему при помощи пакетного менеджера pacman или его фронтеда yaourt:

За удаление пакета из системы тоже будет отвечать пакетный менеджер.

Благодаря системе сборки CMake можно легко собрать пакеты анимированных обоев Bezier Clock и для других дистрибутивов GNU/Linux.

Кроме того, в KDE Plasma 5 Workspace имеется специальный встроенный пакетный менеджер, который называется Plasma Package Manager. Можно установить пакет bezier-clock-v1.0.tar.xz, инструкцию по сборке которого я написал чуть выше, с его помощью следующим образом:

После чего пакет установится аналогино первому способу. Удалить его из системы тоже можно через Plasma Package Manager:

Эта утилита позволяет удобно управлять различными расширениями KDE Plasma 5.

<< Перейти к содержанию

7. Заключение, полезные ссылки и ресурсы

Создание анимированных обоев Bezier Clock для KDE Plasma 5 позволило мне разобраться в написании приложений с использованием технологии Qt Quick на языке программирования QML, узнать о таком языке визуализации данных, как Processing и успешно попрактиковаться в создании установочных пакетов для Arch Linux. Этот процесс дал мне огромное количество ценного опыта, я смог понять механизм работы анимированных обоев в KDE Plasma 5 и разобраться во внутренней кухне сборки пакетов для своего дистрибутива GNU/Linux.



Bezier Clock в качестве анимированных обоев окружения KDE Plasma 5, демонстрация на видеохостинге YouTube.

Размер установочных TAR.XZ-пакетов получился совсем крошечным, всего 15 КБ. Это связано с тем, что QML является интерпретируемым языком, а интерпретатор уже установлен в систему.

Скачать TAR.XZ-пакеты Bezier Clock, готовые для установки в дистрибутивы GNU/Linux, можно по этим ссылкам:

[Скачать Plasma TAR.XZ-пакет Bezier Clock, 13 КБ | Download Plasma Bezier Clock 21 TAR.XZ-package, 13 КB]
[Скачать All TAR.XZ-пакет Bezier Clock, 15 КБ | Download All Bezier Clock 21 TAR.XZ-package, 15 КB]
[Скачать Arch Linux TAR.XZ-пакет Bezier Clock, 15 КБ | Download Arch Linux Bezier Clock 21 TAR.XZ-package, 15 КB]

К сожалению, в последних версиях KDE Plasma 5.7.x внесли баг, из-за которого не читаются и не сохраняются настройки анимированных обоев. Поэтому после установки и активации Bezier Clock можно увидеть чёрный экран. Для исправления этой ошибки нужно просто нажать кнопку Reset to Default, а потом Apply. Этот баг зарепорчен и можно наблюдать за исправлением ситуации здесь и здесь.

Все исходные коды и проект в целом выложен в репозиторий на ресурсе Github. Мои изменения и исходные файлы доступны под лицензией MIT. Ссылка на репозиторий:

https://github.com/EXL/BezierClock

В этой работе я использовал огромное количество материалов, основные из них я выделю в полезных ссылках ниже. Огромное спасибо ресурсам stackoverflow.com и google.com за то, что они есть.

  1. Сайт с часами на кривых Безье от Jack’а Frigaard’а;
  2. Исходный код часов на кривых Безье от Jack’а Frigaard’а;
  3. Документация библиотеки Processing.js;
  4. Замечательная книга QML Book, посвящённая разработке приложений на Qt Quick/QML, седьмая глава книги посвящена портированию HTML5-приложений на QML;
  5. Официальная документация по Qt Quick и QML;
  6. Мануал по созданию своих элементов (обоев, виджетов, апплетов) для KDE Plasma 5;
  7. Статья на ArchWiki, подробно рассматривающая рецепты создания пакетов для Arch Linux.

Update 06-MAR-2017: Прочитать о твике Bezier Clock для KDE Plasma 5, который добавляет возможность установки собственного фонового изображения, можно в разделе этой статьи.

<< Перейти к содержанию

Dev, Manuals, Others

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *