原文: http://zetcode.com/gui/gtk2/gtktreeview/

在 GTK+ 编程教程的这一部分中,我们使用GtkTreeView小部件。

GtkTreeView小部件是一个复杂的小部件,可用于显示列表和树。 小部件可以具有一列或多列。 GtkTreeView小部件具有 MVC(模型视图控制器)设计架构。 这意味着数据与视图是分开的。

GtkTreeView小部件还可以使用其他几个对象。 GtkCellRenderer确定如何在GtkTreeViewColumn中显示数据。 GtkListStoreGtkTreeStore代表模型。 它们处理在GtkTreeView小部件中显示的数据。 GtkTreeIter是用于引用GtkTreeView中的行的结构。 GtkTreeSelection是处理选择的对象。

ListView

第一个示例将显示一个简单的列表视图。 我们将显示文本数据。

listview.c

  1. #include <gtk/gtk.h>
  2. enum {
  3. LIST_ITEM = 0,
  4. N_COLUMNS
  5. };
  6. void init_list(GtkWidget *list) {
  7. GtkCellRenderer *renderer;
  8. GtkTreeViewColumn *column;
  9. GtkListStore *store;
  10. renderer = gtk_cell_renderer_text_new ();
  11. column = gtk_tree_view_column_new_with_attributes("List Items",
  12. renderer, "text", LIST_ITEM, NULL);
  13. gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
  14. store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);
  15. gtk_tree_view_set_model(GTK_TREE_VIEW(list),
  16. GTK_TREE_MODEL(store));
  17. g_object_unref(store);
  18. }
  19. void add_to_list(GtkWidget *list, const gchar *str) {
  20. GtkListStore *store;
  21. GtkTreeIter iter;
  22. store = GTK_LIST_STORE(gtk_tree_view_get_model
  23. (GTK_TREE_VIEW(list)));
  24. gtk_list_store_append(store, &iter);
  25. gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);
  26. }
  27. void on_changed(GtkWidget *widget, gpointer label) {
  28. GtkTreeIter iter;
  29. GtkTreeModel *model;
  30. gchar *value;
  31. if (gtk_tree_selection_get_selected(
  32. GTK_TREE_SELECTION(widget), &model, &iter)) {
  33. gtk_tree_model_get(model, &iter, LIST_ITEM, &value, -1);
  34. gtk_label_set_text(GTK_LABEL(label), value);
  35. g_free(value);
  36. }
  37. }
  38. int main(int argc, char *argv[]) {
  39. GtkWidget *window;
  40. GtkWidget *list;
  41. GtkWidget *vbox;
  42. GtkWidget *label;
  43. GtkTreeSelection *selection;
  44. gtk_init(&argc, &argv);
  45. window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  46. list = gtk_tree_view_new();
  47. gtk_window_set_title(GTK_WINDOW(window), "List view");
  48. gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  49. gtk_container_set_border_width(GTK_CONTAINER(window), 10);
  50. gtk_window_set_default_size(GTK_WINDOW(window), 270, 250);
  51. gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
  52. vbox = gtk_vbox_new(FALSE, 0);
  53. gtk_box_pack_start(GTK_BOX(vbox), list, TRUE, TRUE, 5);
  54. label = gtk_label_new("");
  55. gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
  56. gtk_container_add(GTK_CONTAINER(window), vbox);
  57. init_list(list);
  58. add_to_list(list, "Aliens");
  59. add_to_list(list, "Leon");
  60. add_to_list(list, "The Verdict");
  61. add_to_list(list, "North Face");
  62. add_to_list(list, "Der Untergang");
  63. selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
  64. g_signal_connect(selection, "changed",
  65. G_CALLBACK(on_changed), label);
  66. g_signal_connect(G_OBJECT (window), "destroy",
  67. G_CALLBACK(gtk_main_quit), NULL);
  68. gtk_widget_show_all(window);
  69. gtk_main();
  70. return 0;
  71. }

在我们的代码示例中,我们在GtkTreeView中显示了五个项目。 我们只有一列,并且该列的标题是隐藏的。 我们将GtkVBox放入窗口。 该框具有两个小部件:GtkTreeViewGtkLabel

  1. list = gtk_tree_view_new();

gtk_tree_view_new()函数创建一个新的GtkTreeView小部件。

  1. gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);

