The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа

Сигналы

Сигналы GObject не имеют никакого отношения к стандартным сигналам UNIX: они подключают произвольные события определённые приложением с любым количеством слушающих. Например, в GTK+, каждое пользовательское событие (нажатие клавиши или перемещение курсора) происходит из X сервера и генерирует GTK+ событие через форму эмиссии сигнала в данном экземпляре объекта.

Каждый сигнал регистрируется в системе типов вместе с типом который может его издать: пользователи типа, как говорится подключают в данный экземпляр типа когда он регистрирует замыкание вызываемое при эмиссии сигнала. Пользователи могут так же издать сигнал или остановить эмиссию сигнала изнутри одного из замыканий к которому подключен сигнал.

Когда сигнал издаётся на данном экземпляре типа, вызываются все замыкания подключенные в данном типовом экземпляре. Все замыкания связанные с такими сигналами представляют callback-функцию следующей сигнатуры:

return_type function_callback (gpointer instance, ... , gpointer user_data);

Регистрация сигналов

Для регистрации сигнала в существующем типе, мы можем использовать одну из функций g_signal_newv, g_signal_new_valist или g_signal_new:

guint                 g_signal_newv         (const gchar        *signal_name,
                                             GType               itype,
                                             GSignalFlags        signal_flags,
                                             GClosure           *class_closure,
                                             GSignalAccumulator  accumulator,
                                             gpointer            accu_data,
                                             GSignalCMarshaller  c_marshaller,
                                             GType               return_type,
                                             guint               n_params,
                                             GType              *param_types);

Количество параметров у этих функций является немного запутывающим, но они достаточно просты:

  • signal_name: строка которая может использоваться для уникальной идентификации данного сигнала.

  • itype: экземпляр типа который может издавать данный сигнал.

  • signal_flags: частично определённый порядок в котором вызываются замыкания связанные с сигналом.

  • class_closure: замыкание по умолчанию для сигнала: если эмиссия сигнала не NULL, она будет вызвана в этой эмиссии сигнала. Момент вызова этого замыкания по сравнению с вызовом других замыканий связанных с этим игналом, частично зависит от signal_flags.

  • accumulator: указатель функции которая вызывается после каждого вызванного замыкания. Если она возвращает FALSE, эмиссия сигнала останавливается. Если возвращает TRUE, эмиссия сигнала выполняется нормально. Это также используется для вычисления возвращаемого значения сигнала, основываясь на возвращённом значении всех вызванных замыканий.

  • accumulator_data: этот указатель будет передан в каждый запрос accumulator в течение эмиссии.

  • c_marshaller: C маршаллер по умолчанию для любого замыкания которое подключено к этому сигналу.

  • return_type: тип значения возвращаемого сигналом.

  • n_params: количество принимаемых сигналом параметров.

  • param_types: массив GTypes который указывает тип каждого параметра сигнала. Длина массива указывается с помощью n_params.

Как вы видите в выше приведённых определениях, сигнал - это в основном описание замыканий которые могут быть подключены к этому сигналу и описание порядка в котором вызываются замыкания подключенные к данному сигналу.

Подключение сигнала

Если вы хотите подключить к сигналу замыкание, у вас есть три возможности:

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

  • Вы можете использовать g_signal_override_class_closure которая отменит class_closure полученного типа. Эту функцию возможно вызвать только в унаследованном типе, типе в котором сигнал был зарегистрирован. Эта функция используется только языковыми привязками.

  • Вы можете зарегистрировать замыкание с помощью семейства функций g_signal_connect. Это экземпляро-специфичная операция: замыкание будет вызвано только в течение эмиссии данного сигнала в данном экземпляре.

Также возможно подключить различные виды callback-функций к данному сигналу: обработчики прерываний эмиссии вызываются всякий раз когда издаётся данный сигнал для экземпляра на котором он издаётся. Обработчики прерываний эмиссии используются например чтобы заставить все эмиссии mouse_clicked в приложении издавать звук небольшого нажатия кнопки мыши. Обработчики прерываний эмиссии подключаются с помощью g_signal_add_emission_hook и удаляются с помощью g_signal_remove_emission_hook.

Эмиссия сигнала

Эмиссия сигнала выполняется через использование семейства функций g_signal_emit.

void                  g_signal_emitv        (const GValue       *instance_and_params,
                         guint               signal_id,
                         GQuark              detail,
                         GValue             *return_value);

  • Массив instance_and_params GValues содержит список вводимых в сигнал параметров. Первый элемент массива это указатель экземпляра для вызова сигнала. Следующие элементы массива содержат список параметров для сигнала.

  • signal_id идентифицирует вызываемый сигнал.

  • подробную идентификацию определяют детали вызываемого сигнала. Деталь - это своего рода магическая пара символ/параметр которая помещается в течение эмиссии сигнала и которая используется замыканием подключённым к сигналу для фильтрации нежелательных эмиссий сигнала. В большинстве случаев, вы можете безопасно установить это значение в ноль. Смотрите the section called “ The detail argument” для подробностей об этом параметре.

  • return_value содержит возвращаемое значение последнего вызванного замыкания в течение эмиссии если не был определён accumulator. Если accumulator был определён в процессе создания сигнала, этот сумматор используется для вычисления return_value как функция возвращаемых значений всех замыканий вызываемых в течение эмиссии. [10] Если нет замыканий вызываемых в течение эмиссии, return_value тем не менее инициализируется в null.

