Єдина Країна! Единая Страна!

GTK+ — 4. Події та сигнали

Березень 25th, 2009

У цій частині я розповім про систему подій (events) та сигналів у бібліотеці GTK+. Власне, бібліотека GTK+ є “event driven system”, тобто, такою, що цілком побудована на основі системи подій та сигналів на них. Адже усі графічні додатки є “event driven”, тобто такими, мета яких реагувати на певні дії користувача (події), надсилати своєму коду сигнали, і обробляти їх.

Усе починається з того, що програма запускає головний цикл, який неперервно перевіряє наявність нових подій. Якщо ніяких подій не відбувається, програма тихенько собі спить, і нічогісінько не робить. У GTK+ повідомленнями про події є повідомлення від сервера X. Коли віджет генерує якусь подію, програма може на неї відреагувати. Програміст GTK+ може з’єднувати специфічні колбеки (callback) та сигнали. Сам callback є нічим іншим, як вказівником на звичайнісіньку функцію, яка реагуватиме на сигнал. Наприклад, маємо віджет “кнопка”, подію “натискання на кнопку”, і колбек (функцію), яка викликається при обробці події (натискання на кнопку).

У версії 2.0 сигнальну систему було перенесена з GTK до GLib, ось чому функції і типи, що описуються в цьому розділі мають префікс “g_” а не “gtk_”. Ми не будемо заглиблюватись в деталі, що до додаткових можливостей, які дає сигнальна система GLib 2.0 порівняно з сигнальною системою GTK 1.2.


Перед тим, як розглянути в деталі “Привіт, світе”, обговоримо сигнали і функції зворотнього виклику (ФЗВ, “колбеки”) трохи детальніше. GTK+ — набір інструментів, керований подіями, що означає те, що воно “засинає” на функції gtk_main() поки не виникне якась подія і керування не передасться відповідній функції.

Ця передача керування базується на уявленні про “сигнали”. (Зауважимо, що ці “сигнали” — не те саме, що юніксові системні сигнали, і впроваджені без їх використання, хоча, термінологія — майже ідентична). Коли з’являється подія, така, як натиснення кнопки миші, відповідна подія буде “випущеною” елементом інтерфейсу (ЕІ), на якому був курсор миші. Так GTK робить більшість своєї корисної роботи. Є сигнали, які успадковують всі елементи інтерфейсу, такі, як “destroy”, і є сигнали, специфічні для для певного типу ЕІ, наприклад, “toggled” для для кнопки вибору. Це робиться з використанням таких функцій, як ця:

1
gulong g_signal_connect(gpointer *object, const gchar *name, GCallback func, gpointer func_data);

Де перший аргумент — ЕІ, який буде посилати сигнал, другий — ім’я сигналу, який ви бажаєте обробляти, третій — функція, яка буде викликана, коли з’явиться сигнал і четвертий — данні, які передадуться цій функції.

Функція, вказана в третьому аргументі називається “функцією зворотнього виклику”, і загалом, має мати такий вигляд:

1
void callback_func (GtkWidget *widget, gpointer callback_data );

Де перший аргумент — вказівник на ЕІ, котрий посилає сигнал, і другий — вказівник на данні, передані, як останній аргумент функції g_signal_connect(), як показано вище.

Зауважимо, що вище наведена декларація — загальний приклад, адже деякі специфічні сигнали ЕІ генерують різні викликаючі параметри.

Інший виклик, який використовується з тією ж метою — g_signal_connect_swapped():

1
gulong g_signal_connect_swapped (gpointer *object, const gchar *name, GCallback func, gpointer *slot_object);

g_signal_connect_swapped() — те саме, що і g_signal_connect() тільки ФЗВ використовує лише один аргумент — вказівник на об’єкт GTK. Отже коли використовується ця функція, для приєднання сигналів, ФЗВ мусить мати вигляд:

1
void callback_func(GtkObject *object);

де object — зазвичай є ЕІ. Як правило, ми не будемо використовувати g_signal_connect_swapped() однак, вона зазвичай використовується, щоб викликати функцію GTK, яка використовує лише один аргумент, як у випадку з нашим прикладом “Привіт, світе”.

Підставою для того, щоб мати дві функції для приєднання сигналів є прагнення дозволити функціям зворотнього виклику мати різну кількість аргументів. Багато функцій бібліотеки GTK мають лише один аргумент — вказівник на GtkWidget, отже, ви можете захотіти використати g_signal_connect_swapped() для них. Тоді, як вашим функціям можуть знадобитись додаткові аргументи.