我们使用gtk_tree_view_set_headers_visible()函数隐藏列标题。

  1. label = gtk_label_new("");
  2. gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);

创建GtkLabel并将其放置在GtkTreeView下方。

  1. init_list(list);

此函数初始化列表。

  1. renderer = gtk_cell_renderer_text_new();
  2. column = gtk_tree_view_column_new_with_attributes("List Items",
  3. renderer, "text", LIST_ITEM, NULL);
  4. gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);

在该函数内部,我们创建并附加一列。

  1. store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);
  2. gtk_tree_view_set_model(GTK_TREE_VIEW(list),
  3. GTK_TREE_MODEL(store));

我们创建一个GtkListStore(模型)并将其设置为列表。

  1. g_object_unref(store);

TreeView增加了存储对象的引用。 我们使用g_object_unref()函数将引用从 2 减少到 1。 然后随视图自动销毁模型。

  1. add_to_list(list, "Aliens");

该用户功能将一个选项添加到列表中。

  1. store = GTK_LIST_STORE(gtk_tree_view_get_model
  2. (GTK_TREE_VIEW(list)));
  3. gtk_list_store_append(store, &iter);
  4. gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);

add_to_list()函数内部,我们使用gtk_tree_view_get_model()函数调用获得模型。 我们附加一个新行,并为该行设置一个值,该值由GtkTreeIter对象引用。

  1. selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));

不需要显式创建GtkTreeSelection; 它是使用GtkTreeView小部件自动创建的。 使用gtk_tree_view_get_selection()函数调用可获得对窗口小部件的引用。

  1. g_signal_connect(selection, "changed",
  2. G_CALLBACK(on_changed), label);

GtkTreeSelectionchanged信号连接到on_changed()处理器。

  1. if (gtk_tree_selection_get_selected(
  2. GTK_TREE_SELECTION(widget), &model, &iter)) {

gtk_tree_selection_get_selected()函数将iter设置为当前选定的节点。

  1. gtk_tree_model_get(model, &iter, LIST_ITEM, &value, -1);

在处理函数中,我们获取iter对象引用的行中单元格的值。

  1. gtk_label_set_text(GTK_LABEL(label), value);

检索到的值通过gtk_label_set_text()函数设置为标签。

`GtkTreeView`小部件 - 图1

图:列表视图

动态列表视图

第二个示例在前一个示例中添加了其他功能。 我们将能够在列表视图中添加和删除项目。

dynamiclistview.c

  1. #include <gtk/gtk.h>
  2. enum {
  3. LIST_ITEM = 0,
  4. N_COLUMNS
  5. };
  6. GtkWidget *list;
  7. void append_item(GtkWidget *widget, gpointer entry) {
  8. GtkListStore *store;
  9. GtkTreeIter iter;
  10. const gchar *str = gtk_entry_get_text(entry);
  11. store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
  12. gtk_list_store_append(store, &iter);
  13. gtk_list_store_set(store, &iter, LIST_ITEM, str, -1);
  14. gtk_entry_set_text(entry, "");
  15. }
  16. void remove_item(GtkWidget *widget, gpointer selection) {
  17. GtkListStore *store;
  18. GtkTreeModel *model;
  19. GtkTreeIter iter;
  20. store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
  21. model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
  22. if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) {
  23. return;
  24. }
  25. if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection),
  26. &model, &iter)) {
  27. gtk_list_store_remove(store, &iter);
  28. }
  29. }
  30. void remove_all(GtkWidget *widget, gpointer selection) {
  31. GtkListStore *store;
  32. GtkTreeModel *model;
  33. GtkTreeIter iter;
  34. store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
  35. model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
  36. if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) {
  37. return;
  38. }
  39. gtk_list_store_clear(store);
  40. }
  41. void init_list(GtkWidget *list) {
  42. GtkCellRenderer *renderer;
  43. GtkTreeViewColumn *column;
  44. GtkListStore *store;
  45. renderer = gtk_cell_renderer_text_new();
  46. column = gtk_tree_view_column_new_with_attributes("List Item",
  47. renderer, "text", LIST_ITEM, NULL);
  48. gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
  49. store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);
  50. gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
  51. g_object_unref(store);
  52. }
  53. int main(int argc, char *argv[]) {
  54. GtkWidget *window;
  55. GtkWidget *sw;
  56. GtkWidget *remove;
  57. GtkWidget *add;
  58. GtkWidget *removeAll;
  59. GtkWidget *entry;
  60. GtkWidget *vbox;
  61. GtkWidget *hbox;
  62. GtkTreeSelection *selection;
  63. gtk_init(&argc, &argv);
  64. window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  65. gtk_window_set_title(GTK_WINDOW(window), "List view");
  66. gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  67. gtk_container_set_border_width(GTK_CONTAINER (window), 10);
  68. gtk_widget_set_size_request(window, 370, 270);
  69. sw = gtk_scrolled_window_new(NULL, NULL);
  70. list = gtk_tree_view_new();
  71. gtk_container_add(GTK_CONTAINER(sw), list);
  72. gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
  73. GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  74. gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
  75. GTK_SHADOW_ETCHED_IN);
  76. gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
  77. vbox = gtk_vbox_new(FALSE, 0);
  78. gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5);
  79. hbox = gtk_hbox_new(FALSE, 5);
  80. add = gtk_button_new_with_label("Add");
  81. remove = gtk_button_new_with_label("Remove");
  82. removeAll = gtk_button_new_with_label("Remove All");
  83. entry = gtk_entry_new();
  84. gtk_widget_set_size_request(entry, 120, -1);
  85. gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, TRUE, 3);
  86. gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, TRUE, 3);
  87. gtk_box_pack_start(GTK_BOX(hbox), remove, FALSE, TRUE, 3);
  88. gtk_box_pack_start(GTK_BOX(hbox), removeAll, FALSE, TRUE, 3);
  89. gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 3);
  90. gtk_container_add(GTK_CONTAINER(window), vbox);
  91. init_list(list);
  92. selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
  93. g_signal_connect(G_OBJECT(add), "clicked",
  94. G_CALLBACK(append_item), entry);
  95. g_signal_connect(G_OBJECT(remove), "clicked",
  96. G_CALLBACK(remove_item), selection);
  97. g_signal_connect(G_OBJECT(removeAll), "clicked",
  98. G_CALLBACK(remove_all), selection);
  99. g_signal_connect(G_OBJECT(window), "destroy",
  100. G_CALLBACK(gtk_main_quit), NULL);
  101. gtk_widget_show_all(window);
  102. gtk_main();
  103. return 0;
  104. }

