2. Глава 14: Первый GTK-проект — Окно с кнопкой

В этой главе мы создадим простейшее полноценное приложение с использованием библиотеки GTK — окно с кнопкой «Закрыть», при нажатии на которую программа завершает работу.

Это первый практический шаг в создании интерактивных GUI-приложений с использованием GTK и языка C на Raspberry Pi Zero 2 W (или любой другой системе с установленной GTK3).

2.1. Основные цели этой главы:

  • Понять базовую структуру минимального GTK-приложения.

  • Создать основное окно приложения.

  • Добавить интерактивный элемент — кнопку — в окно.

  • Обработать событие нажатия на кнопку для завершения работы программы.

  • Скомпилировать и запустить ваше первое GUI-приложение.

### Пример кода: Окно с кнопкой «Закрыть»

Название исходного файла: minimal_window.c

В этом примере мы создадим окно, добавим в него кнопку и научим кнопку закрывать приложение при нажатии.

#include <gtk/gtk.h> // Подключаем основную библиотеку GTK. Этого достаточно для большинства базовых функций.

// Callback-функция, которая будет вызвана при нажатии кнопки.
// 'widget' - указатель на виджет, который испустил сигнал (в нашем случае, кнопка).
// 'data' - пользовательские данные, переданные при подключении сигнала.
// В этом случае 'data' будет указателем на наше окно, которое мы хотим закрыть.
static void on_button_clicked(GtkWidget *widget, gpointer data) {
    // Закрываем окно, переданное в качестве 'data'.
    // GTK_WINDOW(data) безопасно приводит gpointer к GtkWindow*, чтобы мы могли использовать gtk_window_close.
    gtk_window_close(GTK_WINDOW(data));
}

int main(int argc, char *argv[]) {
    // Инициализируем GTK. Это обязательный вызов для любой GTK-программы.
    // Он обрабатывает аргументы командной строки, специфичные для GTK, и подготавливает библиотеку к работе.
    gtk_init(&argc, &argv);

    // Создаём новое окно верхнего уровня.
    // GtkWidget* - это базовый тип для всех виджетов GTK.
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); // GTK_WINDOW_TOPLEVEL - это тип окна верхнего уровня

    // Устанавливаем заголовок окна, который будет отображаться в строке заголовка.
    // GTK_WINDOW(window) выполняет приведение типа, поскольку gtk_window_set_title ожидает GtkWindow*.
    gtk_window_set_title(GTK_WINDOW(window), "GTK Minimal App");

    // Устанавливаем размер окна по умолчанию (ширина, высота) в пикселях.
    gtk_window_set_default_size(GTK_WINDOW(window), 300, 100);

    // Устанавливаем положение окна на экране.
    // GTK_WIN_POS_CENTER центрирует окно на экране.
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

    // Подключаем сигнал "destroy" окна к функции gtk_main_quit.
    // Этот сигнал испускается, когда пользователь закрывает окно (например, кликая на "X").
    // gtk_main_quit() останавливает главный цикл GTK, что приводит к завершению программы.
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    // Создаём новую кнопку с текстовой меткой "Закрыть".
    GtkWidget *button = gtk_button_new_with_label("Закрыть");

    // Подключаем сигнал "clicked" от кнопки к нашей функции on_button_clicked.
    // Когда кнопка будет нажата, вызовется on_button_clicked.
    // Мы передаём указатель на 'window' как пользовательские данные (data),
    // чтобы on_button_clicked знала, какое окно закрывать.
    g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), window);

    // Устанавливаем кнопку в качестве дочернего элемента окна.
    // В GTK3 для окна можно установить только один прямой дочерний элемент.
    // gtk_container_add() - это более старый, но часто используемый способ добавления виджетов.
    // В более новых версиях GTK (и для GtkApplicationWindow) используется gtk_window_set_child().
    gtk_container_add(GTK_CONTAINER(window), button);

    // Показываем все виджеты, начиная с окна и включая все его дочерние элементы.
    // Виджеты по умолчанию невидимы, их нужно явно показать.
    gtk_widget_show_all(window); // Показывает и окно, и кнопку внутри него.

    // Запускаем главный цикл событий GTK.
    // Эта функция передает управление библиотеке GTK, которая начинает слушать события (клики, ввод и т.д.).
    // Программа будет выполняться в этом цикле до тех пор, пока не будет вызвана gtk_main_quit().
    gtk_main();

    return 0; // Возвращаем 0, указывая на успешное завершение программы.
}