Внутренне, массив GValue помещается в надлежащую функцию эмиссии, signal_emit_unlocked_R (реализована в gsignal.c). Эмиссия сигнала может быть разделена на 5 шагов:

  • RUN_FIRST: если был использован флаг G_SIGNAL_RUN_FIRST при регистрации сигнала вызывается class_closure для этого сигнала если есть. Переходим в состояние EMISSION_HOOK.

  • EMISSION_HOOK: если обработчики прерывания эмиссии были добавлены в сигнал, они вызываются от первого добавленного до последнего. Аккумулируем возвращаемые значения и переходим в состояние HANDLER_RUN_FIRST.

  • HANDLER_RUN_FIRST: если есть замыкания подключенные с помощью семейства функций g_signal_connect, и если они не заблокированы (с помощью семейства функций g_signal_handler_block) они выполняются, от первого до последнего подключения. Переходим в состояние RUN_LAST.

  • RUN_LAST: если флаг G_SIGNAL_RUN_LAST был установлен в течение регистрации и если было установлено замыкание class_closure, оно вызывается. Переходим в состояние HANDLER_RUN_LAST.

  • HANDLER_RUN_LAST: если есть замыкания подключенные с помощью семейства функций g_signal_connect_after, если они не вызваны в течение HANDLER_RUN_FIRST и не заблокированы, они выполняются здесь, от первого до последнего подключения. Переходим в состояние RUN_CLEANUP.

  • RUN_CLEANUP: если флаг G_SIGNAL_RUN_CLEANUP был установлен в процессе регистрации и если было установлено class_closure, оно вызывается. Здесь эмиссия сигнала завершается.

Если в какой нибудь точке выполнения эмиссии (исключая состояние RUN_CLEANUP), одно из замыканий или обработчиков прерывания эмиссии остановят эмиссию сигнала с помощью g_signal_stop, эмиссия переходит в состояние CLEANUP.

Если, в какой нибудь точке выполнения эмиссии, одно из замыканий или обработчиков прерывания издаст тот же сигнал в том же экземпляре, эмиссия перегрузится в состояние RUN_FIRST.

Функция суммирования вызывается во всех состояниях, после вызова каждого замыкания (исключая EMISSION_HOOK и CLEANUP). Это суммирует возвращаемые значения замыканий в возвращаемое значение сигнала и возвращает TRUE или FALSE. Если, в каком нибудь пункте, не возвращено TRUE, эмиссия переходит в состояние CLEANUP.

Если нет суммирующей функции, возвращаемое значение последнего обработчика будет возвращено функцией g_signal_emit.

Детальный параметр

Все функции связанные с эмиссией сигнала или подключением сигнала имеют параметр с названием детальный. Иногда этот параметр скрыт с помощью API, но он всегда присутствует в той или иной форме.

Из трёх основных функций подключения, только одна имеет явный детальный параметр в виде GQuark [11]:

gulong     g_signal_connect_closure_by_id          (gpointer          instance,
                           guint          signal_id,
                           GQuark          detail,
                           GClosure         *closure,
                           gboolean          after);

Две другие функции скрывают детальный параметр в идентифицирующем имени сигнала:

gulong     g_signal_connect_closure          (gpointer          instance,
                           const gchar       *detailed_signal,
                           GClosure         *closure,
                           gboolean          after);
gulong     g_signal_connect_data              (gpointer          instance,
                           const gchar     *detailed_signal,
                           GCallback      c_handler,
                           gpointer          data,
                           GClosureNotify      destroy_data,
                           GConnectFlags      connect_flags);

Здесь параметр detailed_signal - это строка которая идентифицирует имя подключаемого сигнала. Однако, формат этой строки структурирован как signal_name::detail_name. Подключение к сигналу с именем notify::cursor_position будет фактически подключением к сигналу notify с именем cursor_position. Внутренне, строка преобразуется в GQuark если она представлена.

Из четырёх основных функций эмиссии сигнала, три имеют явный детальный параметр как GQuark:

void                  g_signal_emitv        (const GValue       *instance_and_params,
                         guint               signal_id,
                         GQuark              detail,
                         GValue             *return_value);
void                  g_signal_emit_valist  (gpointer            instance,
                         guint               signal_id,
                         GQuark              detail,
                         va_list             var_args);
void                  g_signal_emit         (gpointer            instance,
                         guint               signal_id,
                         GQuark              detail,
                         ...);

Четвёртая функция скрывает его в параметре имени сигнала:

void                  g_signal_emit_by_name (gpointer            instance,
                         const gchar        *detailed_signal,
                         ...);

Формат параметра detailed_signal такой же как формат используемый функциями g_signal_connect: signal_name::detail_name.

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

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

Как простое правило, пользователи могут и должны установить детальный параметр в ноль: это полностью отключит дополнительную фильтрацию.



[10] James (снова!!) привёл несколько нетривиальных примеров сумматоров: “ Например, вы можете иметь сумматор который игнорирует возвращаемый NULL из замыкания, а только суммирует не-NULL значения. Другой сумматор может попытаться вернуть список возвращенных замыканиями значений.

[11] GQuark это целочисленное которое представлено уникальной строкой. Оно может преобразовываться между строкой и целочисленным с помощью функций g_quark_from_string и g_quark_to_string.



Закладки на сайте
Проследить за страницей
Created 1996-2019 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру
Hosting by Ihor