1. Глава 13: Введение в GTK
В этой главе мы начнем изучение библиотеки GTK (GIMP Toolkit) — одного из самых популярных инструментов для создания графического интерфейса пользователя (GUI) в Linux, включая Raspberry Pi. GTK позволяет разрабатывать интерактивные приложения с окнами, кнопками, текстовыми полями и другими элементами, которые мы видим в современных десктопных программах.
Вы узнаете:
Как установить GTK на Raspberry Pi.
Основные принципы архитектуры GTK, включая его событийно-ориентированную модель.
Что такое GObject и почему он является фундаментом для всех элементов GTK.
Как создать простую GTK-программу.
—
### Установка GTK на Raspberry Pi
Прежде чем мы сможем приступить к программированию графических приложений, нам необходимо установить саму библиотеку GTK и необходимые инструменты для компиляции. Эти инструкции актуальны для операционных систем на базе Debian, таких как Raspberry Pi OS.
Чтобы начать работу с GTK на Raspberry Pi Zero 2 W (или любой другой модели Raspberry Pi с Debian-подобной ОС), установите необходимые пакеты, выполнив следующие команды в терминале:
sudo apt update
sudo apt install libgtk-3-dev
sudo apt update
: Обновляет список доступных пакетов и их версий из репозиториев. Это всегда хороший первый шаг перед установкой новых пакетов.sudo apt install libgtk-3-dev
: Устанавливает пакеты разработчика для GTK 3. Пакетlibgtk-3-dev
включает в себя заголовочные файлы и библиотеки, необходимые для компиляции программ, использующих GTK.
Проверьте, что у вас установлены следующие утилиты, которые понадобятся для компиляции программ на C:
pkg-config
: Утилита, которая помогает компилятору найти необходимые пути к заголовочным файлам и библиотекам для GTK. Она автоматизирует процесс указания всех флагов компиляции.gcc
илиclang
: Компилятор языка C.gcc
(GNU Compiler Collection) обычно установлен по умолчанию в большинстве дистрибутивов Linux.
Для компиляции программ с GTK:
После написания вашей GTK-программы на C, вы будете компилировать её с помощью следующей команды. Обратите внимание на использование pkg-config
, которое значительно упрощает процесс:
gcc main.c -o app `pkg-config --cflags --libs gtk+-3.0`
gcc main.c
: Указывает компилятору GCC скомпилировать исходный файлmain.c
.-o app
: Создает исполняемый файл с именемapp
. Вы можете выбрать любое другое имя.` `pkg-config --cflags --libs gtk+-3.0` `
: Это ключевая часть. Командаpkg-config
запрашивает у системы все необходимые флаги компилятора (--cflags
для заголовочных файлов) и флаги компоновщика (--libs
для библиотек), которые нужны для сборки приложения, использующего GTK 3. Результат этой команды (например,-I/usr/include/gtk-3.0 ... -lgtk-3 -lgdk-3 ...
) вставляется в командуgcc
перед её выполнением.
—
### Архитектура событий
GTK, как и большинство современных графических библиотек, использует событийно-ориентированную модель программирования. Это означает, что ваш код не выполняет действия линейно от начала до конца, а вместо этого «ждёт» наступления определенных событий и реагирует на них.
Событие — это любое действие, которое может произойти в графическом интерфейсе. Примеры событий: клик мышью по кнопке, нажатие клавиши на клавиатуре, изменение размера окна, перемещение курсора и т.д.
Сигнал — это механизм GTK, который «испускается» виджетом (элементом GUI) в ответ на определенное событие. Например, кнопка испускает сигнал
"clicked"
(нажата), когда пользователь по ней кликает.Обработчик события (callback function) — это обычная функция на C, которую вы пишете, и которая будет вызвана GTK, когда соответствующий сигнал будет испущен.
Как это работает:
Вы «подключаете» (connect) ваш обработчик события к сигналу определенного виджета. Когда виджет испускает этот сигнал, GTK автоматически вызывает вашу функцию-обработчик.
Пример подключения обработчика сигнала:
g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), NULL);
g_signal_connect
: Функция GTK для подключения сигнала к обработчику.button
: Указатель на виджет, от которого мы ожидаем сигнал (например, кнопка)."clicked"
: Имя сигнала, который мы хотим перехватить. Это стандартизированные имена сигналов GTK.G_CALLBACK(on_button_clicked)
: МакросG_CALLBACK
используется для безопасного приведения типа вашей функции-обработчика (on_button_clicked
) к ожидаемому типу указателя функции.on_button_clicked
— это имя вашей функции, которая будет вызвана при клике.NULL
: Этоuser_data
(пользовательские данные), которые могут быть переданы в функцию-обработчик. Если данные не нужны, передаетсяNULL
.
Таким образом, каждый виджет может испускать сигналы, которые мы можем «перехватывать» с помощью функции g_signal_connect
, чтобы ваш код мог реагировать на действия пользователя.
—
### Что такое GObject?
GObject — это не просто библиотека, это целая объектно-ориентированная система, реализованная на языке C. Она является фундаментальной основой GTK и многих других библиотек проекта GNOME. GObject предоставляет C возможности, которые обычно ассоциируются с объектно-ориентированными языками, такими как C++ или Java, но при этом сохраняет низкоуровневый контроль C.
Ключевые возможности GObject:
Наследование: Объекты могут наследовать свойства и поведение от родительских объектов. Например, все виджеты GTK наследуются от базового класса
GObject
.Интерфейсы: Позволяют объектам реализовывать определенные наборы функций, не связываясь напрямую с конкретной иерархией наследования.
Сигналы и слоты: Это и есть та самая событийная модель, о которой мы говорили выше. Объекты могут «испускать» сигналы (события), а другие части кода могут «подключаться» к этим сигналам с помощью «слотов» (функций-обработчиков).
Свойства (Properties): Позволяют объектам иметь именованные атрибуты, к которым можно получать доступ и устанавливать их значения универсальным способом.
Ключевые понятия и функции GObject:
GObject
: Это базовый тип всех объектов, построенных с использованием системы GObject, включая все виджеты GTK.g_object_set()
: Функция для установки значений свойств объекта.g_object_get()
: Функция для получения значений свойств объекта.g_signal_connect()
: Как уже было упомянуто, эта функция используется для подключения обработчиков событий (слотов) к сигналам, испускаемым объектами.g_object_unref()
: Функция для уменьшения счетчика ссылок на объект GObject. Когда счетчик ссылок достигает нуля, объект автоматически уничтожается. Это часть системы управления памятью GObject.
Понимание GObject критически важно для эффективной работы с GTK, поскольку именно эта система управляет жизненным циклом виджетов, их взаимодействием и свойствами.
—
### Простой пример GTK-программы
Давайте создадим нашу первую минимальную GTK-программу, которая просто откроет пустое окно с заданным заголовком и размером.
Название файла: simple_gtk_window.c
#include <gtk/gtk.h> // Подключаем основную библиотеку GTK. Этого заголовочного файла достаточно для большинства базовых функций.
// Callback-функция, которая будет вызвана при "активации" приложения.
// 'app' - указатель на объект GtkApplication, представляющий наше приложение.
// 'user_data' - пользовательские данные, которые мы можем передать (в данном случае NULL).
static void on_activate(GtkApplication *app, gpointer user_data) {
// Создаем новое окно приложения. GtkApplicationWindow является основным окном для приложения.
// Оно автоматически связывается с GtkApplication, что обеспечивает правильное управление жизненным циклом.
GtkWidget *window = gtk_application_window_new(app);
// Устанавливаем заголовок окна.
// GTK_WINDOW(window) - это макрос приведения типа, который "приводит"
// общий GtkWidget* к более специфичному GtkWindow*, позволяя использовать функции GtkWindow.
gtk_window_set_title(GTK_WINDOW(window), "Пример GTK");
// Устанавливаем размер окна по умолчанию (ширина, высота).
gtk_window_set_default_size(GTK_WINDOW(window), 200, 100);
// Показываем все виджеты в окне (само окно и все его дочерние элементы, если они есть).
// GTK-виджеты не видны по умолчанию; их нужно явно показать.
gtk_widget_show_all(window);
}
int main(int argc, char **argv) {
// Создаем новый объект GtkApplication.
// "com.example.GTKApp" - уникальный ID приложения (рекомендуется использовать обратный домен).
// G_APPLICATION_FLAGS_NONE - флаги приложения (пока без дополнительных флагов).
GtkApplication *app = gtk_application_new("com.example.GTKApp", G_APPLICATION_FLAGS_NONE);
// Подключаем сигнал "activate" к нашей функции on_activate.
// Сигнал "activate" испускается, когда приложение запускается и готово к отображению своих окон.
// G_CALLBACK() преобразует указатель на функцию в нужный тип для g_signal_connect.
g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
// Запускаем приложение GTK. Эта функция передает управление циклу обработки событий GTK.
// Она блокирует выполнение программы до тех пор, пока приложение не завершится.
int status = g_application_run(G_APPLICATION(app), argc, argv);
// Уменьшаем счетчик ссылок на объект GtkApplication.
// Когда счетчик ссылок достигает нуля, объект освобождается.
// Это важно для предотвращения утечек памяти, так как GtkApplication является GObject.
g_object_unref(app);
return status; // Возвращаем код завершения приложения.
}
—
Компиляция программы:
Сохраните код выше как simple_gtk_window.c
и скомпилируйте его, используя pkg-config
для автоматического подключения необходимых библиотек и заголовочных файлов:
gcc simple_gtk_window.c -o gtk_example `pkg-config --cflags --libs gtk+-3.0`
Запуск и Ожидаемый Вывод:
После успешной компиляции вы можете запустить программу:
./gtk_example
Ожидаемый результат:
На экране появится простое пустое окно с заголовком «Пример GTK» и размерами примерно 200x100 пикселей. Окно будет активно до тех пор, пока вы его не закроете.
—