GtkTreeView 控件
最后修改于 2023 年 10 月 18 日
在本 GTK+ 编程教程中,我们将使用 GtkTreeView
控件。
GtkTreeView
控件是一个复杂的控件,可以用来显示列表和树。该控件可以有一个或多个列。GtkTreeView
控件具有 MVC(模型-视图-控制器)设计架构。这意味着数据与视图是分离的。
还有几个与其他 GtkTreeView
控件一起使用的对象。GtkCellRenderer
决定了数据如何在 GtkTreeViewColumn
中显示。GtkListStore
和 GtkTreeStore
代表模型。它们处理显示在 GtkTreeView
控件中的数据。GtkTreeIter
是一个用于引用 GtkTreeView
中的行的结构。GtkTreeSelection
是一个处理选择的对象。
列表视图
第一个例子将展示一个简单的列表视图。我们显示文本数据。
#include <gtk/gtk.h> enum { LIST_ITEM = 0, N_COLUMNS }; void init_list(GtkWidget *list) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkListStore *store; renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes("List Items", renderer, "text", LIST_ITEM, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(list), column); store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING); gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store)); g_object_unref(store); } void add_to_list(GtkWidget *list, const gchar *str) { GtkListStore *store; GtkTreeIter iter; store = GTK_LIST_STORE(gtk_tree_view_get_model (GTK_TREE_VIEW(list))); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, LIST_ITEM, str, -1); } void on_changed(GtkWidget *widget, gpointer label) { GtkTreeIter iter; GtkTreeModel *model; gchar *value; if (gtk_tree_selection_get_selected( GTK_TREE_SELECTION(widget), &model, &iter)) { gtk_tree_model_get(model, &iter, LIST_ITEM, &value, -1); gtk_label_set_text(GTK_LABEL(label), value); g_free(value); } } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *list; GtkWidget *vbox; GtkWidget *label; GtkTreeSelection *selection; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); list = gtk_tree_view_new(); gtk_window_set_title(GTK_WINDOW(window), "List view"); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_container_set_border_width(GTK_CONTAINER(window), 10); gtk_window_set_default_size(GTK_WINDOW(window), 270, 250); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE); vbox = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 5); label = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5); gtk_container_add(GTK_CONTAINER(window), vbox); init_list(list); add_to_list(list, "Aliens"); add_to_list(list, "Leon"); add_to_list(list, "The Verdict"); add_to_list(list, "North Face"); add_to_list(list, "Der Untergang"); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list)); g_signal_connect(selection, "changed", G_CALLBACK(on_changed), label); g_signal_connect(G_OBJECT (window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(window); gtk_main(); return 0; }
在我们的代码示例中,我们在 GtkTreeView
中显示五个项目。我们只有一列,并且隐藏了该列的标题。我们将一个 GtkVBox
放置在窗口中。该框有两个控件:一个 GtkTreeView
和一个 GtkLabel
。
list = gtk_tree_view_new();
gtk_tree_view_new
函数创建一个新的 GtkTreeView
控件。
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
我们使用 gtk_tree_view_set_headers_visible
函数隐藏列标题。
label = gtk_label_new(""); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
GtkLabel
被创建并放置在 GtkTreeView
下方。
init_list(list);
此函数初始化列表。
renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("List Items", renderer, "text", LIST_ITEM, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
在该函数内部,我们创建一个单列并将其附加到列表中。
store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING); gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
我们创建一个 GtkListStore
(一个模型)并将其设置为列表。
g_object_unref(store);
TreeView
增加了存储对象的引用。我们使用 g_object_unref
函数将引用从 2 减少到 1。然后,该模型将随视图自动销毁。
add_to_list(list, "Aliens");
此用户函数将一个选项添加到列表中。
store = GTK_LIST_STORE(gtk_tree_view_get_model (GTK_TREE_VIEW(list))); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);
在 add_to_list
函数内部,我们使用 gtk_tree_view_get_model
函数调用获取模型。我们追加一个新行,并为该行设置一个值,该行由 GtkTreeIter
对象引用。
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
不需要显式创建 GtkTreeSelection
;它与 GtkTreeView
控件一起自动创建。使用 gtk_tree_view_get_selection
函数调用获取对该控件的引用。
g_signal_connect(selection, "changed", G_CALLBACK(on_changed), label);
GtkTreeSelection
的 changed
信号连接到 on_changed
处理程序。
if (gtk_tree_selection_get_selected( GTK_TREE_SELECTION(widget), &model, &iter)) {
gtk_tree_selection_get_selected
函数将 iter
设置为当前选定的节点。
gtk_tree_model_get(model, &iter, LIST_ITEM, &value, -1);
在处理程序函数内部,我们获取由 iter
对象引用的行中单元格的值。
gtk_label_set_text(GTK_LABEL(label), value);
检索到的值使用 gtk_label_set_text
函数设置为标签。

动态列表视图
第二个例子在前面的基础上增加了额外的功能。我们将能够从列表视图中添加和删除项目。
#include <gtk/gtk.h> enum { LIST_ITEM = 0, N_COLUMNS }; GtkWidget *list; void append_item(GtkWidget *widget, gpointer entry) { GtkListStore *store; GtkTreeIter iter; const gchar *str = gtk_entry_get_text(entry); store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list))); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, LIST_ITEM, str, -1); gtk_entry_set_text(entry, ""); } void remove_item(GtkWidget *widget, gpointer selection) { GtkListStore *store; GtkTreeModel *model; GtkTreeIter iter; store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list))); model = gtk_tree_view_get_model(GTK_TREE_VIEW(list)); if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) { return; } if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), &model, &iter)) { gtk_list_store_remove(store, &iter); } } void remove_all(GtkWidget *widget, gpointer selection) { GtkListStore *store; GtkTreeModel *model; GtkTreeIter iter; store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list))); model = gtk_tree_view_get_model(GTK_TREE_VIEW(list)); if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) { return; } gtk_list_store_clear(store); } void init_list(GtkWidget *list) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkListStore *store; renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("List Item", renderer, "text", LIST_ITEM, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(list), column); store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING); gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store)); g_object_unref(store); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *sw; GtkWidget *remove; GtkWidget *add; GtkWidget *removeAll; GtkWidget *entry; GtkWidget *vbox; GtkWidget *hbox; GtkTreeSelection *selection; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "List view"); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_container_set_border_width(GTK_CONTAINER (window), 10); gtk_widget_set_size_request(window, 370, 270); sw = gtk_scrolled_window_new(NULL, NULL); list = gtk_tree_view_new(); gtk_container_add(GTK_CONTAINER(sw), list); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE); vbox = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5); hbox = gtk_hbox_new(FALSE, 5); add = gtk_button_new_with_label("Add"); remove = gtk_button_new_with_label("Remove"); removeAll = gtk_button_new_with_label("Remove All"); entry = gtk_entry_new(); gtk_widget_set_size_request(entry, 120, -1); gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, TRUE, 3); gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, TRUE, 3); gtk_box_pack_start(GTK_BOX(hbox), remove, FALSE, TRUE, 3); gtk_box_pack_start(GTK_BOX(hbox), removeAll, FALSE, TRUE, 3); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 3); gtk_container_add(GTK_CONTAINER(window), vbox); init_list(list); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list)); g_signal_connect(G_OBJECT(add), "clicked", G_CALLBACK(append_item), entry); g_signal_connect(G_OBJECT(remove), "clicked", G_CALLBACK(remove_item), selection); g_signal_connect(G_OBJECT(removeAll), "clicked", G_CALLBACK(remove_all), selection); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(window); gtk_main(); return 0; }
在该示例中,我们有三个按钮和一个文本输入框。这些按钮用于添加新项目、删除选定项目和删除所有项目。
sw = gtk_scrolled_window_new(NULL, NULL); list = gtk_tree_view_new(); gtk_container_add(GTK_CONTAINER(sw), list);
GtkTreeView
放置在滚动窗口内。
if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), &model, &iter)) { gtk_list_store_remove(store, &iter); }
gtk_list_store_remove
函数从列表中删除一个项目。
gtk_list_store_clear(store);
gtk_list_store_clear
从列表中删除所有项目。
if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) { return; }
此代码检查列表中是否还有剩余项目。显然,只有当列表中至少剩下一个项目时,我们才能删除项目。

