Часть 5. Сигналы и события

Содержание
5.1. Теория сигналов и событий
5.2. события
5.3. Созвращаемое значение вызова
5.4. Испускание сигналов
5.5. Удаление вызовов
5.6. Временное отключение вызовов
5.7. Общение с обработчиком сигналов
5.8. Испускание и распространение сигналов

5.1. Теория сигналов и вызовов

GTK - управляемый событиями инструментарий сигналов, которые содержатся в main Gtk до того, как событие произойдет и пройдет контроль в соответствующей функции.

Прохождение контроля реализуется при помощи идеи сигналов. Эти сигналы не похожи на системные сигналы Unix и не осуществляются при помощи их использования, хотя терминология практически идентична. Когда происходит что-то подобное нажатию кнопки мыши, то соответствующий сигнал "испускается" виджетом, на котором была нажата кнопка. Реализация этой схемы и является одной из основных функций GTK. Есть как сигналы, которые наследуют все виджеты(например 'destroy') так и сигналы, которые являются специфичными для какого-то типа виджетов, например сигнал 'toggled' на кнопке toggle.

Чтобы заставить виджет выполнить действие, устанавливаем обработчик сигнала на вызов определенной функции. Например как-то так:

$object->signal_connect( "signal_name", \&signal_func );

$object->signal_connect( signal_name => \&signal_func );

$object->signal_connect( "signal_name", \&signal_func, $optional_data ... );

$object->signal_connect( "signal_name", \&signal_func, @optional_data );

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

Левая переменная signal_connect() - виджет, который будет вызывать сигнал. Первый аргумент - строка, представляющая сигнал, который в дальнейшем будет регистрироваться. Второй аргумент - ссылка на вызываемую по сигналу подпрограмму. Эту подпрограмму называют вызовом обработчика. Если необходимо отдать ему некоторые данные, то их надо указать в списке после второго аргумента.

Если вы не разбираетесь в ссылках Perl, то обычному вызову подпрограммы предшествует "\&" и передавая таким образом ссылки на подпрограмму, вы не можете передавать подпрограмме параметры в скобках.

Для передачи одного сигнала подпрограмме необходимо использовать примерно такой код:

$object->signal_connect( "signal_name", sub { do_something; } );

Вызывающая фукция обычко определяется примерно так:

sub callback_func
  {
    my ( $widget, @data ) = @_;
    ...
  }

Первый аргумент посылает вызов всегда как виджет, который бы сгенерировал данный сигнал и остальная часть аргументов - дополнительные данные, посланные при помощи функции signal_connect() Запомните, что Perl не заставляет объявлять или использовать аргументы, присланные подпрограмме и это является хорошим тоном при программировании.

Стоит заметить, что вышеприведенное описание функции обработчика сигнала иллюстрирует только общую схему работы. Некоторые виджеты испускают различые по схеме работы сигналы. Например виджет CList 'select_row' передает параметры колонками и столбцами.

5.2. События

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

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

my ( $widget, $data, $event ) = @_;

или, есил Вы хотите передать список:

my ( $widget, @data ) = @_;
my $event = pop( @data );

Некоторыми наиболее употребляемыми полями в структуре (вызовов - ??) явялются button , keyval , и type .

Структура button содержит помер нажатой кнопки(1,2 или 3). Структура keyval содержит имя кнопки, которая была нажата на клавиатуре(если была). И структура type содержит нижеперечисленные строчки:

Такая структурированность типов сигналов довольно просто позволяет определить причину события. Если нажали кнопку мыши, то какую? Пример:

sub some_event
  {
    my ( $widget, @data ) = @_;
    my $event = pop( @data );

    if ( ( defined( $event->{'type'} ) ) and
         ( $event->{'type'} eq 'button_press' ) )
      {
        if ( $event->{'button'} == 3 )
          {
            # right click
          }
        else
          {
            # non-right click
          }
      }
  }

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

5.3. Возвращаемое вызовом значение

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

5.4. Испускание сигналов

Если вы хотите испустить специфический сигнал, вы можете сделать это при помощи функции

$widget->signal_emit( $id );

$widget->signal_emit_by_name( $signal_name );

Агрумент первого вызова является идентификатором тега, который возвращается signal_connect(). Аргумент во второй форме является строкой, определяемой именем сигнала.

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

5.5. Удаление вызовов

Id вызова, которые возвращается функцией signal_connect() также позволяет удалить вызов из списка вызовов при помощи функции signal_disconnect() например так:

$widget->signal_disconnect( $id );

Если необходимо удалить все сигналы, то для этого служит функция

$widget->signal_handlers_destroy();

Этот запрос удаляет все текущие сигналы из объекта, заданные первым аргументом. ЭТа процедура выполняется автоматически при уничтожении виджета и нет необходимости заботиться о данной функции.

5.6. Временное выключение вызовов

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

$widget->signal_handler_block( $callback_id );
$widget->signal_handler_block_by_func( \&callback, $data );
$widget->signal_handler_block_by_data( $data );

$widget->signal_handler_unblock( $callback_id );
$widget->signal_handler_unblock_by_func( \&callback, $data );
$widget->signal_handler_unblock_by_data( $data );

5.7. Общение с обработчиком сигналов

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

$id = $object->signal_connect( $signal_name,
                               \&function,
                               @optional_data );

$id = $object->signal_connect_after( $signal_name,
                                     \&function,
                                     @optional_data );

$id = $object->signal_connect_object( $signal_name,
                                      \&function,
                                      $slot_object );

$id = $object->signal_connect_object_after( $signal_name,
                                            \&function,
                                            $slot_object );

# I'm unsure of this one
$id = $object->signal_connect_full( $name,
                                    \&function,
                                    $callback_marshal,
                                    @optional_data,
                                    \&destroy_function,
                                    $object_signal,
                                    $after );

# I'm not sure of this one either
$id = $object->signal_connect_interp( $name,
                                      \&function,
                                      @optional_data,
                                      \&destroy_function,
                                      $after );

$id = $object->signal_connect_object_while_alive( $signal,
                                                  \&function,
                                                  $alive_object );

$id = $object->signal_connect_while_alive( $signal,
                                           \&function,
                                           @optional_data,
                                           $alive_object );

$object->signal_disconnect( $id );

$object->signal_disconnect_by_func( \&function,
                                    @optional_data );

5.8. Испускание и распространение сигналов

Эмиссия сигнала - процесс, посредством которого GTK управляет всеми хандлерами для определенного объекта и сигнала. Сначала, отметьте, что возвращаемое значение в результате эмиссии сигнала - возвращаемое значение последнего выполненного хэндлера. Так как сигналы типа 'last', то обработка будет производиться при помощи дефолтным хендлером, если не запрашивать signal_connect_after().

Если событие(например "button_press_event") определено, то

Некоторые следствия вышеупомянутого: