8. Глава 20: Деревья и Таблицы (GtkTreeView)
В этой главе мы погрузимся в GtkTreeView — один из самых универсальных и мощных виджетов GTK. Он позволяет отображать данные как в табличной форме (строки и столбцы), так и в иерархической (древовидной) структуре. GtkTreeView не хранит данные сам по себе, а выступает в роли «представления», отображая данные из внешней модели данных.
Использование GtkTreeView требует понимания следующих ключевых компонентов:
GtkTreeView: Сам виджет, который отображает данные.
GtkListStore: Модель данных для табличной структуры (списка строк). Каждая строка в GtkListStore не имеет родителей или дочерних элементов.
GtkTreeStore: Модель данных для иерархической (древовидной) структуры. Каждая строка может иметь дочерние элементы, формируя дерево.
GtkTreeViewColumn: Определяет отдельный столбец в GtkTreeView, включая его заголовок и способ отображения данных.
GtkCellRenderer: Абстрактный базовый класс для объектов, которые отвечают за отрисовку (рендеринг) содержимого отдельной ячейки в GtkTreeView. Например, GtkCellRendererText отрисовывает текст, GtkCellRendererPixbuf — изображения.
—
### Пример 20.1: Табличный GtkTreeView с GtkListStore
Название исходного файла: treeview_liststore_example.c
В этом примере мы создадим простую таблицу, отображающую данные о пользователях (Имя, Возраст, Профессия). Для хранения этих данных мы будем использовать GtkListStore, поскольку данные здесь не имеют иерархической структуры.
#include <gtk/gtk.h> // Подключаем основную библиотеку GTK.
// Перечисление для удобства определения индексов столбцов в GtkListStore.
// Это делает код более читабельным и менее подверженным ошибкам.
enum {
COL_NAME, // Столбец для имени (G_TYPE_STRING)
COL_AGE, // Столбец для возраста (G_TYPE_INT)
COL_OCCUPATION, // Столбец для профессии (G_TYPE_STRING)
NUM_COLS // Общее количество столбцов
};
/**
* @brief Главная функция программы.
*
* Инициализирует GTK, создает окно, GtkListStore (модель данных),
* наполняет его данными, создает GtkTreeView, настраивает его колонки
* и запускает главный цикл событий GTK.
*
* @param argc Количество аргументов командной строки.
* @param argv Массив строк аргументов командной строки.
* @return Код завершения программы.
*/
int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
// 1. Создание главного окна.
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Пример GtkTreeView (ListStore)");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// 2. Создание GtkListStore (модели данных).
// gtk_list_store_new() создает новую модель данных.
// Первый аргумент: NUM_COLS - общее количество столбцов.
// Остальные аргументы: типы данных для каждого столбца.
GtkListStore *store = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
GtkTreeIter iter; // Итератор для указания на текущую строку.
// 3. Добавление данных в GtkListStore.
// Каждая пара gtk_list_store_append() и gtk_list_store_set() добавляет одну строку.
// Первая строка: Алиса, 30, Инженер
gtk_list_store_append(store, &iter); // Добавляем новую строку и получаем итератор.
gtk_list_store_set(store, &iter,
COL_NAME, "Алиса", // Устанавливаем имя
COL_AGE, 30, // Устанавливаем возраст
COL_OCCUPATION, "Инженер", // Устанавливаем профессию
-1); // -1 означает конец списка аргументов.
// Вторая строка: Боб, 25, Дизайнер
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
COL_NAME, "Боб",
COL_AGE, 25,
COL_OCCUPATION, "Дизайнер",
-1);
// Третья строка: Анна, 35, Врач
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
COL_NAME, "Анна",
COL_AGE, 35,
COL_OCCUPATION, "Врач",
-1);
// Четвертая строка: Чарли, 28, Программист
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
COL_NAME, "Чарли",
COL_AGE, 28,
COL_OCCUPATION, "Программист",
-1);
// 4. Создание GtkTreeView с созданной моделью данных.
// GtkTreeView - это виджет-представление, которое отображает данные из GtkTreeModel.
GtkWidget *view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
// Отключаем автоматический инкремент счетчика ссылок на модель.
// GtkTreeView сам увеличивает счетчик ссылок при связывании,
// поэтому мы можем безопасно уменьшить наш.
g_object_unref(store);
// 5. Добавление столбцов (колонок) в GtkTreeView.
// Для каждого столбца в таблице мы создаем GtkTreeViewColumn.
// GtkTreeViewColumn связывает столбец модели с рендерером, который отображает данные.
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
// --- Колонка "Имя" ---
// Создаем рендерер для отображения текста.
renderer = gtk_cell_renderer_text_new();
// Создаем столбец с заголовком "Имя".
// "text", COL_NAME: связываем свойство "text" рендерера с данными из столбца COL_NAME модели.
column = gtk_tree_view_column_new_with_attributes("Имя", renderer, "text", COL_NAME, NULL);
// Добавляем столбец в GtkTreeView.
gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
// Включение сортировки по нажатию на заголовки колонок.
gtk_tree_view_column_set_sort_column_id(column, COL_NAME);
// --- Колонка "Возраст" ---
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("Возраст", renderer, "text", COL_AGE, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
gtk_tree_view_column_set_sort_column_id(column, COL_AGE); // Включение сортировки.
// --- Колонка "Профессия" ---
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes("Профессия", renderer, "text", COL_OCCUPATION, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
gtk_tree_view_column_set_sort_column_id(column, COL_OCCUPATION); // Включение сортировки.
// 6. Размещение GtkTreeView в окне.
// Чтобы GtkTreeView можно было прокручивать, его обычно помещают в GtkScrolledWindow.
GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(scrolled_window), view);
// Устанавливаем политику прокрутки (всегда показывать полосы прокрутки)
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
// Добавляем GtkScrolledWindow в главное окно.
gtk_container_add(GTK_CONTAINER(window), scrolled_window);
// 7. Отображение всех виджетов.
gtk_widget_show_all(window);
// 8. Запуск главного цикла событий GTK.
gtk_main();
return 0;
}
—
### Пример 20.2: Древовидный GtkTreeView с GtkTreeStore
Название исходного файла: treeview_treestore_example.c
В этом примере мы создадим иерархический список, например, дерево категорий и подкатегорий. Для этого используется GtkTreeStore, которая, в отличие от GtkListStore, может хранить дочерние элементы для каждой строки.
#include <gtk/gtk.h> // Подключаем основную библиотеку GTK.
// Перечисление для удобства определения индексов столбцов в GtkTreeStore.
enum {
COL_DISPLAY_TEXT, // Столбец для отображаемого текста
NUM_COLS_TREE // Общее количество столбцов в этой модели
};
/**
* @brief Главная функция программы.
*
* Инициализирует GTK, создает окно, GtkTreeStore (модель данных дерева),
* наполняет его иерархическими данными, создает GtkTreeView, настраивает
* его колонки и запускает главный цикл событий GTK.
*
* @param argc Количество аргументов командной строки.
* @param argv Массив строк аргументов командной строки.
* @return Код завершения программы.
*/
int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
// 1. Создание главного окна.
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Пример GtkTreeView (TreeStore)");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
// 2. Создание GtkTreeStore (модели данных для дерева).
// gtk_tree_store_new() создает иерархическую модель.
// Первый аргумент: NUM_COLS_TREE - количество столбцов.
// Остальные аргументы: типы данных для каждого столбца.
GtkTreeStore *treestore = gtk_tree_store_new(NUM_COLS_TREE, G_TYPE_STRING);
GtkTreeIter parent_iter, child_iter; // Итераторы для родительских и дочерних узлов.
// 3. Добавление данных в GtkTreeStore.
// Данные добавляются иерархически.
// --- Родительская категория: "Животные" ---
// gtk_tree_store_append(treestore, &parent_iter, NULL): добавляет новый корневой узел
// (NULL в качестве родителя) и устанавливает parent_iter на него.
gtk_tree_store_append(treestore, &parent_iter, NULL);
// Устанавливаем текст для корневого узла.
gtk_tree_store_set(treestore, &parent_iter, COL_DISPLAY_TEXT, "Животные", -1);
// Дочерние элементы для "Животные":
// gtk_tree_store_append(treestore, &child_iter, &parent_iter): добавляет новый дочерний узел
// к узлу, на который указывает parent_iter, и устанавливает child_iter на него.
gtk_tree_store_append(treestore, &child_iter, &parent_iter);
gtk_tree_store_set(treestore, &child_iter, COL_DISPLAY_TEXT, "Собака", -1);
gtk_tree_store_append(treestore, &child_iter, &parent_iter);
gtk_tree_store_set(treestore, &child_iter, COL_DISPLAY_TEXT, "Кот", -1);
gtk_tree_store_append(treestore, &child_iter, &parent_iter);
gtk_tree_store_set(treestore, &child_iter, COL_DISPLAY_TEXT, "Птица", -1);
// --- Еще одна родительская категория: "Растения" ---
gtk_tree_store_append(treestore, &parent_iter, NULL);
gtk_tree_store_set(treestore, &parent_iter, COL_DISPLAY_TEXT, "Растения", -1);
// Дочерние элементы для "Растения":
gtk_tree_store_append(treestore, &child_iter, &parent_iter);
gtk_tree_store_set(treestore, &child_iter, COL_DISPLAY_TEXT, "Цветы", -1);
// Подкатегория для "Цветы"
GtkTreeIter sub_child_iter;
gtk_tree_store_append(treestore, &sub_child_iter, &child_iter); // parent_iter теперь - "Цветы"
gtk_tree_store_set(treestore, &sub_child_iter, COL_DISPLAY_TEXT, "Розы", -1);
gtk_tree_store_append(treestore, &sub_child_iter, &child_iter);
gtk_tree_store_set(treestore, &sub_child_iter, COL_DISPLAY_TEXT, "Тюльпаны", -1);
gtk_tree_store_append(treestore, &child_iter, &parent_iter);
gtk_tree_store_set(treestore, &child_iter, COL_DISPLAY_TEXT, "Деревья", -1);
// 4. Создание GtkTreeView с созданной моделью GtkTreeStore.
GtkWidget *tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(treestore));
g_object_unref(treestore); // Уменьшаем счетчик ссылок на модель.
// 5. Добавление колонки в GtkTreeView.
// Для дерева обычно требуется только одна колонка для отображения иерархии.
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
// Заголовок столбца "Категории".
// "text", COL_DISPLAY_TEXT: связываем свойство "text" рендерера с данными из столбца COL_DISPLAY_TEXT.
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("Категории", renderer, "text", COL_DISPLAY_TEXT, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
// Дополнительные настройки для GtkTreeView:
// Разрешить сворачивание/разворачивание узлов.
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(tree), column);
// Разрешить пользователю изменять размер колонок.
gtk_tree_view_column_set_resizable(column, TRUE);
// Автоматически развернуть все корневые узлы при запуске.
// gtk_tree_view_expand_all(GTK_TREE_VIEW(tree));
// 6. Размещение GtkTreeView в окне (через GtkScrolledWindow для прокрутки).
GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_container_add(GTK_CONTAINER(scrolled_window), tree);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_container_add(GTK_CONTAINER(window), scrolled_window);
// 7. Отображение всех виджетов.
gtk_widget_show_all(window);
// 8. Запуск главного цикла событий GTK.
gtk_main();
return 0;
}
—
8.1. Компиляция и Запуск
Сохраните каждый пример кода в соответствующий файл: treeview_liststore_example.c и treeview_treestore_example.c.
### Компиляция:
Для сборки программ используйте следующие команды в терминале, находясь в директории с исходными файлами:
# Для примера с GtkListStore (таблица):
gcc treeview_liststore_example.c -o treeview_liststore_example `pkg-config --cflags --libs gtk+-3.0`
# Для примера с GtkTreeStore (дерево):
gcc treeview_treestore_example.c -o treeview_treestore_example `pkg-config --cflags --libs gtk+-3.0`
### Запуск:
После успешной компиляции вы можете запустить каждое приложение:
# Для примера с GtkListStore:
./treeview_liststore_example
# Для примера с GtkTreeStore:
./treeview_treestore_example
—
8.2. Ожидаемый Результат
- `treeview_liststore_example`:
Откроется окно с заголовком «Пример GtkTreeView (ListStore)».
Внутри окна вы увидите таблицу с тремя столбцами: «Имя», «Возраст», «Профессия».
Таблица будет содержать четыре строки данных, которые мы добавили в GtkListStore (Алиса, Боб, Анна, Чарли).
Вы сможете изменять размер колонок и сортировать данные, кликая по заголовкам.
- `treeview_treestore_example`:
Откроется окно с заголовком «Пример GtkTreeView (TreeStore)».
Внутри окна вы увидите древовидную структуру (иерархический список).
Будут корневые узлы «Животные» и «Растения».
Вы сможете раскрывать «Животные», чтобы увидеть «Собака», «Кот», «Птица».
Вы сможете раскрывать «Растения», а затем «Цветы», чтобы увидеть «Розы» и «Тюльпаны».
—
8.3. Дополнительные Ресурсы
—