Вход:  Пароль:  
FreeSource: devel/GtkGnomeGlade?/gladewidget ...
Free Source | Каталог | Изменения | НовыеКомментарии | Пользователи | Регистрация |

Заметки о встраивании сторонних виджетов в Glade


Будем считать, что у нас есть виджет My Widget? из набора виджетов My, и мы хотим добавить его поддержку в glade и libglade.

0. Требования к библиотеке виджетов


Зачастую программисты, пишущие расширения для Gtk+ не утруждают себя следованием всем канонам описания виджетов в gtk. В результате получается, что виджеты правильно работают, если их использовать в коде, написанном руками, но в glade с ними возникают проблемы. Главным образом это касается случая, когда у виджета есть набор свойств (properties). Для glade недостаточно, если библиотека будет содержать функции my_widget_set_property(widget, property) и my_widget_get_property(widget), а в самой структуре виджета будет содержаться поле property. В этом случае он просто не увидит такого свойства. Нужно, во-первых, чтобы при инициализации класса виджета (функция my_widget_class_init) свойства были соответствующим образом зарегистрированы средствами glib (функция g_object_class_install_property). Помимо этого должны быть определены методы gobject_class->set_property и gobject_class->get_property. Glade будет пользоваться как раз ими.


Ещё один момент относится к выбору родительского класса. Gtk+ позволяет в этом отношении большие вольности, например, можно в качестве родительского класса взять Gtk Vbox? и создать на его основе виджет, не являющийся контейнером. В Glade такое не пройдёт, т.к. он проверяет тип родительского класса, и в данном примере будет считать виджет контейнером со всеми вытекающими из этого последствиями. Точнее, похоже, что так делать всё-таки можно (например, Gtk Color Selection? является наследником как раз Gtk Vbox?), но нужно соблюсти ряд формальностей: построение содержимого виджета следует делать внутри функции my_widget_init (а не my_widget_new), и всю процедуру создания содержимого следует разместить между вызовами функций gtk_widget_push_composite_child и gtk_widget_pop_composite_child.


В Glade и libglade загрузка модулей расширения происходит по-разному, несмотря на то, что они имеют дело с одним и тем же описанием интерфейса в xml. Поэтому нужно осуществлять поддержку сторонних виджетов отдельно для glade и libglade.

1. Glade


Для того, чтобы glade увидел наш виджет и знал, как с ним обращаться, требуются следующие файлы:
1. Так называемый файл каталога. Это файл в формате xml, откуда glade узнаёт, как ему работать с нашим(и) виджет(ами). Называется он произвольно, но лучше, наверное, чтобы его название указывало на набор виджетов. Т.е. в нашем случае это будет my.xml Этот файл кладётся в каталог /usr/share/glade3/catalogs Его содержимое примерно таково:



Во второй строке (тэг <glade-catalog>)определяются глобальные параметры:


Есть ещё атрибуты “version” и “targeable”, но смысл их применения мне не ясен. Из всего перечисленного обязательными являются только атрибуты “name” и “library”.


Далее, внутри пары тэгов <glade-widget-classes></glade-widget-classes>, идут описания самих классов виджетов. Описание каждого класса виджета заключено внутри пары тэгов <glade-widget-class></glade-widget-class>. У тега <glade-widget-class> имеются три обязательных атрибута:


Остальные свойства являются необязательными:


Есть ещё парочка свойств “since”, “builder-unsupport”, назначение которых мне не ясно. В принципе, во многих случаях для описания виджета кроме пары тэгов <glade-widget-class></glade-widget-class> больше ничего не надо. В более сложных случаях там могут находиться описания методов, действий, параметров и т.п.; это всё мы рассмотрим далее.

Наконец, пара тэгов <glade-widget-group></glade-widget-group> описывает раздел палитры glade, в котором появятся наши виджеты. У тега <glade-widget-group> есть два обязательных атрибута: “name” — честно говоря, я не знаю где он используется, т.к. в заголовке раздела палитры glade отображается второй параметр — “title”. Далее идут теги <glade-widget-class-ref name="My Widget?"/>, в которых параметр “name” соответствует полю “name” виджета, который мы хотим видеть в данном разделе палитры. Косая черта в конце тэга не является опечаткой!!! В одном файле каталога может быть несколько групп виджетов <glade-widget-group></glade-widget-group>; каждый из которых будет соответствовать своему разделу палитры glade. Ещё внутри группы может присутствовать тэг <default-palette-state expanded="False”/>, указывающий glade, что данный раздел при запуске отображается свёрнутым.


2. Библиотека сопровождения. Это динамически загружаемый модуль, который glade загружает, когда встречает соответствующий параметр “library” в файле каталога. Он лежит в /usr/lib/glade/modules, а его название образуется из значения поля “library” путём прибавления приставки “lib” вначале и расширения “.so” в конце. В нашем случае это будет libglade-my.so Одна из главных задач, выполняемых библиотекой сопровождения — загрузка библиотеки, реализующей набор виджетов, поддержку которого мы хотим получить в glade. Для этого она, во-первых, должна динамически связываться с библиотекой виджетов, а во-вторых, содержать в своём коде обращение к какой-либо функции из библиотеки виджетов (иначе в силу излишнего, в данном случае, интеллекта ld он не будет динамически связывать библиотеку сопровождения с библиотекой виджетов несмотря на указание ему параметра -lmy (считаем, что сама библиотека виджетов называется libmy.so)). Кроме того, в среди параметров линкера должно присутствовать -module. Вот пример практически минимального кода библиотеки сопровождения:



