Сьогодні ми будемо розглядати віконну систему 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 ;
}
Компілюємо і запускаємо програму за допомогою команд:
І отримуємо результат:
Контейнери
В основному, усі віджети бібліотеки GTK (включно з головними вікнами) можуть мати у собі, в якості дочірнього внутрішнього, тільки один елемент — інший віджет. Але один спеціальний віджет може мати у собі множину віджетів. Цей елемент називається Box або контейнер і створюється він за допомогою функції:
GtkWidget* gtk_box_new (GtkOrientation orientation, gint spacing) ;
Передаються їй два параметри
- orientation — цей параметр встановлює орієнтацію площини розміщення елементів в контейнері. Тобто ви можете встановити, як елементи будуть розміщуватись відносно один-одного — горизонтально, чи вертикально (GTK_ORIENTATION_HORIZONTAL або GTK_ORIENTATION_VERTICAL відповідно).
- 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) ;
Їм передаються наступні параметри.
- GtkBox* box — вказівник на контейнер створений за допомогою функції gtk_box_new.
- GtkWidget *child — вказівник на дочірній елемент, тобто віджет, який буде міститися в контейнері.
- gboolean expand — TRUE або FALSE, для того щоб віджет займав увесь вільний простір по відношенню до інших дочірніх віджетів контейнеру (інші віджети будуть займати мінімальну кількість простору).
- gboolean fill — якщо expand встановлено в TRUE, то даний елемент вирішує, чи сам віджет буде займати весь вільний простір (TRUE), або він буде мати велику відстань до інших віджетів (FALSE).
- 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 ;
}
Після компілювання і запуску даної програми, ми можемо отримати:
Як бачимо, поле для введення розмістилось практично на всю довжину вікна, а кнопка має мінімальну ширину у піксельну довжину її напису.
Створення обробників
Наступна програма містить в собі обробники подій.
#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) ;
}
Після компілювання і виконання даної програми ми отримаємо:
Оголошення обробників слід виконувати згідно з контексту (тобто віджета і його події), інакше ви не зможете коректно отримувати дані. Документацію про контекст події слід шукати в документації по віджетам.
Отже сьогодні ми навчились писати віконні програми для GNOME/GTK. Не забудьте відвідати домашню сторінку для розробників GTK-програм за адресою developer.gnome.org, на якому Ви знайдете велику кількість документації про різноманітні віджети, які будуть потрібні Вашій програмі.