Еще о сигналах и обработчиках

Если испущен какой-либо из сигналов, к которому подключилась программа, вызывается соответствующий обработчик. Наш обработчик "delete_event" завершает цикл событий "gtk_main()" путем вызова "gtk_main_quit()"; это заставляет "gtk_main()" вернуть управление, что завершает программу. Обработчик "clicked" заменяет текст метки тем же, но перевернутым текстом. Заметьте, что метка была передана в обработчик как параметр "user_data" в "gtk_signal_connect()".

Обычно, ошибочно думают что все сигналы используют один и тот же тип обработчика. Каждый сигнал требует обработчик с определенным типом и набором аргументов и определенным поведением. Сигнал "clicked" имеет очень распространенный тип обработчика; его обработчик принимает указатель на виджет, который испустил сигнал и любой "user_data"-параметр, который предоставляет программист. Обработчик должен вернуть void, иначе может произойти порча памяти.

С другой стороны, "delete_event" является особым случаем. Он принимает три аргумента; первый и последний аналогичны "clicked", тогда как второй является указателем на событие, которое породило сигнал (события -- это сообщения от X приложению, оповещающие о передвижениях мыши, нажатиях на клавиши и т.п.). Обработчик "delete_event" возвращает волшебное значение: если вернули FALSE, значит Gtk+ удалит окно; а если TRUE -- то ничего не сделает. Возвращайте TRUE, если вам надо сделать что-то отличное от удаления окна; например, если вы захотите предупредить пользователя о несохраненном документе.

Заголовочные файлы виджетов -- наилучшее быстрое справочное руководство по типу обработчиков. Структура класса для виджета будет иметь место для обработчика сигнала по умолчанию; ваш обработчик должен быть смоделирован на основе обработчика по умолчанию. Например, в "gtk/gtkbutton.h" структура класса GtkButton выглядит примерно так: struct _GtkButtonClass { GtkBinClass parent_class; void (* pressed) (GtkButton *button); void (* released) (GtkButton *button); void (* clicked) (GtkButton *button); void (* enter) (GtkButton *button); void (* leave) (GtkButton *button); };

Глава 9 объясняет, для чего нужна структура класса; а сейчас, просто обратите внимание на указатели на функции, и отметьте, что они соответствуют сигналам. Для перехода от этого: void (* clicked) (GtkButton *button); к этому: static void button_click_cb(GtkWidget *w, gpointer data); просто добавьте gpointer data к описанию функции в структуре класса. В Hello, World я также сменил тип с GtkButton* на GtkWidget*; это делается часто, так как может быть более удобно иметь GtkWidget*. Аргумент всегда будет кнопкой (GtkButton), испускающей сигнал.

Другой пример может быть полезен; вот "delete_event" из "gtk/gtkwidget.h": gint (* delete_event) (GtkWidget *widget, GdkEventAny *event); а вот обработчик из Hello, World: static gint delete_event_cb(GtkWidget *w, GdkEventAny *e, gpointer data);

Ну вот и все с этим. Вы можете писать простые приложения Gtk+, используя только информацию, представленную в этом разделе. Gtk+ и Gnome -- мощные средства разработки приложений, потому что вы можете думать о реальной функциональности, вместо борьбы за появление окна на экране.


Linux Land
2000-09-15