Привіт, світе!

Ось і наш привіт світові:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <gtk/gtk.h>
#include <stdlib.h>
 
 
static void destroy (GtkWidget*, gpointer);
static void clicked (GtkWidget*, gpointer);
 
int main (int argc, char *argv[])
{
    GtkWidget *window, *button;
 
    gtk_init (&argc, &argv);
 
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "window");
 
    button = gtk_button_new_with_label ("the button");
 
    gtk_container_set_border_width (GTK_CONTAINER (window), 25);
    gtk_container_add (GTK_CONTAINER (window), button);
 
    gtk_widget_show_all (window);
 
    g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (clicked), NULL);
 
    gtk_main ();
 
    return 0;
}
 
static void destroy (GtkWidget *window, gpointer data)
{
    gtk_main_quit ();
}
 
static void clicked (GtkWidget *button, gpointer data)
{
    g_printf ("button clicked\n");
}

Коротко про те, що ми тут маємо: спершу створили вікно, контейнер, і додали до нього кнопку з міткою “the button”. Потім з’єднали два сигналу з двома різними колбеками (функціями): сигнал “destroy” вікна з функцією destroy, а сигнал “clicked” кнопки — з функцією “clicked” (для простоти обрав назви функцій, які відповідають назвам сигналів).

Функція “clicked” за допомогою функції gtk+ g_print просто виводить на стандартний вивід напис “button clicked”, тобто, при кожному натисканні на кнопку, на консоль виводитиметься “button clicked”.

Функції “destroy”, прив’язана до однойменного сигналу нашого головного вікна, як бачите, лиш викликає іншу функцію gtk+ — gtk_main_quit(), яка завершує роботу головного циклу програми, а тоді вже й роботу самої програми.

Від’єднання сигналу

Ми також можемо від’єднувати колбеки від сигналів. Ось простий приклад:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <gtk/gtk.h>
 
 
int handler_id;
 
void button_clicked (GtkWidget *widget, gpointer data)
{
    g_print ("button clicked\n");
}
 
void toogle_signal (GtkWidget *widget, gpointer window)
{
    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
        handler_id = g_signal_connect (G_OBJECT(window), "clicked", G_CALLBACK(button_clicked), NULL);
    } else {
        g_signal_handler_disconnect (window, handler_id);
    }
}
 
int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *fixed;
    GtkWidget *button;
    GtkWidget *check;
 
    gtk_init (&argc, &argv);
 
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER);
    gtk_window_set_default_size (GTK_WINDOW(window), 250, 150);
    gtk_window_set_title (GTK_WINDOW(window), "Disconnect");
 
    fixed = gtk_fixed_new ();
    gtk_container_add (GTK_CONTAINER(window), fixed);
 
    button = gtk_button_new_with_label ("Click");
    gtk_widget_set_size_request (button, 80, 30);
    gtk_fixed_put (GTK_FIXED(fixed), button, 30, 50);
 
    check = gtk_check_button_new_with_label ("Connect");
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(check), TRUE);
    gtk_fixed_put (GTK_FIXED(fixed), check, 130, 50);
 
    handler_id = g_signal_connect (G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), NULL);
 
    g_signal_connect (G_OBJECT(check), "clicked", G_CALLBACK(toogle_signal), (gpointer) button);
    g_signal_connect_swapped (G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
 
    gtk_widget_show_all (window);
 
    gtk_main();
 
    return 0;
}

gtk_disconnect_event

Тут ми маємо кнопку, і кнопку-прапорець. Кнопка-прапорець під’єднує і від’єднує колбеки від сигналу “clicked” кнопки.

1
handler_id = g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), NULL);

Функція g_signal_connect() повертає унікальний ідентифікатор обробника, який ідентифікує колбек. А цей код отримує стан кнопки-прапорця. Якщо прапорець встановлено, він під’єднує колбек, або ж від’єднує, якщо прапорець знято:

1
2
3
4
5
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) {
    handler_id = g_signal_connect (G_OBJECT(window), "clicked",  G_CALLBACK(button_clicked), NULL);
} else {
    g_signal_handler_disconnect (window, handler_id);
}

Використані матеріали

  • http://docs.linux.org.ua/GTK
  • http://zetcode.com/tutorials/gtktutorial/gtkevents/

    Коментарі

    коментарі

    Powered by Facebook Comments

  • Leave a Reply