За последнее время было создано огромное количество самых разных библиотек на все случаи жизни. Хочешь воспроизводить музыку с YouTube? Не вопрос! Желаешь отправить запрос и получить ответ от нейронной сети? Да пожалуйста!
Всё это — колоссальное подспорье для любого программиста. Ведь не нужно с нуля писать огромную портянку кода, а можно взять уже готовое решение и подключить его к своему проекту.
Особенно внушительным числом библиотек может похвастаться Python. Но речь пойдет не о нем. В этой статье мы поговорим о создании простого приложения для просмотра текущей погоды. Приложение показывает название населенного пункта, текущую температуру, состояние неба, направление и скорость ветра, видимость и давление.
Для доступа к данным о погоде в приложении используется библиотека gweather. Для определения геолокации была задействована библиотека geoclue.
В качестве языка программирования был выбран Vala. Большинство моих проектов написаны именно на этом языке, и в этом случае я не стал делать исключения. Репозиторий проекта можно найти здесь.
Зачем мне это понадобилось? Подобных приложений великое множество, они куда навороченнее и обладают более внушительным функционалом. Да, но таких приложений, написанных на Vala, совсем немного. Если посмотреть результаты поиска по запросу «weather» на сайте Flathub, то можно заметить, что на Vala создано только одно приложение «Meteo» и написано оно на уже считающимся устаревшем фреймворке Gtk3. Я же буду использовать более новый Gtk4 и libadwaita.
Начало
Для разработки идеально подойдет среда GNOME Builder. Я выбрал встроенный шаблон «Приложение GNOME» с уже включенной поддержкой библиотеки libadwaita. В этом шаблоне интерфейс описывается в файле window.ui, и вручную его редактировать, по моему скромному мнению, — удовольствие ниже среднего. Я решил делать интерфейс для этого проекта с помощью Cambalache.
Помню, в старых версиях Builder имелся встроенный дизайнер на основе Glade, который был довольно удобен. В новых версиях после перехода на Gtk4 дизайнер убрали, Glade практически прекратил развитие, но зато был запущен новый проект под названием Cambalache. Что тут можно сказать? В принципе, пользоваться вполне можно, хотя разработчикам еще есть над чем поработать.
В целом интерфейс у программы довольно простой. В верхней части контейнера Gtk.Box расположилась текстовая метка для отображения названия населенного пункта. Чуть ниже находится контейнер с горизонтальной ориентацией, составленный из виджета Gtk.Image и двух меток для вывода значения температуры и состояния неба. Последние три контейнера имеют одинаковую конструкцию, состоящую из иконки и метки. Вот так это все выглядит:
После того как был создан интерфейс программы, надо не забыть подключить наши библиотеки. Делать это нужно в папке src в файле meson.build:
weather_deps = [
dependency('gtk4'),
dependency('libadwaita-1', version: '>= 1.2'),
dependency('gweather4'),
dependency('libgeoclue-2.0')
]
Для всех своих программ я использую упаковку flatpak. Поэтому понадобится правильно сформированный манифест. В шаблоне проекта уже имеется готовый, но в нем не прописаны модули нужных нам библиотек. Примерно вот так это должно выглядеть:
"modules" : [
{
"name" : "geocode-glib-2.0",
"buildsystem" : "meson",
"config-opts" : [
"-Denable-gtk-doc=false",
"-Denable-installed-tests=false",
"-Dsoup2=false"
],
"sources" : [
{
"url" : "https://download.gnome.org/sources/geocode-glib/3.26/geocode-glib-3.26.4.tar.xz",
"sha256" : "2d9a6826d158470449a173871221596da0f83ebdcff98b90c7049089056a37aa",
"type" : "archive",
"x-checker-data": {
"type": "gnome",
"name": "geocode-glib"
}
}
]
},
{
"name" : "gweather",
"buildsystem" : "meson",
"config-opts" : [
"-Dgtk_doc=false",
"-Dtests=false"
],
"sources" : [
{
"url" : "https://download.gnome.org/sources/libgweather/4.2/libgweather-4.2.0.tar.xz",
"sha256" : "af8a812da0d8976a000e1d62572c256086a817323fbf35b066dbfdd8d2ca6203",
"type" : "archive",
"x-checker-data": {
"type": "gnome",
"name": "libgweather",
"versions": {
"<": "40.0",
">=": "4.0.0"
}
}
}
]
},
{
"name" : "weather",
"builddir" : true,
"buildsystem" : "meson",
"sources" : [
{
"type" : "dir",
"path" : "."
}
]
}
]
Все модули будут загружены с указанных адресов и установлены в согласии с прописанными опциями изолированно от основной системы.
Получение данных
Для начала приложению нужно определить свое местоположение. Об этом расскажу в следующем разделе. Местоположение хранится в переменной location, которая подается на вход конструктору класса Info из GWeather. Дополнительно в качестве свойства ему следует указать контактные данные (можно вымышленные) и, таким образом, мы получаем weather_info:
weather_info = new GWeather.Info (location) {
contact_info = "alex@dev.io"
};
Теперь, применяя к этому объекту различные методы, можно получать самые разнообразные данные. Подробнее можно посмотреть здесь.
Например, для определения состояния неба используется такой код:
weather_label.label = dgettext ("libgweather", weather_info.get_sky ());
Но если требуется выразить полученное значение в определенных единицах измерения, то код немного усложняется. Вот пример для получения скорости и направления ветра:
double speed;
GWeather.WindDirection direction;
weather_info.get_value_wind (GWeather.SpeedUnit.MS, out speed, out direction);
wind_label.label = _("%s, %i m/s").printf (direction.to_string(), (int) speed);
Чтобы узнать название населенного пункта, можно применить такой код:
location_label.label = dgettext ("libgweather-locations", location.get_city_name ());
А для отображения иконки состояния погоды такой:
weather_icon.icon_name = weather_info.get_icon_name ();
Здесь нужно уточнить, что в этом случае можно отображать и символические иконки. Для этого потребуется применить вызов метода get_symbolic_icon_name к объекту weather_info.
Как видно, ничего особо сложного нет. Библиотека довольно лаконичная и позволяет буквально в одну строчку получать необходимые данные.
Геопозиция
Как уже говорилось, местоположение определяется при помощи библиотеки geoclue. В методе get_location происходит вызов метода on_location_updated, на вход которого подаются широта и долгота:
private void get_location () {
get_gclue_simple.begin ((obj, res) => {
var simple = get_gclue_simple.end (res);
if (simple != null) {
simple.notify["location"].connect (() => {
on_location_updated (simple.location.latitude, simple.location.longitude);
});
on_location_updated (simple.location.latitude, simple.location.longitude);
} else {
alert(_("Unable to Get Location"), _("Make sure location access is turned on in settings"));
}
});
}
В случае, если определение местоположения невозможно, например, если доступ к необходимым службам отключен на устройстве, то будет показано соответствующее сообщение. Для показа сообщения используется диалоговое окно из libadwaita:
private void alert (string heading, string body){
var dialog_alert = new Adw.MessageDialog(this, heading, body);
if (body != "") {
dialog_alert.set_body(body);
}
dialog_alert.add_response("ok", _("_OK"));
dialog_alert.set_response_appearance("ok", SUGGESTED);
dialog_alert.response.connect((_) => { dialog_alert.close(); });
dialog_alert.show();
}
Система должна предоставить быстрый способ включения сервисов определения местоположения. То есть, помимо вышеупомянутого диалога, пользователь должен увидеть еще один, из которого можно буквально в одно движение запустить необходимые службы.
Ресурсы и сохранение размеров окна
Для нижних трех меток, в которых отображаются данные о ветре, дальности видимости и давлении, нужны символические иконки. Но не все нужные иконки можно найти в наборе темы Adwaita, поэтому пришлось дополнять ресурсы приложения найденными в сети иконками. Скачанные изображения сохраняются в папке src/icons. В шаблоне проекта изначально присутствует файл gresource.xml, и в нем должны быть прописаны все ресурсы приложения:
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/io/github/alexkdeveloper/weather">
<file preprocess="xml-stripblanks">window.ui</file>
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
<file compressed="true">icons/wind-symbolic.svg</file>
<file compressed="true">icons/open-eye-symbolic.svg</file>
<file compressed="true">icons/pressure-symbolic.svg</file>
</gresource>
</gresources>
Теперь эти имена svg-файлов нужно прописать для соответствующих виджетов в файле интерфейса, и все должно нормально заработать. Больше ничего делать не нужно. Файл ресурсов входит в состав шаблона, а значит, он уже прописан в сборочном сценарии.
Теперь о сохранении размеров окна. Мне вдруг захотелось сделать так, чтобы приложение могло запоминать размер окна и восстанавливать его при каждом новом запуске. Для этого нужен файл gschema.xml. В нем записаны все необходимые ключи и их значения по умолчанию:
<?xml version="1.0" encoding="UTF-8"?>
<schemalist gettext-domain="weather">
<schema id="io.github.alexkdeveloper.weather" path="/io/github/alexkdeveloper/weather/">
<key name="width" type="i">
<default>600</default>
</key>
<key name="height" type="i">
<default>300</default>
</key>
<key name="is-maximized" type="b">
<default>false</default>
</key>
<key name="is-fullscreen" type="b">
<default>false</default>
</key>
</schema>
</schemalist>
В файле window.vala ключи и свойства связываются при помощи метода bind, применяемого для объекта settings класса Settings:
var settings = new Settings ("io.github.alexkdeveloper.weather");
settings.bind ("width", this, "default-width", SettingsBindFlags.DEFAULT);
settings.bind ("height", this, "default-height", SettingsBindFlags.DEFAULT);
settings.bind ("is-maximized", this, "maximized", SettingsBindFlags.DEFAULT);
settings.bind ("is-fullscreen", this, "fullscreened", SettingsBindFlags.DEFAULT);
Значения этих ключей хранятся в специальном файле и каждый раз обновляются. При запуске приложения эти значения считываются и передаются окну в качестве его свойств.
Сборка и установка
Собрать готовый пакет можно прямо в Builder. Для этого в боковой панели имеется специальная вкладка, в которой нужно выбрать самый нижний пункт в списке. Но тут меня ждало разочарование. При попытке сборки пакета среда выдала ошибку:
ошибка: /home/alex/.var/app/org.gnome.Builder/cache/gnome-builder/projects/weather/flatpak/staging/x86_64-main/export/share/icons/hicolor/scalable/apps/io.github.alexkdeveloper.weather.svg is not a valid icon: Icon validation: execvpe bwrap: File name too long
Слишком длинное имя файла? Не может такого быть! Причем эта ошибка возникала для любого проекта. Скорее всего, это баг именно этой версии среды разработки, так как в более старых версиях такого не наблюдалось.
Ну что же, придется использовать flatpak-builder. Ввожу команду для установки пакета в консоли, а мне в ответ от пакетного менеджера прилетает сообщение, что совпадений не найдено. Я на данный момент использую ROSA Fresh 12.4, и по всей видимости, разработчики дистрибутива не посчитали нужным добавлять этот пакет в репозиторий.
В официальной документации сказано, что этот пакет можно установить в виде flatpak. Я так и сделал. Далее в консоли нужно ввести такую команду:
flatpak run org.flatpak.Builder --force-clean --install --user /home/user-name/projects-directory/weather-app /home/user-name/projects-directory/weather/io.github.alexkdeveloper.weather.json
В результате выполнения этой команды приложение должно быть собрано и установлено с пользовательским типом установки. Никаких ошибок в этот раз не возникло.
Приложение получилось простым и со своей задачей прекрасно справляется. Думаю, на этом можно заканчивать. Надеюсь, что статья была для вас интересной и полезной.
Автор статьи @KAlexAl
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
-15% на заказ любого VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.
SchwarzerEngel
И в чем смысл? Заюзал готовые либы, обделил приложение хоть каким то ui эмм и? Смысл в том, чтобы показать, что можно на каком-то языке написать hello world? Не понял.