Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Возможности тулкита GTK+ и сопутствующих библиотек

Автор: A. Панин
Дата публикации: 10 июня 2015 г.

Механизм загрузки ресурсов приложений

Дополнительные файлы ресурсов используются как приложениями с графическим интерфейсом, так и приложениями с интерфейсом командной строки. Примерами файлов ресурсов, используемых приложениями с графическим интерфейсом на основе тулкита GTK+, являются файлы изображений, звуковые файлы, файлы описания графического интерфейса и действий, файлы каскадных таблиц стилей с описаниями стилей элементов графического интерфейса, файлы с необходимыми для работы приложения данными и.т.д. Приложениям с интерфейсом командной строки для корректной работы также могут понадобиться файлы с данными. При разработке приложений, использующих файлы ресурсов, чаще всего либо предусматривают копирование этих файлов в директорию приложения, созданную в системной директории для хранения данных приложений (обычно /usr/share/), либо преобразовывают содержимое этих файлов в байтовые массивы, которые впоследствии размещаются в подключаемом к исходному коду приложения заголовочном файле. В последнем случае во-первых ускоряется загрузка приложения, а во-вторых снижается сложность кода, ведь в случае работы с байтовыми массивами больше не приходится обрабатывать ситуации с недоступностью файлов. В данной статье рассматривается третий вариант, а именно, механизм загрузки ресурсов приложений, реализованный в рамках библиотеки GIO.

1. Утилита для обработки ресурсов и файл описания ресурсов

Перед осуществлением доступа к данным ресурсов на уровне приложения необходимо произвести преобразование этих файлов в байтовый массив. Для выполнения этого действия должна использоваться утилита glib-compile-resources из комплекта поставки библиотеки GLib. В качестве исходных данных утилита принимает специально подготовленный файл формата XML со списком файлов ресурсов и описанием методов предварительной обработки/преобразования каждого из них. Файл должен иметь расширение .gresource.xml. Пример рассматриваемого файла приведен в Листинге 1.

Листинг 1 - Файл описания файлов ресурсов приложения

<?xml version="1.0" encoding="UTF-8"?>
<gresources>
  <gresource prefix="/org/gtk/GWhois">
    <file preprocess="to-pixdata">alnilam-Stars-Pattern.png</file>
    <file preprocess="xml-stripblanks">whois-server-list.xml</file>
    <file compressed="true">gwhois.css</file>
  </gresource>
</gresources>

Как вы наверняка поняли, в статье будет описываться процесс преобразования приложения gwhois из прошлой статьи серии, который вполне обоснован, так как данное приложение использует файл формата XML, содержащий информацию о серверах whois, с именем whois-server-list.xml. Кроме того, для демонстрации методики загрузки графических файлов и файлов каскадных таблиц стилей в файле описания ресурсов приложения приведены имена файлов alnilam-Stars-Pattern.png и gwhois.css. При преобразовании файлов ресурсов в байтовый массив каждый из этих файлов может быть сжат с помощью библиотеки ZLib в случае использования параметра "compressed" с значением "true" (очевидно, что в данном случае осуществляется сжатие файла каскадной таблицы стилей с именем gwhois.css). При доступе к сжатому файлу ресурса в процессе работы приложения будет автоматически осуществляться его декомпрессия. Перед преобразованием из файлов формата XML с помощью утилиты xmllint могут быть убраны лишние пробелы в случае использования параметра предварительной обработки "preprocess" с значением "xml-stripblanks" (в данном случае лишние пробелы удаляются из файла whois-server-list.xml). Кроме того, файлы изображений могут преобразовываться во внутренний формат хранения графических данных GDK с помощью утилиты gdk-pixbuf-pixdata в случае использовании параметра предварительной обработки "preprocess" с значением "to-pixdata" (в данном случае описанным образом преобразуется файл изображения alnilam-Stars-Pattern.png). Также следует обратить внимание на значение параметра "prefix", ведь при доступе к данным ресурсов на уровне приложения префикс должен записываться непосредственно перед именем каждого из файлов ресурсов.

Утилита glib-compile-resources может генерировать как бинарный файл, который впоследствии может загружаться с помощью специальных функций библиотеки GIO, так и набор из файла исходного кода языка программирования C с объявлением байтового массива с данными ресурсов и вызовами функций для инициализации и регистрации ресурсов в рамках приложения, а также соответствующего заголовочного файла для подключения к исходному коду приложения, разработанного с использованием языка программирования C. Для генерации бинарного файла следует использовать следующую команду:

В качестве имени бинарного файла ресурсов, имени идентификатора языка C и имени файла описания ресурсов чаще всего используется имя приложения, поэтому в случае генерации бинарного файла ресурсов для приложения gwhois команда будет выглядеть следующим образом:

