Глава 16: Метки и Базовые Контейнеры Компоновки (GtkBox) ======================================================== В этой главе мы углубимся в создание более сложных графических интерфейсов, изучив два ключевых элемента: **текстовые метки** с помощью `GtkLabel` и **контейнеры для компоновки виджетов** (`GtkBox`). Вы научитесь отображать статический текст и эффективно располагать несколько элементов интерфейса по вертикали или горизонтали. --- GtkLabel: Отображение Текста ---------------------------- :doc: `GtkLabel ` — это фундаментальный виджет GTK, предназначенный для отображения одной или нескольких строк текста. Он широко используется для: * **Подписей** к другим элементам управления (например, "Имя пользователя:"). * **Заголовков** разделов или окон. * **Пояснений** и инструкций для пользователя. * **Отображения динамически изменяющегося текста** (например, счётчика, статуса). Текст в `GtkLabel` может быть простым или форматированным с использованием **Pango Markup**, что позволяет применять жирный шрифт, курсив, изменять цвет и размер текста. ### Пример 16.1: Простое окно с меткой Название исходного файла: `label_example.c` Этот пример демонстрирует базовое использование `GtkLabel` для отображения простого приветствия в центре окна. .. code-block:: c // label_example.c #include 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`` является контейнером для одного виджета, добавленная метка будет автоматически центрирована в окне. --- GtkBox: Горизонтальная и Вертикальная Компоновка ------------------------------------------------- :doc: `GtkBox ` — это один из наиболее часто используемых **контейнеров компоновки** в 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``, который содержит метку и кнопку, расположенные друг под другом. .. code-block:: c // layout_vbox_example.c #include // Функция-обработчик для кнопки (из предыдущей главы) 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``, который содержит три кнопки, расположенные рядом. .. code-block:: c // layout_hbox_example.c #include // Единый обработчик для всех кнопок 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`). Для сборки используйте следующую команду, адаптируя её для каждого файла: .. code-block:: bash # Для примера с 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`_ - Репозиторий с примерами кода из этого руководства. --- .. _GtkLabel: https://docs.gtk.org/gtk3/class.Label.html .. _GtkBox: https://docs.gtk.org/gtk3/class.Box.html .. _GTK3 Containers: https://developer.gnome.org/gtk3/stable/ch03.html .. _C_GUI_Handbook GitHub: https://github.com/AIDevelopersMonster/C_GUI_Handbook