Глава 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-подобной ОС), установите необходимые пакеты, выполнив следующие команды в терминале: .. code-block:: bash 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``, которое значительно упрощает процесс: .. code-block:: bash 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 автоматически вызывает вашу функцию-обработчик. **Пример подключения обработчика сигнала:** .. code-block:: c 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`` .. code-block:: c #include // Подключаем основную библиотеку 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`` для автоматического подключения необходимых библиотек и заголовочных файлов: .. code-block:: bash gcc simple_gtk_window.c -o gtk_example `pkg-config --cflags --libs gtk+-3.0` **Запуск и Ожидаемый Вывод:** После успешной компиляции вы можете запустить программу: .. code-block:: bash ./gtk_example **Ожидаемый результат:** На экране появится простое пустое окно с заголовком "Пример GTK" и размерами примерно 200x100 пикселей. Окно будет активно до тех пор, пока вы его не закроете. ---