4. Глава 16: Метки и Базовые Контейнеры Компоновки (GtkBox)
В этой главе мы углубимся в создание более сложных графических интерфейсов, изучив два ключевых элемента: текстовые метки с помощью GtkLabel и контейнеры для компоновки виджетов (GtkBox). Вы научитесь отображать статический текст и эффективно располагать несколько элементов интерфейса по вертикали или горизонтали.
—
4.1. GtkLabel: Отображение Текста
- doc:
GtkLabel <https://docs.gtk.org/gtk3/class.Label.html> — это фундаментальный виджет GTK, предназначенный для отображения одной или нескольких строк текста. Он широко используется для:
Подписей к другим элементам управления (например, «Имя пользователя:»).
Заголовков разделов или окон.
Пояснений и инструкций для пользователя.
Отображения динамически изменяющегося текста (например, счётчика, статуса).
Текст в GtkLabel может быть простым или форматированным с использованием Pango Markup, что позволяет применять жирный шрифт, курсив, изменять цвет и размер текста.
### Пример 16.1: Простое окно с меткой
Название исходного файла: label_example.c
Этот пример демонстрирует базовое использование GtkLabel для отображения простого приветствия в центре окна.
// label_example.c
#include <gtk/gtk.h>
int main(int argc, char *argv[]) {
// Инициализация GTK
gtk_init(&argc, &argv);
// Создание главного окна приложения
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Пример с GtkLabel");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 100);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
// Подключение сигнала "destroy" для корректного завершения приложения
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// Создание метки с заданным текстом
// Обратите внимание на использование русской буквы 'П'. Убедитесь, что ваш файл сохранён в UTF-8!
GtkWidget *label = gtk_label_new("Привет, GTK!");
// Добавление метки в окно
// Поскольку окно может иметь только один прямой дочерний элемент,
// метка будет размещена по центру окна по умолчанию.
gtk_container_add(GTK_CONTAINER(window), label);
// Отображение всех виджетов
gtk_widget_show_all(window);
// Запуск главного цикла событий GTK
gtk_main();
return 0; // Возвращаем код успешного завершения
}
Комментарии к коду:
gtk_label_new("Привет, GTK!");
: Эта функция создаёт новый виджетGtkLabel
с указанным текстом. Важно, чтобы исходный файл был сохранен в кодировке UTF-8, если вы используете нелатинские символы (например, русские буквы), иначе могут возникнуть предупреждения Pango или некорректное отображение.gtk_container_add(GTK_CONTAINER(window), label);
: В данном случае, посколькуGtkWindow
является контейнером для одного виджета, добавленная метка будет автоматически центрирована в окне.
—
4.2. GtkBox: Горизонтальная и Вертикальная Компоновка
- doc:
GtkBox <https://docs.gtk.org/gtk3/class.Box.html> — это один из наиболее часто используемых контейнеров компоновки в GTK. Он позволяет размещать несколько виджетов в одном ряду — либо по горизонтали (
GTK_ORIENTATION_HORIZONTAL
), либо по вертикали (GTK_ORIENTATION_VERTICAL
).
GtkBox
упрощает создание линейных интерфейсов, будь то строка кнопок или столбец полей ввода.
### Основные функции GtkBox:
gtk_box_new(GtkOrientation orientation, int spacing)
: Создаёт новый контейнерGtkBox
.orientation
: Определяет направление размещения виджетов. Может бытьGTK_ORIENTATION_HORIZONTAL
илиGTK_ORIENTATION_VERTICAL
.spacing
: Определяет фиксированный отступ (в пикселях) между соседними виджетами внутри бокса.
gtk_box_pack_start(GtkBox *box, GtkWidget *child, gboolean expand, gboolean fill, guint padding)
: Добавляет дочерний виджет в начало (или верх/лево) бокса.box
: Указатель на контейнерGtkBox
.child
: Виджет, который нужно добавить.expand
: Если TRUE, виджет будет занимать максимально доступное пространство в том направлении, в котором он «растёт» (для вертикального бокса — по вертикали, для горизонтального — по горизонтали), если оно доступно. Если FALSE, он займёт только минимально необходимое пространство.fill
: Если TRUE, виджет будет заполнять всё доступное пространство в своём сегменте бокса. Если FALSE, он будет использовать только свой естественный размер, а остальное пространство будет пустым.padding
: Дополнительный отступ (в пикселях), который будет добавлен вокруг этого конкретного виджета внутри бокса. Этот отступ добавляется к spacing всего бокса.
gtk_box_pack_end(GtkBox *box, GtkWidget *child, gboolean expand, gboolean fill, guint padding)
: Аналогичноgtk_box_pack_start
, но добавляет виджет в конец (или низ/право) бокса.
### Пример 16.2: Окно с вертикальным GtkBox
Название исходного файла: layout_vbox_example.c
Этот пример демонстрирует создание вертикального GtkBox
, который содержит метку и кнопку, расположенные друг под другом.
// layout_vbox_example.c
#include <gtk/gtk.h>
// Функция-обработчик для кнопки (из предыдущей главы)
static void on_button_clicked(GtkWidget *widget, gpointer data) {
g_print("Кнопка в GtkBox нажата!\n");
}
int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkBox Вертикальный Пример");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 150);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// 1. Создание вертикального контейнера GtkBox с отступом 10 пикселей между элементами.
// GTK_ORIENTATION_VERTICAL указывает, что виджеты будут располагаться друг под другом.
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
// 2. Создание дочерних виджетов: метки и кнопки.
GtkWidget *label = gtk_label_new("Это метка внутри VBox");
GtkWidget *button = gtk_button_new_with_label("Нажми меня!");
// 3. Подключение обработчика для кнопки.
g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), NULL);
// 4. Добавление виджетов в GtkBox с использованием gtk_box_pack_start.
// - expand: TRUE - виджет может расширяться, занимая доступное пространство.
// - fill: TRUE - виджет будет заполнять своё выделенное пространство.
// - padding: 5 - дополнительный отступ в 5 пикселей вокруг этого виджета.
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 5);
gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 5);
// 5. Добавление GtkBox в окно.
// Теперь окно содержит не метку или кнопку напрямую, а контейнер GtkBox,
// который уже содержит метку и кнопку.
gtk_container_add(GTK_CONTAINER(window), vbox);
// 6. Отображение всех виджетов.
gtk_widget_show_all(window);
// 7. Запуск главного цикла GTK.
gtk_main();
return 0;
}
—
### Пример 16.3: Окно с горизонтальным GtkBox
Название исходного файла: layout_hbox_example.c
Этот пример демонстрирует создание горизонтального GtkBox
, который содержит три кнопки, расположенные рядом.
// layout_hbox_example.c
#include <gtk/gtk.h>
// Единый обработчик для всех кнопок
static void on_generic_button_clicked(GtkWidget *widget, gpointer data) {
// Получаем текст кнопки, чтобы вывести, какая именно кнопка была нажата
const char *button_label = gtk_button_get_label(GTK_BUTTON(widget));
g_print("Нажата кнопка: %s\n", button_label);
}
int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "GtkBox Горизонтальный Пример");
gtk_window_set_default_size(GTK_WINDOW(window), 450, 100);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// 1. Создание горизонтального контейнера GtkBox с отступом 5 пикселей.
GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
// 2. Создание трёх кнопок.
GtkWidget *button1 = gtk_button_new_with_label("Кнопка 1");
GtkWidget *button2 = gtk_button_new_with_label("Кнопка 2");
GtkWidget *button3 = gtk_button_new_with_label("Кнопка 3");
// 3. Подключение обработчика к каждой кнопке.
// Все кнопки будут использовать одну и ту же функцию обработчика.
g_signal_connect(button1, "clicked", G_CALLBACK(on_generic_button_clicked), NULL);
g_signal_connect(button2, "clicked", G_CALLBACK(on_generic_button_clicked), NULL);
g_signal_connect(button3, "clicked", G_CALLBACK(on_generic_button_clicked), NULL);
// 4. Добавление кнопок в GtkBox.
// Обратите внимание на параметры expand и fill для разных кнопок.
// Кнопка 1: не расширяется, не заполняет, небольшой отступ.
gtk_box_pack_start(GTK_BOX(hbox), button1, FALSE, FALSE, 0);
// Кнопка 2: расширяется и заполняет доступное пространство.
gtk_box_pack_start(GTK_BOX(hbox), button2, TRUE, TRUE, 0);
// Кнопка 3: не расширяется, но заполняет своё пространство, если оно есть.
gtk_box_pack_start(GTK_BOX(hbox), button3, FALSE, TRUE, 0);
// 5. Добавление GtkBox в окно.
gtk_container_add(GTK_CONTAINER(window), hbox);
// 6. Отображение всех виджетов.
gtk_widget_show_all(window);
// 7. Запуск главного цикла GTK.
gtk_main();
return 0;
}
Комментарии к `gtk_box_pack_start` (и `gtk_box_pack_end`):
Эти функции являются ключевыми для управления тем, как виджеты будут себя вести внутри GtkBox:
expand
(gboolean
):TRUE: Виджет будет пытаться занять любое дополнительное пространство, которое остаётся в контейнере GtkBox после того, как все виджеты с expand=FALSE займут своё минимальное место. Если несколько виджетов имеют expand=TRUE, они разделят это дополнительное пространство между собой.
FALSE: Виджет займёт только минимально необходимое пространство. Он не будет расширяться, даже если есть свободное место.
fill
(gboolean
):TRUE: Если виджету выделено больше пространства, чем его естественный размер (например, потому что expand был TRUE или контейнер просто большой), он заполнит всё выделенное ему пространство.
FALSE: Виджет будет использовать только свой естественный размер, даже если ему выделено больше места. Оставшееся пространство внутри его «ячейки» в боксе будет пустым.
padding
(guint
):Дополнительный отступ (в пикселях), который будет добавлен к естественному пространству, занимаемому виджетом. Этот отступ применяется по обе стороны от виджета в направлении ориентации бокса.
Понимание expand и fill критически важно для создания гибких и адаптивных макетов, которые хорошо выглядят при разных размерах окон.
—
### Компиляция и Запуск
Сохраните каждый пример кода в соответствующий файл (например, label_example.c, layout_vbox_example.c, layout_hbox_example.c).
Для сборки используйте следующую команду, адаптируя её для каждого файла:
# Для примера с GtkLabel:
gcc label_example.c -o label_example `pkg-config --cflags --libs gtk+-3.0`
./label_example
# Для примера с вертикальным GtkBox:
gcc layout_vbox_example.c -o layout_vbox_example `pkg-config --cflags --libs gtk+-3.0`
./layout_vbox_example
# Для примера с горизонтальным GtkBox:
gcc layout_hbox_example.c -o layout_hbox_example `pkg-config --cflags --libs gtk+-3.0`
./layout_hbox_example
—
### Ожидаемый результат
`label_example.c`: Откроется небольшое окно с заголовком «Пример с GtkLabel» и по центру будет виден текст «Привет, GTK!».
`layout_vbox_example.c`: Появится окно с заголовком «GtkBox Вертикальный Пример». Внутри будут расположены метка («Это метка внутри VBox») над кнопкой («Нажми меня!»). Между ними будет небольшой отступ. При нажатии кнопки, в терминал будет выведено сообщение.
`layout_hbox_example.c`: Откроется окно «GtkBox Горизонтальный Пример». Внутри будут расположены три кнопки («Кнопка 1», «Кнопка 2», «Кнопка 3») рядом друг с другом. Вы заметите, что «Кнопка 2» занимает больше места, растягиваясь, так как для неё expand и fill установлены в TRUE. При нажатии любой из кнопок, в терминал будет выведено сообщение, указывающее, какая кнопка была нажата.
—
### Дополнительные ресурсы
GtkLabel: Официальная документация GtkLabel.
GtkBox: Официальная документация GtkBox.
GTK3 Containers: Раздел «Containers» в официальной документации GTK3, описывающий различные контейнеры компоновки.
C_GUI_Handbook GitHub - Репозиторий с примерами кода из этого руководства.
—