树视图
以下示例使用 GtkTreeView
控件来显示分层数据。在前面的两个示例中,我们使用了列表视图;现在我们将使用树视图。
#include <gtk/gtk.h> enum { COLUMN = 0, NUM_COLS }; void on_changed(GtkWidget *widget, gpointer statusbar) { GtkTreeIter iter; GtkTreeModel *model; gchar *value; if (gtk_tree_selection_get_selected( GTK_TREE_SELECTION(widget), &model, &iter)) { gtk_tree_model_get(model, &iter, COLUMN, &value, -1); gtk_statusbar_push(GTK_STATUSBAR(statusbar), gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), value), value); g_free(value); } } GtkTreeModel *create_and_fill_model(void) { GtkTreeStore *treestore; GtkTreeIter toplevel, child; treestore = gtk_tree_store_new(NUM_COLS, G_TYPE_STRING); gtk_tree_store_append(treestore, &toplevel, NULL); gtk_tree_store_set(treestore, &toplevel, COLUMN, "Scripting languages", -1); gtk_tree_store_append(treestore, &child, &toplevel); gtk_tree_store_set(treestore, &child, COLUMN, "Python", -1); gtk_tree_store_append(treestore, &child, &toplevel); gtk_tree_store_set(treestore, &child, COLUMN, "Perl", -1); gtk_tree_store_append(treestore, &child, &toplevel); gtk_tree_store_set(treestore, &child, COLUMN, "PHP", -1); gtk_tree_store_append(treestore, &toplevel, NULL); gtk_tree_store_set(treestore, &toplevel, COLUMN, "Compiled languages", -1); gtk_tree_store_append(treestore, &child, &toplevel); gtk_tree_store_set(treestore, &child, COLUMN, "C", -1); gtk_tree_store_append(treestore, &child, &toplevel); gtk_tree_store_set(treestore, &child, COLUMN, "C++", -1); gtk_tree_store_append(treestore, &child, &toplevel); gtk_tree_store_set(treestore, &child, COLUMN, "Java", -1); return GTK_TREE_MODEL(treestore); } GtkWidget *create_view_and_model(void) { GtkTreeViewColumn *col; GtkCellRenderer *renderer; GtkWidget *view; GtkTreeModel *model; view = gtk_tree_view_new(); col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Programming languages"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(col, renderer, TRUE); gtk_tree_view_column_add_attribute(col, renderer, "text", COLUMN); model = create_and_fill_model(); gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); g_object_unref(model); return view; } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *view; GtkTreeSelection *selection; GtkWidget *vbox; GtkWidget *statusbar; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_title(GTK_WINDOW(window), "Tree view"); gtk_widget_set_size_request(window, 350, 300); vbox = gtk_vbox_new(FALSE, 2); gtk_container_add(GTK_CONTAINER(window), vbox); view = create_view_and_model(); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 1); statusbar = gtk_statusbar_new(); gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1); g_signal_connect(selection, "changed", G_CALLBACK(on_changed), statusbar); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_widget_show_all(window); gtk_main(); return 0; }
在该示例中,我们将编程语言分为两组:脚本语言和编译语言。语言类别用作其项目列表的顶级节点。当前选定的项目显示在状态栏中。创建树视图的步骤与创建列表视图非常相似。
treestore = gtk_tree_store_new(NUM_COLS, G_TYPE_STRING);
gtk_tree_store_new
函数创建一个 GtkTreeStore
,它是一个与 GtkTreeView
一起使用的类似树的数据结构。
gtk_tree_store_append(treestore, &toplevel, NULL); gtk_tree_store_set(treestore, &toplevel, COLUMN, "Scripting languages", -1);
这两行创建一个顶级节点。
gtk_tree_store_append(treestore, &child, &toplevel); gtk_tree_store_set(treestore, &child, COLUMN, "Python", -1);
在这里,我们将一个子项添加到顶级节点中。

在本章中,我们介绍了 GtkTreeView
控件。