Сьогодні ми будемо розглядати віконну систему GTK+, а точніше, створення вікон за допомогою даної бібліотеки. Для даної статті, припускається, що Ви користуєтесь ОС GNU/Linux, з встановленим віконним сервером X і віконною системою GNOME, або ж з встановленими бібліотеками сумісності.

Встановлення необхідних програм і компонентів

Перш ніж приступати до створення віконних програм, необхідно створити умови, які будуть цьому сприяти. І найперше, що нам потрібно, це пакунок розробника для GTK — бібліотека libgtk-3-dev (для третьої версії бібліотеки, як Ви, напевне, здогадались). За необхідності також можна встановити пакунок libgtk-3-doc, який містить документацію по бібліотеці і зручний переглядач devhelp. Для встановлення вищевказаних компонентів і програм, можна скористатися наступною командою (для Debian-похідних ОС):
sudo apt-get install libgtk-3-dev libgtk-3-doc devhelp
Для менеджерів пакунків відмінних від apt-get (дистрибутиви Debian, Ubuntu, Linux Mint), потрібно звернутися до документації Вашої ОС. Також обов'язковою умовою являється присутність компілятора і його супутніх компонентів.

Компонування програм з бібліотекою

Для того, щоб Ваша GTK-програма запустилась або просто скомпілювалась, її необхідно скомпонувати з наступним списком бібліотек:
-lgtk-3 -lgdk-3 -latk-1.0 -lgio-2.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo-gobject -lpango-1.0 -lcairo -lgobject-2.0 -lglib-2.0 
І передати ще більше шляхів до необхідних заголовкових файлів. Дану роботу нам спростить корисна програма “pkg-config”, яка виводить конфігурацію системних пакетів. В загальному, команда компіляції Вашого вихідного коду програми у виконуваний файл буде виглядати наступним чином (вихідний код міститься у файлі window.cxx, вихідний виконуваний файл названий “file”):
g++ window.cxx -o file `pkg-config --cflags --libs gtk+-3.0`
Тепер можна приступити до створення вікон.

Головне вікно

Щоб створити головне вікно програми необхідно використовувати функцію gtk_window_new. Даній функції передається тільки один параметр, який відповідає за тип створюваного вікна. В загальному оголошення даної функції виглядає наступним чином:
GtkWidget* gtk_window_new (GtkWindowType type) ;
Саме через це мені і подобається віконна система GTK (принаймні з точки зору програмування) - вона проста у використанні. Їй не потрібно передавати 11 чи цілих 12 параметрів. Всі не зазначені властивості нового вікна встановлюються в їх значення за умовчанням. Якщо вам необхідно встановити яку-небудь властивість вікна — необхідно викликати відповідну функцію. Але повернемось до функції gtk_window_new. В якості параметра їй можна передавати:
  • GTK_WINDOW_TOPLEVEL для нормальних вікон з обрамленням;
  • GTK_WINDOW_POPUP для вікон, які не мають обрамлення, яких не можна максимізувати/мінімізувати, які не отримують клавіатурні події і які ігноруються віконним менеджером.
Зазвичай використовується параметр GTK_WINDOW_TOPLEVEL. Проста програма, яка використовує функцію gtk_window_new і має необхідну структуру, буде виглядати наступним чином.
#include <gtk/gtk.h> /* усе необхідне для GTK*/

/* головна функція програми */
int main (int argc, char** argv) 
{
    /* Дуже важливо! Перед використанням бібліотеки, її необхідно
    ** ініціалізувати, в іншому випадку ваша програма не буде працювати */
    gtk_init (&argc, &argv) ;
    
    /* вказівник на тип GtkWidget, який буде мітити структуру
    ** нашого вікна */
    GtkWidget* window ;
    
    /* створюємо звичайне вікно */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;
    
    /* в добавок встановлюємо заголовок вікна */
    gtk_window_set_title (GTK_WINDOW(window),"Головне вікно програми") ;
    
    /* За допомогою даної функції ми прив'язуємо події до 
    ** спеціальних функцій-обробників.
    ** В даному випадку, ми прив'язуємо до вікна window для події
    ** "destroy" (знищення, закриття) стандартний обробник gtk_main_quit.
    ** Останній параметр передається функції обробнику. */
    g_signal_connect (window, "destroy", G_CALLBACK(gtk_main_quit), NULL) ;
    
    /* показуємо створене вікно */
    gtk_widget_show_all (window) ;
    
    /* Запускаємо головний цикл GTK-програми. Без цього вікно не 
    ** буде реагувати на події*/
    gtk_main () ;
    
    /* знищуємо вікно і його елементи, і вивільняємо пам'ять */
    gtk_widget_destroy (window) ;
    
    /* у разі закриття вікна (спрацювання функції 
    ** gtk_main_quit) - вихід з програми */
    return 0 ;
}
Компілюємо і запускаємо програму за допомогою команд: І отримуємо результат: simple_window_GTK_on_screen

