Linux. Полное руководство.
23.3. Первая программа на GTK+
23.3.1. Виджиты.
Перед написанием самой простой GTK-программы нужно разобраться с терминологией GTK. Элементы графического интерфейса пользователя — окна, кнопки, поля ввода, переключатели и тому подобное — называются виджитами.
Основным элементом графического интерфейса является окно. Виджиты для размещения в окне помещаются в контейнер. В самом окне выравнивать виджиты можно с помощью вертикальных/горизонтальных боксов или же таблиц. Второй способ более гибок, хотя он может показаться вам сложнее.
Виджиты могут реагировать на сигналы, например, щелчок мышью. При этом вызывается функция-обработчик события (сигнала), если вы определили ее.
Работа с виджитами происходит по такой схеме:
1. создание виджита с помощью одной из функций библиотеки GTK;
2. определение свойств виджита;
3. определение сигналов виджита, если он должен реагировать на сигналы;
4. размещение виджита в контейнере, то есть привязка его к окну;
5. отображение виджита.
Нужно обязательно отобразить виджит, иначе его никто не увидит. Например, следующий фрагмент кода создает виджит — кнопку с текстом — и отображает ее.
GtkWidget *button;
...
/* Рисуем кнопочку с надписью Hello, All */
Button = gtk_button_new_with_label("Hello, All");
/* При нажатии кнопки будет вызвана функция hello() */
Gtk_signal_connect(GTK_OBJECT (button), "clicked",
GTK_SIGNAL_FUNC(hello), NULL);
/* Помещаем кнопку в контейнер */
Gtk_container_add(GTK_CONTAINER(window), button);
/* Отображаем кнопку. */
Gtk_widget_show(button);
Первый оператор создает кнопку (button), второй — добавляет кнопку в контейнер. В данном случае контейнером является наше окно. Виджит window должен быть создан раньше: нельзя создать кнопку без окна. Точнее, можно, но тогда она не будет привязана к какому-либо окну и мы ее не увидим. Функция gtk_widget_show() отображает нашу кнопку. Не забудьте отобразить и само окно. Порядок отображения виджитов особой роли не играет, но рекомендуется главное окно отображать в последнюю очередь.
23.3.2. Окна.
Сейчас мы напишем программу, которая будет формировать небольшое графическое окошко. Начнем сразу с исходного кода — так будет проще понять, что есть что.
Листинг 23.2. Простое окно (файл first.с).
#include <gtk/gtk.h>
Int main(int argc, char *argv[]) {
GtkWidget *window1;
gtk_init(&argc, &argv);
window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window1), "Заголовок");
gtk_widget_show(window1);
gtk_main();
return 0;
}
Сначала мы подключаем заголовочный файл gtk/gtk.h — это необходимое условие для того, чтобы начать работу с библиотекой GTK. В первой строке программы мы объявляем наш основной (и единственный в этой программе) виджит — виджит основного окна:
GtkWidget *window1;
Обратите внимание, что виджит объявлен, но работать с ним пока нельзя. Сначала (обязательно до вызова первой GTK-функции) нужно вызвать инициализирующую функцию gtk_init() и передать ей два два параметра — аргументы функции main(). После того, как библиотека инициализирована, нужно вызвать функцию gtk_window_new(), которая создает окно (напомню, что пока окно объявлено, но не создано). Теперь, когда виджит окна создан, можно установить его свойства и определить реакцию на сигналы. Установим свойство Title (заголовок) окна. Это делается с помощью функции gtk_window_set_title():
Gtk_window_set_title(GTK_WINDOW(window1), "Заголовок");
Теперь можно отобразить наше окно:
Gtk_widget_show(window1);
Чтобы наше приложение могло реагировать на события оконной среды (например, щелчок мыши), нужно вызвать функцию gtk_main(). Функции gtk_init() и gtk_main() должны присутствовать в любой GTK-программе.
Теперь откомпилируем наше приложение. Для этого введем следующую команду в командной строке:
$ gcc first.с -о first `gtk-config --cflags` `gtk-config --libs`
Флаги `gtk config --cflags` `gtk-config --libs` нужно использовать при компиляции любой GTK-программы. Если компиляция не удается, то проверьте, что вы используете апострофы ( `), а не одинарные кавычки ( '), и что программа gtk-config у вас установлена.
Запустим нашу программу в эмуляторе терминала X Window (или оконной среды GNOME/KDE):
$ ./first.
Вы увидите окно, изображенное на рис. 23.1.
Рис. 23.1. Простое окно.
Теперь закроем окно и перейдем к терминалу: окно закрыто, мы его больше не видим, а терминал не освобожден. Наша программа не реагирует на событие закрытия окна. По идее, когда графическая среда закрывает окно, программа должна завершить свою работу. А наша программа этого не делает. Значит, нужно «научить» ее реагировать на события (сигналы) оконной системы. Для этого нажмите в терминале Ctrl+С и отредактируйте исходный текст программы следующим образом:
Листинг 23.3. Добавим реакцию на закрытие окна.
#include <gtk/gtk.h>
Int main(int argc, char *argv[]) {
GtkWidget *window1;
gtk_init(*argc, &argv);
window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window1), "destroy",
GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
gtk_widget_show(window1);
gtk_main();
return 0;
}
Функция gtk_signal_connect() устанавливает реакцию объекта window1 на сигнал destroy и вызывает функцию qtk_main_quit() для завершения работы программы.
А что если нам при завершении работы программы нужно выполнить какие-нибудь специфические действия, например, удалить временные файлы? Тогда нужно написать свою функцию-обработчик события destroy (листинг 23.4).
Эта функция будет называется destroy_window1(), и мы «пропишем» ее в функции gtk_signal_connect() в качестве обработчика события закрытия окна вместо gtk_main_quit(). Делать она не будет ничего, просто вызовет стандартную функцию gtk_main_quit().
Листинг 23.4. Добавляем собственную функцию-обработчик завершения работы.
#include <gtk/gtk.h>
Void destroy_window1(GtkWidget *widget, gpointer data);
Int main(int argc, char *argv[]) {
GtkWidget *window1;
gtk_init(&argc, &argv);
window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window1), "destroy",
(GtkSignalFunc)destroy_window1, &window1);
gtk_widget_show(window1);
gtk_main();
return(0);
}
Void destroy_window1(GtkWidget *widget, gpointer data) {
gtk_main_quit();
}
23.3.3. Изменение размеров окна.
Вам кажется, что окно слишком маленькое и не подходит для нашей программы? Для изменения размеров окна лучше всего использовать функцию.
Void gtk_window_set_default_size(GtkWindow *window,
gint width, gint height);
Эта функция устанавливает ширину окна window равной width, а высоту — height.
Для изменения позиции окна на экране используется функция.
Void gtk_widget_set_uposition(GtkWidget *widget,
gint coord_x, gint coord_y);
Эту же функцию можно использовать для изменения расположения любого виджита — не обязательно, чтобы последний был окном.
Первая функция объявлена в файле gtk/gtkwindow.h, а вторая — в файле gtk/gtkwidget.h:
#include <gtk/gtkwindow.h>
#include <gtk/gtkwidget.h>
...
Gtk_window_set_default_size(window1, 200, 300);
Gtk_wigdet_set_uposition(window1, 50, 50);
23.3.4. Обработка сигналов.
Перед тем, как перейти к следующему пункту, нужно еще раз рассмотреть функцию gtk_signal_connect(). Данной функции нужно передать четыре параметра:
♦ GtkObject *object — объект, которому может быть послан сигнал;
♦ const gchar *name — имя сигнала, например, «destroy»;
♦ GtkSignalFunc func — имя функции обратного вызова, то есть функции, которая будет вызвана для обработки сигнала;
♦ gpointer data — любые данные, которые будут переданы функции-обработчику.
Что такое сигнал? Как только пользователь переместил мышь, оконная среда посылает приложению сигнал, оповещающий о том, что мышь была перемещена. Как только пользователь щелкнул мышью, приложение получит сигнал об этом щелчке. Обрабатывать все сигналы может окно, но удобнее для каждого виджита установить собственную реакцию на события.
Функция, которая обрабатывает сигнал, называется по-разному: функция-обработчик (мы будем использовать именно это название), функция обратного вызова и callback-функция. Такой функции нужно передать два параметра (их передаст сама GTK):
♦ GtkWidget *widget — виджит;
♦ gpointer data — данные.
Параметры, которые нужно передать обработчику, зависят от передаваемого сигнала. Например, если бы мы передавали не сигнал «destroy», а сигнал «delete-event», то нужно было бы указать уже три параметра:
♦ GtkWidget *widget — виджит;
♦ GdkEvent *event — событие;
♦ gpointer data — данные.
Вот наиболее часто используемые сигналы:
♦ button_press_event — нажата левая кнопка мыши;
♦ button_release_event — левая кнопка отпущена;
♦ motion_notify_event — движение мыши;
♦ delete_event — удаление объекта;
♦ destroy_event — уничтожение объекта;
♦ key_press_event — нажата клавиша клавиатуры;
♦ key_release_event — клавиша отпущена;
♦ enter_notify_event — указатель мыши вошел в пределы объекта;
♦ leave_notify_event — указатель мыши вышел за пределы объекта;
♦ focus_in_event — объект стал активным (получил фокус);
♦ focus_out_event — объект не активен;
♦ drag_begin_event — начало перемещения объекта;
♦ drag_request_event — запрос на перемещение объекта;
♦ drag_end_event — перемещение объекта;
♦ drop_enter_event — объект перемещен.
Наиболее часто используемые события GDK (используются в функции-обработчике) перечислены ниже:
♦ GDK_NOTHING — не произошло никакого события;
♦ GDK_DELETE — удаление;
♦ GDK_DESTROY — уничтожение;
♦ GDK_MOTION_NOTIFY — уведомление о перемещении;
♦ GDK_BUTTON_PRESS — нажата любая кнопка мыши;
♦ GDK_1BUTTON_PRESS — нажатие первой кнопки мыши;
♦ GDK_2BUTTON_PRESS — нажатие второй кнопки мыши;
♦ GDK_3BUTTON_PRESS — нажата третья кнопка;
♦ GDK_BUTTON_RELEASE — кнопка (любая) отпущена;
♦ GDK_KEY_PRESS — нажата клавиша;
♦ GDK_KEY_RELEASE — клавиша отпущена;
♦ GDK_ENTER_NOTIFY — указатель мыши в пределах объекта (виджита);
♦ GDK_LEAVE_NOTIFY — указатель мыши вышел за пределы виджита;
♦ GDK_FOCUS_CHANGE — изменения фокуса ввода;
♦ GDK_OTHER_EVENT — другое событие.
23.3.5. Виджит событий — EventBox.
Далеко не все виджиты связаны с окнами. Например, GtkLabel (надпись), GtkTable (контейнер-таблица), GtkHBox (горизонтальный контейнер), GtkVBox (вертикальный контейнер) и некоторые другие с окнами не связаны.
Если нужно, чтобы эти виджиты реагировали на определенные сигналы, нужно использовать виджит EventBox, позволяющий привязать сигнал к не связанному с окном виджиту. Следующая программа демонстрирует привязку события button_press_event к виджиту GtkLabel.
Листинг 23.5. Виджит EventBox.
#include <gtk/gtk.h>
Int main(int argc, char *argv[]) {
GtkWidget *window1; // главное окно.
GtkWidget *event_box1; // eventbox.
GtkWidget *label; // надпись.
/* Инициализируем GTK */
gtk_init( &argc, &argv );
/* Создаем окно с заголовком "Надпись" */
window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window1), "Надпись");
/* Устанавливаем реакцию на закрытие окна */
gtk_signal_connect(GTK_OBJECT(window1), "destroy",
GTK_SIGNAL_FUNC(gtk_exit), NULL );
/* устанавливаем ширину рамки контейнера — окна */
gtk_container_set_border_width(GTK_CONTAINER(window1), 10);
/* создаем event_box */
event_box1 = gtk_event_box_new();
/* помещаем event_box в контейнер */
gtk_container_add(GTK_CONTAINER(window1), event_box1);
/* отображаем event_box */
gtk_widget_show(event_box1);
/* создаем надпись */
label = gtk_label_new(" -== Click here to exit. ==- ");
/* помещаем надпись в контейнер event_box */
gtk_container_add(GTK_CONTAINER(event_box1), label);
/* отображаем окно */
gtk_widget_show(label);
/* устанавливаем реакцию GtkLabel на щелчок */
/*(при щелчке - выход) */
gtk_widget_set_events(event_box1, GDK_BUTTON_PRESS_MASK);
gtk_signal_connect(GTK_OBJECT(event_box1), "button_press_event",
GTK_SIGNAL_FUNC(gtk_exit), NULL);
gtk_widget_realize(event_box1);
/* изменяем курсор над надписью — курсор превратится в руку */
gdk_window_set_cursor(event_box1->window, gdk_cursor_new(GDK_HAND1));
/* отображаем окно */
gtk_widget_show(window1);
gtk_main();
return 0;
}
Откомпилируйте и запустите программу. Над надписью указатель мыши должен принять вид руки (как в браузере над ссылкой). При щелчке на надписи программа будет закрыта.
Если вы хотите создать надпись на русском языке, то подключите заголовок locale.h и вызовите функцию:
Setlocale(LC_ALL, "ru_RU.KOI8-R");
Конечно, значение локали у вас может быть другим. Эту функцию нужно вызвать ДО инициализации GTK+.
Рис. 23.2. Надпись.
- 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.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
- 331.
- 332.
- 333.
- 334.
- 335.
- 336.
- 337.
- 338.
- 339.
- 340.
- 341.
- 342.
- 343.
- 344.
- 345.
- 346.
- 347.
- 348.
- 349.
- 350.
- 351.
- 352.
- 353.
- 354.
- 355.
- 356.
- 357.
- 358.
- 359.
- 360.
- 361.
- 362.
- 363.
- 364.
- 365.
- 366.
- 367.
- 368.
- 369.
- 370.
- 371.
- 372.
- 373.
- 374.
- 375.
- 376.
- 377.
- 378.
- 379.
- 380.
- 381.
- 382.
- 383.
- 384.
- 385.
- 386.
- 387.
- 388.
- 389.
- 390.
- 391.
- 392.
- 393.
- 394.
- 395.
- 396.
- 397.
- 398.
- 399.
- 400.
- 401.
- 402.
- 403.
- 404.
- 405.
- 406.
- 407.
- 408.
- 409.
- 410.
- 411.
- 412.
- 413.
- 414.
- 415.
- 416.
- 417.
- 418.
- 419.
- 420.
- 421.
- 422.
- 423.
- 424.
- 425.
- 426.
- 427.
- 428.
- 429.
- 430.
- 431.
- 432.
- 433.
- 434.
- 435.
- 436.
- 437.
- 438.
- 439.
- 440.
- 441.
- 442.
- 443.
- 444.
- 445.
- 446.
- 447.
- 448.
- 449.
- 450.
- 451.
- 452.
- 453.
- 454.
- 455.
- 456.
- 457.
- 458.
- 459.
- 460.
- 461.
- 462.
- 463.
- 464.
- 465.
- 466.
- 467.
- 468.
- 469.
- 470.
- 471.