### Комментарии к коду и пояснения

Давайте разберём основные строки кода и функции, используемые в примере:

  • #include <gtk/gtk.h>: Обязательная строка для всех GTK-приложений. Она включает все необходимые определения и функции для работы с библиотекой.

  • gtk_init(&argc, &argv);: Инициализация GTK. Это первый вызов, который должен быть сделан в любой GTK-программе. Он обрабатывает любые аргументы командной строки, предназначенные для GTK, и подготавливает внутренние структуры библиотеки.

  • GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);: Создаёт новый объект окна верхнего уровня. GtkWidget — это базовый тип для всех видимых элементов в GTK.

  • gtk_window_set_title(GTK_WINDOW(window), "GTK Minimal App");: Устанавливает текст заголовка окна. GTK_WINDOW(window) — это макрос для безопасного приведения общего указателя GtkWidget* к более специфичному типу GtkWindow*, что необходимо для функций, работающих только с окнами.

  • gtk_window_set_default_size(GTK_WINDOW(window), 300, 100);: Задаёт желаемую ширину и высоту окна в пикселях.

  • gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);: Устанавливает начальное положение окна на экране, в данном случае — по центру.

  • g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);: Критически важный момент! Мы подключаем сигнал «destroy» (который испускается, когда окно закрывается пользователем через «крестик» или Alt+F4) к встроенной функции GTK gtk_main_quit(). Эта функция завершает главный цикл событий GTK, позволяя программе корректно завершиться. Если этого не сделать, закрытие окна не остановит программу.

  • GtkWidget *button = gtk_button_new_with_label("Закрыть");: Создаёт новую кнопку с текстовой меткой «Закрыть» внутри неё.

  • g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), window);: Подключаем функцию on_button_clicked к сигналу «clicked» от нашей кнопки. Когда пользователь нажимает на кнопку, GTK вызывает on_button_clicked. В качестве user_data (четвёртый аргумент) мы передаём указатель на наше window, чтобы внутри on_button_clicked мы знали, какое окно закрывать.

  • gtk_container_add(GTK_CONTAINER(window), button);: Добавляет созданную кнопку в окно. В GTK3 большинство контейнеров (включая окна, если они используются как контейнеры для одного виджета) используют gtk_container_add(). GTK_CONTAINER(window) — это ещё одно приведение типа, позволяющее работать с окном как с контейнером.

  • gtk_widget_show_all(window);: Делает окно и все его дочерние элементы (в данном случае, кнопку) видимыми на экране. По умолчанию виджеты создаются невидимыми.

  • gtk_main();: Главный цикл событий GTK. Эта функция запускает бесконечный цикл, который ждёт событий (кликов, нажатий клавиш, системных сообщений и т.д.) и вызывает соответствующие обработчики. Программа остаётся активной в этом цикле до тех пор, пока не будет вызвана gtk_main_quit(), обычно в ответ на сигнал «destroy» окна или клик по нашей кнопке.

### Компиляция и Запуск

Сохраните код в файл с именем minimal_window.c.

Компиляция:

Для сборки используйте следующую команду в терминале, находясь в директории с файлом:

gcc minimal_window.c -o minimal_window `pkg-config --cflags --libs gtk+-3.0`

Обратите внимание, что мы используем gtk+-3.0 в pkg-config, так как наш код написан для GTK3.

Запуск:

После успешной компиляции вы можете запустить ваше первое GTK-приложение:

./minimal_window

### Ожидаемый результат

  • На вашем рабочем столе откроется новое окно с заголовком «GTK Minimal App».

  • Внутри этого окна будет расположена кнопка с надписью «Закрыть».

  • При нажатии на кнопку «Закрыть» (или при закрытии окна стандартным способом, например, через «крестик» в заголовке), программа корректно завершит свою работу, и окно исчезнет.

### Дополнительные ресурсы

  • https://github.com/AIDevelopersMonster/C_GUI_Handbook - Репозиторий с примерами кода из этого руководства.