W tym samouczku stworzymy najbardziej podstawowy program demonstrujący widget GtkTreeView w akcji. W tym celu otwórz program glade i utwórz następujący interfejs użytkownika:
Przy dodawaniu widoku drzewa wyskoczy okienko dialogowe Tworzenie GtkTreeView. Pole Model TreeView pozostawiamy puste i wciskamy przycisk Utwórz (model utworzymy i wypełnimy w kodzie programu):
A oto kod programu:
#include <gtk/gtk.h>
enum {
COL_NAME = 0,
COL_AGE,
NUM_COLS
};
GtkBuilder *builder;
static GtkTreeModel *create_and_fill_model(void) {
GtkListStore *store;
GtkTreeIter iter;
store = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_UINT);
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, COL_NAME, "Jan Kowalski", COL_AGE, 21, -1);
gtk_list_store_append(store, &iter);
gtk_list_store_set (store, &iter, COL_NAME, "Piotr Nowak", COL_AGE, 43, -1);
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, COL_NAME, "Paweł Szczepanek", COL_AGE, 91, -1);
return GTK_TREE_MODEL(store);
}
static GtkWidget *init_view_and_model(void) {
GtkCellRenderer *renderer;
GtkTreeModel *model;
GtkWidget *treeview;
treeview = GTK_WIDGET(gtk_builder_get_object(builder, "TreeView"));
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1, "Nazwisko", renderer, "text", COL_NAME, NULL);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1, "Wiek", renderer, "text", COL_AGE, NULL);
model = create_and_fill_model();
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), model);
g_object_unref(model);
return treeview;
}
int main(int argc, char **argv) {
GtkWidget *window;
GtkWidget *treeview;
GError *error;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
error = NULL;
gtk_builder_add_from_file(builder, "02_prosta_lista.ui", &error);
if (error) {
g_print("Wystąpił błąd: %s\n", error->message);
g_error_free(error);
return -1;
}
window = GTK_WIDGET(gtk_builder_get_object(builder, "MainWindow"));
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
init_view_and_model();
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Kompilujemy go poleceniem:
g++ -o prosta_lista prosta_lista.cpp `pkg-config --cflags --libs gtk+-3.0`
Jak widzimy, w tworzeniu kontrolki GtkTreeView wykorzystywane są następujące komponenty: model, iterator, renderer, kolumna i widok. Dzieje się tak dlatego gdyż GtkTreeView stosuje wzorzec projektowy MVC (Model / View / Controller) a więc odseparowuje dane od sposobu prezentowania ich na ekranie. Dane rozmaitych typów (łańcuchy znaków, liczby, obrazki, itp.) są przechowywane w modelu. Następnie widok określa, które dane ma wyświetlić, gdzie ma je wyświetlić i jak ma je wyświetlić. Zaletą takiego podejścia jest fakt, że te same dane można wykorzystać do wyświetlenia w wielu widokach. Jeśli dane w modelu zostaną zaktualizowane, wszystkie widoki również zostaną zaktualizowane.
Przystąpmy teraz do analizy kodu przykładowego programu. W metodzie main inicjalizujemy bibliotekę gtk, wczytujemy interfejs użytkownika oraz inicjalizujemy i wypełniamy danymi kontrolkę GtkTreeView. Cała esencja kodu znajduje się w funkcjach init_view_and_model() oraz create_and_fill_model(). Pierwsza z nich odpowiada za utworzenie kolumn oraz zdefiniowanie jakie dane i w jaki sposób będą w nich wyświetlane. W drugiej natomiast tworzymy nowy model i wypełniamy go danymi.
Zacznijmy od sposobu utworzenia magazynu danych w funkcji create_and_fill_model(). Ponieważ chcemy utworzyć dane prezentowane w postaci listy wartości, używamy zmiennej typu GtkListStore* oraz tworzącej ją funkcji gtk_list_store_new:
store = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_UINT);
Funkcja ta w pierwszym parametrze przyjmuje ilość kolumn dla jednego wiersza magazynu danych a następnie listę wartości określających typ przechowywanej wartości w każdej z tych kolumn. Dostępne są następujące typy:
Dane do magazynu danych dodajemy przy pomocy poniższych dwóch funkcji:
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, COL_NAME, "Jan Kowalski", COL_AGE, 21, -1);
gtk_list_store_append() dodaje nowy wiersz do magazynu danych i w zmiennej iter typu GtkTreeIter przechowuje wskaźnik na ten wiersz. Druga funkcja gtk_list_store_set() wypełnia nowo dodany wiersz danymi. Jako pierwsze dwa argumenty tej funkcji przekazujemy wskaźniki do magazynu danych oraz do nowo dodanego wiersza. W następnej kolejności przekazujemy parami dane, które chcemy ustawić. Pierwszą wartością w takiej parze jest numer kolumny natomiast drugą właściwe dane, które chcemy w tej kolumnie przechowywać. Na końcu umieszczamy wartość -1 oznaczającą koniec przekazywania danych.
Na koniec funkcja create_and_fill_model zwraca utworzony magazyn danych zrzutowany na typ GtkTreeModel* który jest typem interfejsu do danych wykorzystywanym w kontrolce GtkTreeView.
Przyjrzyjmy się teraz sposobowi utworzenia kolumn w widgecie GtkTreeView. Najpierw pobieramy wskaźnik na kontrolkę TreeView. Następnie, aby dodać nową kolumnę wywołujemy następujące funkcje:
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1, "Nazwisko", renderer, "text", COL_NAME, NULL);
Najpierw tworzymy obiekt typu GtkCellRenderer przy pomocy wywołania gtk_cell_renderer_text_new().
Obiekt ten będzie wyświetlał wartość przypisaną do jego atrybutu "text". Następnie wywołujemy
funkcję gtk_tree_view_insert_column_with_attributes aby dodać właściwą kolumnę, przypisać jej
obiekt renderera oraz ustawić atrybut "text" tego renderera. Jako pierwszy argument tej funkcji przekazujemy
wskaźnik na kontrolkę GtkTreeView. Drugi argument to pozycja kolumny w kontrolce, -1 oznacza, że kolumna ma zostać
dodana na końcu. Trzeci argument to tekst nagłówka kolumny. Czwarty argument to renderer, który ma zostać użyty
do wyświetlania wartości w tej kolumnie. Kolejne argumenty to lista atrybutów i przypisanych im wartości zakończona
wartością NULL. W tym przypadku atrybutowi "text" przypisujemy wartość znajdującą się na pozycji COL_NAME (0)
w magazynie danych.
Identycznie wygląda sprawa z dodaniem kolumny "Wiek". Na uwagę zasługuje tu fakt, że renderer komórki podczas wyświetlania
wartości automatycznie przekonwertuje sobie wartość typu G_TYPE_UINT na łańcuch znaków.
Na koniec przypisujemy model do widgetu GtkTreeView i zmniejszamy liczbę referencji do tego modelu, żeby zajmowana przez niego pamięć została zwolniona po zniszczeniu GtkTreeView:
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), model);
g_object_unref(model);