glib-compile-resources --target=gwhois.resources --sourcedir=. --generate --c-name gwhois gwhois.gresource.xml

В результате в рабочей директории будет создан бинарный файл с именем gwhois.resources. Для генерации набора из файла исходного кода языка C и заголовочного файла следует использовать последовательность из двух команд, заменив параметр --generate на параметры --generate-source и --generate-header, а также указав имена файлов с соответствующими расширениями. В случае генерации описанного набора файлов для приложения gwhois последовательность команд будет выглядеть следующим образом:

glib-compile-resources --target=gwhois-resources.c --sourcedir=. --generate-source --c-name gwhois gwhois.gresource.xml
glib-compile-resources --target=gwhois-resources.h --sourcedir=. --generate-header --c-name gwhois gwhois.gresource.xml

В результате в рабочей директории будут созданы файлы исходного кода gwhois-resources.c и gwhois-resources.h, причем последний файл предназначен для подключения к исходному коду приложения.

2. Класс набора ресурсов GResource и глобальное пространство ресурсов приложения

Ранее упоминалось, что данные файлов ресурсов могут быть размещены как в бинарном файле, так и в файле исходного кода языка программирования C. В случае использования бинарного файла (например, если используется отличный от C язык программирования) для работы с ресурсами должен создаваться объект набора ресурсов типа GResource, который впоследствии может быть зарегистрирован в глобальном пространстве ресурсов приложения. Для загрузки бинарного файла набора ресурсов может использоваться конструктор g_resource_load(), а также конструктор g_resource_new_from_data() в том случае, если уже имеется доступ к данным этого файла. С помощью обоих конструкторов создаются объекты наборов ресурсов типа GResource, которые могут либо использоваться для доступа к ресурсам приложения, либо регистрироваться в глобальном пространстве ресурсов приложения, что позволит отказаться от передачи указателя на упомянутый объект во все функции приложения, в которых осуществляется доступ к ресурсам.

GResource *
g_resource_load(const gchar *filename,
                GError **error);
GResource *
g_resource_new_from_data(GBytes *data,
                         GError **error);

Регистрация объекта набора ресурсов типа GResource в глобальном пространстве ресурсов приложения осуществляется с помощью функции g_resources_register(), а отмена регистрации - с помощью функции g_resources_unregister(). В случае подключения к исходному коду приложения файлов исходного кода с данными ресурсов будет осуществляться автоматическая регистрация в глобальном пространстве ресурсов.

void
g_resources_register(GResource *resource);
void
g_resources_unregister(GResource *resource);

Каждый из объектов наборов ресурсов типа GResource реализует механизм подсчета ссылок. Создание ссылки на объект осуществляется с помощью метода g_resource_ref(), а удаление - с помощью метода g_resource_unref(). При равенстве счетчика ссылок объекта нулю происходит его уничтожение с освобождением всей зарезервированной памяти. Оба метода являются потокобезопасными (могут вызываться из любого программного потока).

GResource *
g_resource_ref(GResource *resource);
void
g_resource_unref(GResource *resource);

Доступ к данным ресурса может осуществляться как с помощью метода g_resource_lookup_data() в том случае, если объект набора ресурсов не зарегистрирован в глобальном пространстве ресурсов приложения, так и с помощью функции g_resources_lookup_data() в противном случае.

GBytes *
g_resource_lookup_data(GResource *resource,
                       const char *path,
                       GResourceLookupFlags lookup_flags,
                       GError **error);
GBytes *
g_resources_lookup_data(const char *path,
                        GResourceLookupFlags lookup_flags,
                        GError **error);

С помощью аргумента path передается имя файла из набора ресурсов приложения с префиксом (к примеру, в случае использования приведенного выше файла описания ресурсов для доступа к данным файла формата XML со списком серверов whois следует передать строку "/org/gtk/GWhois/whois-server-list.xml"). С помощью аргумента lookup_flags задаются флаги поиска данных ресурсов, причем на данный момент объявлена лишь константа G_RESOURCE_LOOKUP_FLAGS_NONE, обозначающая отсутствие флагов. В случае обнаружения данных ресурса будет возвращен объект ассоциированного байтового массива, в противном случае - значение NULL и объект ошибки с помощью аргумента error. Если же необходимо осуществлять последовательное чтение данных ресурса, следует использовать метод g_resource_open_stream() и функцию g_resources_open_stream().

GInputStream *
g_resource_open_stream(GResource *resource,
                       const char *path,
                       GResourceLookupFlags lookup_flags,
                       GError **error);
GInputStream *
g_resources_open_stream(const char *path,
                        GResourceLookupFlags lookup_flags,
                        GError **error);