Контейнери

В основному, усі віджети бібліотеки GTK (включно з головними вікнами) можуть мати у собі, в якості дочірнього внутрішнього, тільки один елемент — інший віджет. Але один спеціальний віджет може мати у собі множину віджетів. Цей елемент називається Box або контейнер і створюється він за допомогою функції:
GtkWidget* gtk_box_new (GtkOrientation orientation, gint spacing) ;
Передаються їй два параметри
  1. orientation — цей параметр встановлює орієнтацію площини розміщення елементів в контейнері. Тобто ви можете встановити, як елементи будуть розміщуватись відносно один-одного — горизонтально, чи вертикально (GTK_ORIENTATION_HORIZONTAL або GTK_ORIENTATION_VERTICAL відповідно).
  2. spacing — цей параметр вказує на кількість незаповнених віджетами пікселів за умовчанням, які необхідно розмістити між елементами.
Для подальшого упаковування віджетів в створений контейнер, необхідно використовувати функції gtk_box_pack_start і gtk_box_pack_end (для додавання в початок або в кінець черги відповідно):
void gtk_box_pack_end (GtkBox *box, GtkWidget *child, gboolean expand, gboolean fill, guint padding) ;
                  
void gtk_box_pack_start (GtkBox *box, GtkWidget *child, gboolean expand, gboolean fill, guint padding) ;
Їм передаються наступні параметри.
  1. GtkBox* box — вказівник на контейнер створений за допомогою функції gtk_box_new.
  2. GtkWidget *child — вказівник на дочірній елемент, тобто віджет, який буде міститися в контейнері.
  3. gboolean expand — TRUE або FALSE, для того щоб віджет займав увесь вільний простір по відношенню до інших дочірніх віджетів контейнеру (інші віджети будуть займати мінімальну кількість простору).
  4. gboolean fill — якщо expand встановлено в TRUE, то даний елемент вирішує, чи сам віджет буде займати весь вільний простір (TRUE), або він буде мати велику відстань до інших віджетів (FALSE).
  5. guint padding — Кількість незаповнених даним віджетом пікселів по відношенню до сусідніх.
Проста програма з використанням контейнеру буде виглядати наступним чином:
#include <gtk/gtk.h> /* усе необхідне для GTK*/

/* головна функція програми */
int main (int argc, char** argv) 
{
    /* Дуже важливо! Перед використанням бібліотеки, її необхідно
    ** ініціалізувати, в іншому випадку ваша програма не буде працювати */
    gtk_init (&argc, &argv) ;
    
    /* вказівники на тип GtkWidget  */
    GtkWidget* window, /* коловне вікно */
             * box, /* контейнер */
             * button, /* кнопка */
             * edit ; /* поле для введення */
    
    /* створюємо необхідні примірники віджетів-лементів */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;
    box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5) ;
    button = gtk_button_new_with_label ("Довільна кнопка") ;
    edit = gtk_entry_new () ;
    
    /* в добавок встановлюємо заголовок вікна */
    gtk_window_set_title (GTK_WINDOW(window),"Головне вікно програми") ;
    
    /* упаковуємо елементи */
    gtk_box_pack_start (GTK_BOX(box), button, false, false, 5) ; /* кнопка */
    gtk_box_pack_start (GTK_BOX(box), edit, TRUE, TRUE, 5) ; /* поле */
    gtk_container_add (GTK_CONTAINER(window), box) ; /* контейнер в вікно */
    
    /* За допомогою даної функції ми прив'язуємо події до 
    ** спеціальних функцій-обробників.
    ** В даному випадку, ми прив'язуємо до вікна window для події
    ** "destroy" (знищення, закриття) стандартний обробник gtk_main_quit.
    ** Останній параметр передається функції обробнику. */
    g_signal_connect (window, "destroy", G_CALLBACK(gtk_main_quit), NULL) ;
    
    /* показуємо створене вікно */
    gtk_widget_show_all (window) ;
    
    /* Запускаємо головний цикл GTK-програми. Без цього вікно не 
    ** буде реагувати на події*/
    gtk_main () ;
    
    /* знищуємо вікно і його елементи, і вивільняємо пам'ять */
    gtk_widget_destroy (window) ;
    
    /* у разі закриття вікна (спрацювання функції 
    ** gtk_main_quit) - вихід з програми */
    return 0 ;
}
Після компілювання і запуску даної програми, ми можемо отримати: gtk_window_with_gtkbutton_and_gtkentry Як бачимо, поле для введення розмістилось практично на всю довжину вікна, а кнопка має мінімальну ширину у піксельну довжину її напису.