在示例中,我们有三个按钮和一个文本输入。 这些按钮添加一个新项目,删除选定的项目,然后删除所有项目。

  1. sw = gtk_scrolled_window_new(NULL, NULL);
  2. list = gtk_tree_view_new();
  3. gtk_container_add(GTK_CONTAINER(sw), list);

GtkTreeView放置在滚动窗口内。

  1. if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection),
  2. &model, &iter)) {
  3. gtk_list_store_remove(store, &iter);
  4. }

gtk_list_store_remove()函数从列表中删除一个项目。

  1. gtk_list_store_clear(store);

gtk_list_store_clear()从列表中删除所有项目。

  1. if (gtk_tree_model_get_iter_first(model, &iter) == FALSE) {
  2. return;
  3. }

此代码检查列表中是否还有剩余项目。 显然,只有列表中至少剩余一个,我们才能删除项目。

`GtkTreeView`小部件 - 图2

图:动态列表视图

TreeView

以下示例使用GtkTreeView小部件显示分层数据。 在前两个示例中,我们使用了列表视图。 现在我们将使用树形视图。

treeview.c

  1. #include <gtk/gtk.h>
  2. enum {
  3. COLUMN = 0,
  4. NUM_COLS
  5. };
  6. void on_changed(GtkWidget *widget, gpointer statusbar) {
  7. GtkTreeIter iter;
  8. GtkTreeModel *model;
  9. gchar *value;
  10. if (gtk_tree_selection_get_selected(
  11. GTK_TREE_SELECTION(widget), &model, &iter)) {
  12. gtk_tree_model_get(model, &iter, COLUMN, &value, -1);
  13. gtk_statusbar_push(GTK_STATUSBAR(statusbar),
  14. gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar),
  15. value), value);
  16. g_free(value);
  17. }
  18. }
  19. GtkTreeModel *create_and_fill_model(void) {
  20. GtkTreeStore *treestore;
  21. GtkTreeIter toplevel, child;
  22. treestore = gtk_tree_store_new(NUM_COLS,
  23. G_TYPE_STRING);
  24. gtk_tree_store_append(treestore, &toplevel, NULL);
  25. gtk_tree_store_set(treestore, &toplevel,
  26. COLUMN, "Scripting languages",
  27. -1);
  28. gtk_tree_store_append(treestore, &child, &toplevel);
  29. gtk_tree_store_set(treestore, &child,
  30. COLUMN, "Python",
  31. -1);
  32. gtk_tree_store_append(treestore, &child, &toplevel);
  33. gtk_tree_store_set(treestore, &child,
  34. COLUMN, "Perl",
  35. -1);
  36. gtk_tree_store_append(treestore, &child, &toplevel);
  37. gtk_tree_store_set(treestore, &child,
  38. COLUMN, "PHP",
  39. -1);
  40. gtk_tree_store_append(treestore, &toplevel, NULL);
  41. gtk_tree_store_set(treestore, &toplevel,
  42. COLUMN, "Compiled languages",
  43. -1);
  44. gtk_tree_store_append(treestore, &child, &toplevel);
  45. gtk_tree_store_set(treestore, &child,
  46. COLUMN, "C",
  47. -1);
  48. gtk_tree_store_append(treestore, &child, &toplevel);
  49. gtk_tree_store_set(treestore, &child,
  50. COLUMN, "C++",
  51. -1);
  52. gtk_tree_store_append(treestore, &child, &toplevel);
  53. gtk_tree_store_set(treestore, &child,
  54. COLUMN, "Java",
  55. -1);
  56. return GTK_TREE_MODEL(treestore);
  57. }
  58. GtkWidget *create_view_and_model(void) {
  59. GtkTreeViewColumn *col;
  60. GtkCellRenderer *renderer;
  61. GtkWidget *view;
  62. GtkTreeModel *model;
  63. view = gtk_tree_view_new();
  64. col = gtk_tree_view_column_new();
  65. gtk_tree_view_column_set_title(col, "Programming languages");
  66. gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
  67. renderer = gtk_cell_renderer_text_new();
  68. gtk_tree_view_column_pack_start(col, renderer, TRUE);
  69. gtk_tree_view_column_add_attribute(col, renderer,
  70. "text", COLUMN);
  71. model = create_and_fill_model();
  72. gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
  73. g_object_unref(model);
  74. return view;
  75. }
  76. int main(int argc, char *argv[]) {
  77. GtkWidget *window;
  78. GtkWidget *view;
  79. GtkTreeSelection *selection;
  80. GtkWidget *vbox;
  81. GtkWidget *statusbar;
  82. gtk_init(&argc, &argv);
  83. window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  84. gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  85. gtk_window_set_title(GTK_WINDOW(window), "Tree view");
  86. gtk_widget_set_size_request(window, 350, 300);
  87. vbox = gtk_vbox_new(FALSE, 2);
  88. gtk_container_add(GTK_CONTAINER(window), vbox);
  89. view = create_view_and_model();
  90. selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  91. gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 1);
  92. statusbar = gtk_statusbar_new();
  93. gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);
  94. g_signal_connect(selection, "changed",
  95. G_CALLBACK(on_changed), statusbar);
  96. g_signal_connect (G_OBJECT (window), "destroy",
  97. G_CALLBACK(gtk_main_quit), NULL);
  98. gtk_widget_show_all(window);
  99. gtk_main();
  100. return 0;
  101. }

在示例中,我们将编程语言分为两组:脚本语言和编译语言。 语言类别是其项目列表的顶级节点。 当前选择的项目显示在状态栏中。 创建树视图的步骤与创建列表视图非常相似。

  1. treestore = gtk_tree_store_new(NUM_COLS,
  2. G_TYPE_STRING);

gtk_tree_store_new()函数创建GtkTreeStore,它是与GtkTreeView一起使用的树状数据结构。

  1. gtk_tree_store_append(treestore, &toplevel, NULL);
  2. gtk_tree_store_set(treestore, &toplevel,
  3. COLUMN, "Scripting languages",
  4. -1);

这两行创建一个顶级节点。

  1. gtk_tree_store_append(treestore, &child, &toplevel);
  2. gtk_tree_store_set(treestore, &child,
  3. COLUMN, "Python",
  4. -1);

在这里,我们将一个子项添加到顶级节点。

`GtkTreeView`小部件 - 图3

图:树形视图

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