Для получения списка доступных ресурсов следует использовать метод g_resource_enumerate_children() и функцию g_resources_enumerate_children().

char **
g_resource_enumerate_children(GResource *resource,
                              const char *path,
                              GResourceLookupFlags lookup_flags,
                              GError **error);
char **
g_resources_enumerate_children(const char *path,
                               GResourceLookupFlags lookup_flags,
                               GError **error);

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

Для получения информации о ресурсе следует использовать метод g_resource_get_info() и функцию g_resources_get_info().

gboolean
g_resource_get_info(GResource *resource,
                    const char *path,
                    GResourceLookupFlags lookup_flags,
                    gsize *size,
                    guint32 *flags,
                    GError **error);
gboolean
g_resources_get_info(const char *path,
                     GResourceLookupFlags lookup_flags,
                     gsize *size,
                     guint32 *flags,
                     GError **error);

С помощью аргумента path следует передавать имя файла из набора ресурсов приложения с префиксом. Аргументы size и flags служат для получения информации о размере файла в байтах и флагах (атрибутах) этого файла соответственно (на данный момент единственным флагом является константа G_RESOURCE_FLAGS_COMPRESSED, указывающая на то, что файл был сжат).

Например, для получения информации о доступных приложению ресурсах может быть использован код из Листинга 2.

Листинг 2 - Пример использования функций g_resources_enumerate_children() и g_resources_get_info()

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

whois-server-list.xml, 583481 байт(а)
alnilam-Stars-Pattern.png, 160024 байт(а)
gwhois.css, 182 байт(а), сжат

В случае регистрации объекта набора ресурсов в глобальном пространстве ресурсов приложения могут использоваться вспомогательные методы, значительно упрощающие использование данных ресурсов приложения. Примерами таких методов являются:

  1. Метод gdk_pixbuf_new_from_resource(), предназначенный для создания объекта буфера пикселей изображения типа GdkPixbuf на основе данных ресурса.

  2. Метод gdk_pixbuf_new_from_resource_at_scale(), предназначенный для создания объекта буфера пикселей изображения типа GdkPixbuf на основе данных ресурса с автоматическим масштабированием и возможностью сохранения соотношения длин сторон.

  3. Метод gdk_pixbuf_animation_new_from_resource(), предназначенный для создания объекта буфера пикселей анимированного изображения типа GdkPixbufAnimation на основе данных ресурса.

  4. Метод gtk_builder_new_from_resource(), являющийся конструктором объекта системы разбора описания графического интерфейса типа GtkBuilder и выполняющий загрузку и обработку данных описания графического интерфейса приложения из ресурса.

  5. Метод gtk_builder_add_from_resource(), предназначенный для разбора представленного ресурсом описания графического интерфейса приложения и добавления созданных на его основе объектов к уже созданным в рамках существующего объекта разбора описания графического интерфейса типа GtkBuilder объектам.

  6. Метод gtk_builder_add_objects_from_resource(), предназначенный для разбора представленного ресурсом описания графического интерфейса приложения и добавления созданных на его основе объектов для виджетов с указанными именами к уже созданным в рамках существующего объекта разбора описания графического интерфейса типа GtkBuilder объектам.

  7. Метод gtk_css_provider_load_from_resource(), предназначенный для загрузки каскадной таблицы стилей из ресурса (введен в GTK+ 3.16).

GdkPixbuf *
gdk_pixbuf_new_from_resource(const char *resource_path,
                             GError **error);
GdkPixbuf *
gdk_pixbuf_new_from_resource_at_scale(const char *resource_path,
                                      int width,
                                      int height,
                                      gboolean preserve_aspect_ratio,
                                      GError **error);
GdkPixbufAnimation *
gdk_pixbuf_animation_new_from_resource(const char *resource_path,
                                       GError **error);
GtkBuilder *
gtk_builder_new_from_resource(const gchar *resource_path);
guint
gtk_builder_add_from_resource(GtkBuilder *builder,
                              const gchar *resource_path,
                              GError **error);
guint
gtk_builder_add_objects_from_resource(GtkBuilder *builder,
                                      const gchar *resource_path,
                                      gchar **object_ids,
                                      GError **error);
gboolean
gtk_css_provider_load_from_resource(GtkCssProvider  *css_provider,
                                    const gchar     *resource_path,
                                    GError         **error);

Помимо использования описанных выше методов, на ресурсы можно ссылаться из описаний графических интерфейсов приложений и каскадных таблиц стилей. В случае описания графического интерфейса приложения ссылка на ресурс осуществляется с помощью описания свойства: <property name="resource">/путь/к/ресурсу</property>. В случае каскадной таблицы стилей используется следующий формат: url('resource:///путь/к/ресурсу').


Продолжение статьи : 3. Пример использования ресурсов в приложении.