Створення обробників

Наступна програма містить в собі обробники подій.
#include <gtk/gtk.h> /* усе необхідне для GTK*/

/* вказівники на тип GtkWidget  */
GtkWidget* window, /* коловне вікно */
         * box, /* контейнер */
         * button, /* кнопка */
         * edit, /* поле для введення */
         * dialog ; /* діалогове вікно */

/* оголошуємо обробники події */
void handle_button (GtkButton*, gpointer) ;
void dialog_dest () ;

/* головна функція програми */
int main (int argc, char** argv) 
{
    /* Дуже важливо! Перед використанням бібліотеки, її необхідно
    ** ініціалізувати, в іншому випадку ваша програма не буде працювати */
    gtk_init (&argc, &argv) ;
    
    /* створюємо необхідні примірники віджетів-лементів */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL) ;
    box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5) ;
    button = gtk_button_new_with_label ("Довільна кнопка") ;
    edit = gtk_entry_new () ;
    
    /* в добавок встановлюємо заголовок вікна */
    gtk_window_set_title (GTK_WINDOW(window), "Головне вікно програми") ;
    
    /* упаковуємо елементи */
    gtk_box_pack_start (GTK_BOX(box), button, false, false, 5) ; /* кнопка */
    gtk_box_pack_start (GTK_BOX(box), edit, TRUE, TRUE, 5) ; /* поле */
    gtk_container_add (GTK_CONTAINER(window), box) ; /* контейнер в вікно */
    
    /* За допомогою даної функції ми прив'язуємо події до 
    ** спеціальних функцій-обробників.
    ** В даному випадку, ми прив'язуємо до вікна window для події
    ** "destroy" (знищення, закриття) стандартний обробник gtk_main_quit.
    ** Останній параметр передається функції обробнику. */
    g_signal_connect (window, "destroy", G_CALLBACK(gtk_main_quit), NULL) ;
    g_signal_connect (button, "clicked", G_CALLBACK(handle_button), NULL) ;
    
    /* показуємо створене вікно */
    gtk_widget_show_all (window) ;
    
    /* Запускаємо головний цикл GTK-програми. Без цього вікно не 
    ** буде реагувати на події*/
    gtk_main () ;
    
    /* знищуємо вікно і його елементи, і вивільняємо пам'ять */
    gtk_widget_destroy (window) ;
    
    /* у разі закриття вікна (спрацювання функції 
    ** gtk_main_quit) - вихід з програми */
    return 0 ;
}

/* реалізовуємо обробник події для кнопки вікна */
void handle_button (GtkButton*, gpointer)
{
    /* Отримуємо текст з поля для введення */
    const gchar* p = gtk_entry_get_text (GTK_ENTRY(edit));
    
    /* створюємо діалогове вікно-повідомлення */
    dialog = gtk_message_dialog_new (GTK_WINDOW(window),
                                     GTK_DIALOG_MODAL, 
                                     GTK_MESSAGE_INFO, 
                                     GTK_BUTTONS_OK,
                                     "You`ve enter: %s", p) ;
    
    /* приєднуємо сигнал для діалогу */
    g_signal_connect (dialog, "destroy", G_CALLBACK(dialog_dest), NULL) ;
    /* натискання кнопки "ОК" */
    g_signal_connect (dialog, "response", G_CALLBACK(dialog_dest), NULL) ;
       
    /* показуємо віджет діалогу користувачу */
    gtk_widget_show_all (dialog) ;
}

/* обробник події для знищення вікна діалогу */
void dialog_dest () 
{
    /* вивільняємо пам'ять */
    gtk_widget_destroy (dialog) ;
}
Після компілювання і виконання даної програми ми отримаємо: gtk_message_dialog_with_window Оголошення обробників слід виконувати згідно з контексту (тобто віджета і його події), інакше ви не зможете коректно отримувати дані. Документацію про контекст події слід шукати в документації по віджетам. Отже сьогодні ми навчились писати віконні програми для GNOME/GTK. Не забудьте відвідати домашню сторінку для розробників GTK-програм за адресою developer.gnome.org, на якому Ви знайдете велику кількість документації про різноманітні віджети, які будуть потрібні Вашій програмі.
Категорії: