События, сигналы и обратные вызовы
У всех библиотек GUI есть нечто общее. Должен существовать некий механизм для выполнения программного кода на действие пользователя. Программа, выполняющая в режиме командной строки, может позволить себе остановку выполнения в ожидании вывода и затем применить нечто вроде оператора выбора для выполнения разных ветвей программы в зависимости от введенных данных. Такой подход нецелесообразен в случае приложения GUI, поскольку оно должно непрерывно реагировать на ввод пользователя, например, ему приходится постоянно обновлять области окна.
У современных оконных систем есть система событий и приемники событий, которым адресована эта задача. Идея заключается в том, что каждый пользовательский ввод обычно с помощью мыши или клавиатуры инициирует событие. Нажатие на клавиатуре, например, вызовет “событие клавиатуры”. Затем пишется программный код, который ждет приёма такого события и выполняется в случае его возникновения.
Как вы уже видели, эти события генерирует система X Window System, но они мало помогут вам как программисту GTK+, т. к. они очень низкоуровневые. Когда производится щелчок кнопкой мыши, X порождает событие, содержащее координаты указателя мыши, а вам нужно знать, когда пользователь активизирует виджет.
У GTK+ есть собственная система событий и приемников событий, называемых сигналами и обратными вызовами. Их очень легко применять, поскольку для установки обработчика сигнала можно использовать очень полезное свойство языка C, указатель на функцию.
Сначала несколько определений. Сигнал GTK+ порождается объектом типа G_OBJECT
, когда происходит нечто, например, ввод пользователя. Функция, связанная с сигналом и, следовательно, вызываемая при любом порождении сигнала, называется функцией обратного вызова.
Примечание
Имейте в виду, что сигнал GTK+ — это нечто иное, чем сигнал UNIX.
Как программист, использующий GTK+, вы должны заботиться только о написании и связывании функций обратного вызова, поскольку код порождения сигнала — это внутренний программный код определенного виджета.
Прототип или заголовок функции обратного вызова обычно похож на следующий:
void a_callback_function(GtkWidget *widget, gpointer user_data);
Вы передаете два параметра: первый — указатель на виджет, породивший сигнал, второй — произвольный указатель, который вы выбираете самостоятельно, когда связываете обратный вызов. Вы можете использовать этот указатель для любый целей.
Связать функцию обратного вызова тоже очень просто. Вы вызываете функцию-макрос g_signal_connect
и передаете ей виджет, имя сигнала в виде строки, указатель на функцию обратного вызова и ваш произвольный указатель:
gulong g_signal_connect(gpointer *object, const gchar* name, GCallback func, gpointer user_data);
Следует отметить, что для связывания функций обратного вызова нет ограничений. Вы можете иметь много сигналов, связанных с одной и той же функцией обратного вызова, и много функций обратного вызова, связанных с единственным сигналом.
В документации по API GTK+ можно найти подробное описание сигналов, порождаемых каждым виджетом.
Примечание
До появления GTK+ 2 для связывания функций обратного вызова применялась функция-макрос
gtk_signal_connect
. Начиная с GTK+ 2, она была заменена на функцию-макросg_signal_connect
.
Пример: Функция обратного вызова
Рассмотрим пример, где опробуем работу функции-макроса g_signal_connect
.
В программе gtk_callback.c
вставим в окно кнопку и свяжем сигнал clicked
(щелчок мышью по кнопке) с вашей функцией обратного вызова для вывода короткого сообщения:
#include <gtk/gtk.h>
#include <stdio.h>
static int count = 0;
void button_clicked(GtkWidget *button, gpointer data)
{
printf("%s pressed %d time(s) \n", (char *)data, ++count);
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("Hello World!");
gtk_container_add(GTK_CONTAINER(window), button);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), "Button 1");
gtk_widget_show(button);
gtk_widget_show(window);
gtk_main();
return 0;
}
Введите исходный текст программы и сохраните его в файле с именем gtk_callback.c
. Откомпилируйте и скомпонуйте программу аналогично программе gtk1.c
из предыдущего раздела. Запустив ее, вы получите окно с кнопкой. При каждом щелчке кнопки мышью будет выводиться короткое сообщение (рис. 1).
Как это работает
Вы добавили два новых элемента в программу gtk_callback.c
: виджет GtkButton
и функцию обратного вызова. GtkButton
— это виджет простой кнопки, которая может содержать текст, в нашем случае “Hello World”, и порождает сигнал, названный clicked
, каждый раз, когда кнопку щелкают мышью.
Функция обратно вызова button_clicked
связана с сигналом clicked
виджета кнопки с помощью функции-макроса g_signal_connect
:
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), "Button 1");
Обратите внимание на то, что имя кнопки — "Button 1"
— передаётся в функцию обратного вызова как данные пользователя.
Весь остальной добавленный программный код касается виджета кнопки, создаваемой так же, как окно — вызовом функции gtk_button_new_with_label
— функция gtk_widget_show
делает её видимой.
Для расположения кнопки в окне вызывается функция gtk_container_add
. Эта простая функция помещает GtkWidget
внутрь объекта GtkContainer
и принимает контейнер и виджет как аргументы:
void gtk_container_add (GtkContainer *container, GtkWidget *widget);
Как вы уже знаете, GtkWindow
— потомок или дочерний объект объекта GtkContainer
, поэтом вы можете привести тип вашего объекта-окна к типу GtkContainer
с помощью макроса GTK_CONTAINER
:
gtk_container_add(GTK_CONTAINER(window), button);
Функция gtk_container_add
прекрасно подходит для расположения в окне одиночного виджета, но гораздо чаще вам потребуется для создания хорошего интерфейса размещать несколько виджетов в разных частях окна. У комплекта GTK+ есть специальные виджеты как раз для этой цели, именуемые виджетами упаковочных контейнеров.
Назад: Введение в GTK+
Вернуться на главную страницу