3. Глава 15: Кнопки и обработка событий

В этой главе мы углубимся в работу с одним из самых фундаментальных элементов графического интерфейса — кнопками. Мы научимся создавать кнопки с текстом, размещать их в окне и, самое главное, обрабатывать пользовательские действия, такие как клики мышью. Это важный шаг в освоении событийно-ориентированного программирования с использованием библиотеки GTK, которое является краеугольным камнем любого интерактивного приложения.

3.1. Что вы узнаете в этой главе:

  • Как создать базовую кнопку с текстовой меткой.

  • Что такое «сигналы» в GTK и как они являются основой обработки событий.

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

  • Как написать функцию-обработчик (callback) для реакции на клик по кнопке.

  • Как использовать `g_print()` для вывода отладочной информации.

### Основы взаимодействия: Сигналы и Слоты

Как мы уже кратко упоминали в предыдущих главах, GTK использует событийно-ориентированную модель. Это означает, что программа не выполняет команды строго по порядку, а реагирует на «события», происходящие в интерфейсе. Ядро этой модели в GTK — это система «сигналов и слотов» (signals and slots), реализованная через GObject.

  • Сигнал (Signal): Это уведомление, которое «испускает» виджет (или любой другой GObject) в ответ на определённое событие. Например, когда пользователь нажимает на кнопку, кнопка испускает сигнал «clicked». Когда окно закрывается, оно испускает сигнал «destroy».

  • Слот (Slot) / Обработчик события (Callback Function): Это обычная функция на языке C, которую вы пишете. Вы «подключаете» эту функцию к определённому сигналу определённого виджета. Когда виджет испускает сигнал, GTK автоматически вызывает (или «активирует») все подключенные к этому сигналу функции-обработчики.

Механизм g_signal_connect(), который мы использовали ранее для закрытия окна, является центральным для этой системы. Он позволяет установить связь между сигналом, исходящим от виджета, и вашей функцией, которая должна быть вызвана в ответ.

### Пример: Простое окно с кнопкой и выводом в консоль

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

Этот пример покажет, как создать окно с одной кнопкой. При каждом нажатии на кнопку в терминале будет появляться сообщение.

#include <gtk/gtk.h> // Подключаем основную библиотеку GTK.

/**
 * @brief Callback-функция, которая будет вызвана при нажатии кнопки.
 *
 * Эта функция просто выводит сообщение в консоль, демонстрируя,
 * что сигнал "clicked" был успешно обработан.
 *
 * @param widget Указатель на GtkWidget, который испустил сигнал (в данном случае, GtkButton).
 * Хотя мы не используем его явно в этой функции, он всегда передаётся.
 * @param data   Универсальный указатель на пользовательские данные.
 * В этом примере мы передаём NULL, так как никаких дополнительных данных не требуется.
 */
static void on_button_clicked(GtkWidget *widget, gpointer data) {
    // g_print() - это функция из библиотеки GLib (основа GTK),
    // аналогичная printf(), но с лучшей поддержкой для вывода в консоль
    // в GUI-приложениях GTK (особенно при запуске из IDE или без прямого терминала).
    g_print("Кнопка 'Нажми меня' была нажата!\n");
}

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

    // 2. Создание главного окна.
    // Создаётся новое окно верхнего уровня.
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    // 3. Настройка окна.
    // Устанавливаем заголовок окна.
    gtk_window_set_title(GTK_WINDOW(window), "Пример кнопки");
    // Устанавливаем размер окна по умолчанию.
    gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
    // Центрируем окно на экране.
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

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

    // 5. Создание кнопки.
    // Создаём новую кнопку с текстовой меткой.
    GtkWidget *button = gtk_button_new_with_label("Нажми меня");

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

    // 7. Добавление кнопки в окно.
    // Добавляем созданную кнопку в окно. В GTK3 окна могут содержать
    // только один прямой дочерний виджет.
    gtk_container_add(GTK_CONTAINER(window), button);

    // 8. Отображение всех виджетов.
    // Все виджеты GTK по умолчанию невидимы. Эта функция делает окно
    // и все его дочерние элементы (в данном случае, кнопку) видимыми.
    gtk_widget_show_all(window);

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

    // 10. Завершение программы.
    // Код возврата 0 указывает на успешное выполнение.
    return 0;
}

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

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

Компиляция:

Для сборки программы на Raspberry Pi (или любой другой системе с GTK3) используйте следующую команду в терминале, находясь в директории с исходным файлом:

gcc -o button_example button_example.c `pkg-config --cflags --libs gtk+-3.0`
  • gcc -o button_example button_example.c: Указывает компилятору GCC взять исходный файл button_example.c и создать исполняемый файл с именем button_example.

  • `` pkg-config –cflags –libs gtk+-3.0 : Это очень важная часть. Утилита ``pkg-config автоматически предоставляет GCC все необходимые пути к заголовочным файлам (--cflags) и библиотекам (--libs), которые требуются для успешной компиляции GTK3-приложения.

Запуск:

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

./button_example

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

  • На вашем рабочем столе появится новое окно с заголовком «Пример кнопки».

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

  • При каждом нажатии на эту кнопку в терминале, из которого вы запустили программу, будет появляться строка:

    Кнопка 'Нажми меня' была нажата!
    
  • Вы можете закрыть окно, нажав на кнопку «X» в заголовке окна или используя комбинацию клавиш (например, Alt+F4), что приведёт к корректному завершению программы.