Здесь единственная функция — как раз упомянутая «пустышка» для того, чтобы библиотека сопровождения связывалась с библиотекой виджетов. Следует отметить, что все функции, упоминаемые в файле каталога (тэги и параметры тэгов типа “xxx-function”) glade ищет в библиотеке сопровождения.


3. Пиктограмма. Последнее, что необходимо glade для поддержки нашего виджета это пиктограмма, отображающая его в палитре виджетов. В настоящее время glade поддерживает пиктограммы двух размеров, 16х16 и 22х22 точки, которые лежат в каталогах /usr/share/glade3/pixmaps/hicolor/16x16/actions/ и /usr/share/glade3/pixmaps/hicolor/22x22/actions/ (лучше, чтобы для каждого виджета имелись пиктограммы обоих размеров), а название складывается по правилу widget-CATALOG_NAME-GENERIC_NAME, где CATALOG_NAME — содержимое поля “name” тэга <glade-catalog>, а GENERIC_NAME — содержимое поля “generic-name” тэга
<glade-widget-class> соответствующего виджета. В нашем случае получается widget-my-widget.png (я видел пиктограммы только типа .png, возможно, поддерживаются любые изображения, загружаемые библиотекой gtk-pixbuf). Возможно явное указание названия пиктограммы параметром “icon-name” тэга <glade-widget-class>.

О свойствах виджета


Glade просматривает все свойства каждого загружаемого виджета. Поэтому в файле каталога во многих случаях нет необходимости их описывать. Однако описание свойств в файле каталога даёт много дополнительных возможностей. Свойства описываются внутри пары тэгов <properties></properties> внутри описания класса виджета. Каждое свойство задаётся парой тегов <property></property> (Возможна и однострочная запись <property что-то там/>). Тэг <property> имеет следущие атрибуты (обязательным из которых является только атрибут “id”):


Есть ещё парочка атрибутов, “since” и “resource”, назначение которых мне не ясно.


Внутри пары тэгов <property></property> могут присутсвовать дочерние тэги:


Иногда бывает нужно, чтобы при изменении свойств виджета выполнялись какие-то действия, отличные от просто вызова метода set_property класса данного виджета. Для этого в каталоге в описании класса виджета можно использовать пару тэгов <set_property_func></set_property_func>, между которыми заключено название функции, вызываемой при изменении какого-либо из свойств виджета. Сама функция определяется в исходниках библиотеки сопровождения. Вот пример кода функции изменения свойства:


Здесь установка свойств “property1” и “property2” требует особых действий, выполняемых функциями glade_my_widget_set_property1 и glade_my_widget_set_property2; конструкция, к которой происходит обращение в случае изменения других свойств фактически представляет собой обёртку для запуска метода set_property класса нашего виджета. Само значение свойства можно получить с помощью функций g_value_get_xxx(value) (в зависимости от типа свойства это может быть g_value_get_int, g_value_get_string и т.п.). Параметром у макроса GWA_GET_CLASS должен быть тип родительского виджета. (Считаем, что родительским классом My Widget? является Gtk Drawing Area?).


Аналогично можно определить и функцию, отвечающую за получение свойств виджета. Её название задаётся внутри пары тэгов <get_property_func></get_property_func> в файле каталога. Свойство передаётся через параметр value этой функции (сначала следует выполнить вызов g_value_reset(value), а затем — g_value_set_xxx(value, value_itself).


У виджета-контейнера могут присутствовать свойства упаковки дочерних виджетов. Насколько я понимаю, их синтаксис идентичен просто свойствам за исключением того, что их описания располагаются внутри пары тэгов <packing-properties></packing-properties>. Свойства упаковки дочерних виджетов должны быть зарегистрированы при инициализации класса виджета (в самой библиотеке виджета) с помощью функции gtk_container_class_install_child_property().

Что ещё может быть у виджета


Внутри описания класса виджета кроме уже описанных функций <set_property_func> и <get_property_func> могут быть определены следующие функции (каждый тэг является контейнером, внутри которого помещается название определяемой в библиотеке сопровождения функции):


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



Каждое определение действия (тэг <action>) имеет обязательные атрибуты “id” — идентификатор действия и “name” — его название, как оно будет отображаться в редакторе свойств glade. Необязательный атрибут “stock” указывает на то, что данному действию соответствует определённая пиктограмма. Как видно из примера, описания действий могут быть вложенными. Если пользователь совершает действие, то вызвается функция из библиотеки сопровождения, название которой glade строит следующим образом: вначале идёт “glade_", затем — название виджета, угаданное glade на основании названия («name”) виджета по тому же принципу, что и для функции, возвращающей тип виджета (в нашем случае my_widget), и в конце — “_action_activate”. Итого в нашем случае получаем glade_my_widget_action_activate.


Функция, обрабатывающая выполнение действия, имеет примерно следующий вид:


Последним аргументом ей передаётся идентификатор действия, по которому можно узнать, какое действие произвёл пользователь. Если было выбрано вложенное действие, то строка идентификатора будет содержать полный идентификатор, состоящий из идентификатора выбранного действия и идентификаторов вышележащих действий, в которое выбранное действие было вложено, например, «add_parent/Alignment».


Если виджет является контейнером, то описание класса может содержать действия упаковки (packing actions) — действия, выполняемые над заключёнными в него виджетами (например, «вставить новый в начало», «вставить новый в конец», «удалить»). Их описание полностью идентично действиям, за исключением того, что они помещаются внутри пары тэгов <packing-actions></packing-actions>.


Описание класса виджета может содержать параметры упаковки по умолчанию. Они имеют следующий вид:



здесь пара тэгов <packing-defaults></packing-defaults> ограничивает список параметров упаковки по умолчанию, пара тэгов <parent-class></parent-class> определяет, для какого родительского класса (т.е. при упаковке нашего виджета в контейнер, относящийся к соответствующему классу) эти параметры предназначаются (параметр “name” тэга <parent-class> определяет наименование родительского класса), а между тэгами <parent-class></parent-class> располагаются сами значения параметров по умолчанию (наименование параметра задается атрибутом “id” тега <child-property>, а его значение по умолчанию — атрибутом “default” того же тэга). В приведённом примере установлено, что при упаковке нашего виджета в контейнер класса Gtk V Box? параметр “expand” по умолчанию будет иметь значение “false”.

2. libglade


Для того, чтобы наши сторонние виджеты могли использоваться в интерфейсе, созданном с помощью Glade, следует ещё позаботиться об их поддержки в библиотеке libglade, которая занимается построением интерфейса во время исполнения программы. Для этого следует написать ещё одну библиотеку сопровождения, — на это раз уже для libglade. Вот образец кода подобной библиотеки:



Основное значение здесь выполняет функция glade_register_widget(). Первым её аргументом идёт тип виджета, возвращающую тип нашего виджета. Затем — указатель на функцию, отвечающую за построение виджета. В подавляющем большинстве случаев нет необходимости самим что-то здесь изобретать, и следует воспользоваться функцией libglade glade_standard_build_widget(). Последние два аргумента (у нас оба равны NULL) используются для контейнеров: третий аргумент — указатель на функцию, отвечающую за построение дочерних виджетов (часто можно опять пользоваться стандартной функцией glade_standard_build_children()), четвёртый — указатель на функцию, осуществляющую поиск внутренних виджетов составного виджета. Эта функция имеет следующий вид:



Для нас важны последние два аргумента этой функции: Gtk Widget? *parent, возвращающий указатель на наш виджет, и const gchar *name — название внутреннего виджета, указатель на который мы желаем получить.


Если в нашей библиотеке имеется несколько виджетов (и мы желаем получить поддержку их всех в libglade), то все они должны быть зарегистрированы посредством вызова функции glade_register_widget(). Кроме того, внутри функции glade_module_register_widgets() могут ещё находиться процедуры регистрации фиктивных свойств виджетов (т.е. таких свойств, которые не зарегистрированы с помощью вызова функции g_object_class_install_property() при инициализации класса виджета. Честно говоря, наличие таких свойств не вполне корректно, но glade/libglade позволяют выкручиваться и в этих случаях). Регистрация производится вызовом функции



У этой функции первый аргумент — тип виджета, для которого мы хотим зарегистрировать свойство, второй — название свойства и третий — функция, которая вызывается при изменении свойства. Она имеет вид change_function(Glade XML? *xml, Gtk Widget? *widget, const char *name, const char *value), где второй аргумент — указатель на виджет, свойство которого изменилось, третий — название свойства, и четвёртый — его значение. Четвёртый аргумент имеет тип «gchar*" (видимо, создатели libglade не захотели связываться с GValue), и если свойство имеет отличный от gchar* тип, то приходится преобразовывать его в то, что нам надо (например, если нам нужен целочисленный тип, то следует писать INT(value)).


Библиотека поддержки наших виджетов в libglade должна динамически связываться с самой библиотекой виджетов, а параметры компоновщика должны быть следующие: -export-dynamic -module -avoid-version -no-undefined. Модуль кладётся в /usr/lib/libglade/2.0 В нашем случае рекомендуемое его название — libmy.so

Источники информации

http://glade.gnome.org/docs/catalog.html
http://www.jamesh.id.au/software/libglade/
Кроме того многое вырыто из исходников glade (рекомендую посмотреть на glade-%version/plugins/gtk+/glade-gtk.c и gtk+.xml там же) и libglade (libglade-%version/glade/glade-gtk.c).


 
Файлов нет. [Показать файлы/форму]
Комментариев нет. [Показать комментарии/форму]