树和列表小组件概述 [源代码]

本文档介绍了 GtkTreeView 小组件和辅助类,如树模型和单元格渲染器。所有这些都已弃用,并将在 GTK 5 中删除。其替换内容在 列表小组件概述 中进行了说明。

简介

要在 GTK 中创建树或列表,请结合 GtkTreeView 小组件使用 GtkTreeModel 接口。此小组件围绕一个模型/视图/控制器设计,由四个主要部分组成

  • 树视图小组件 (GtkTreeView)
  • 视图列 (GtkTreeViewColumn)
  • 单元格渲染器 (GtkCellRenderer 等)
  • 模型接口 (GtkTreeModel)

视图由前三个对象组成,而最后一个是模型MVC 设计的主要优点之一是,可以针对单个模型创建多个视图。例如,可以为文件管理器创建映射文件系统的模型。可以创建许多视图来显示文件系统的各个部分,但只需要在内存中保留一个副本来存储。

单元格渲染器的目的是为小组件提供可扩展性,并允许使用多种方式呈现相同类型的数据。例如,考虑如何呈现一个布尔变量。应该将其呈现为字符串 “True” 或 “False”、“On” 或 “Off”,还是应该将其呈现为复选框?

创建模型

GTK 提供了两个可用的简单模型:GtkListStoreGtkTreeStoreGtkListStore 用于对列表小组件进行建模,而 GtkTreeStore 对树进行建模。有可能开发一种新型模型,但现有模型应该可以满足所有情况,除了最特殊的情况。创建模型相当

GtkListStore *store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);

这将创建一个具有两列的列表存储:一个字符串列和一个布尔列。通常,不会像那样直接传递 2;通常都会创建一个枚举,其中会对不同的列进行枚举,然后跟一个表示列总数的标记。下一个示例将说明这一点,仅使用树存储来代替列表存储。创建树存储的操作几乎完全相同。

enum
{
   TITLE_COLUMN,
   AUTHOR_COLUMN,
   CHECKED_COLUMN,
   N_COLUMNS
};

GtkTreeStore *store = gtk_tree_store_new (N_COLUMNS,       /* Total number of columns */
                                          G_TYPE_STRING,   /* Book title              */
                                          G_TYPE_STRING,   /* Author                  */
                                          G_TYPE_BOOLEAN); /* Is checked out?         */

根据创建的是哪种模型,使用 gtk_tree_store_set() 或 gtk_list_store_set() 来将数据添加到模型。要执行此操作,必须获取 GtkTreeIter。该迭代器指向将添加数据的位置。

一旦获取到一个迭代器,就会使用 gtk_tree_store_set() 将数据应用于迭代器所指向的模型部分。考虑以下示例

GtkTreeIter iter;

gtk_tree_store_append (store, &iter, NULL);  /* Acquire an iterator */

gtk_tree_store_set (store, &iter,
                    TITLE_COLUMN, "The Principle of Reason",
                    AUTHOR_COLUMN, "Martin Heidegger",
                    CHECKED_COLUMN, FALSE,
                    -1);

请注意,最后一个参数为 -1。始终会执行此操作,因为这是一个可变参数函数,它需要知道何时停止处理参数。它可用于设置给定行中任意列或所有列中的数据。

gtk_tree_store_append() 的第三个参数是父迭代器。它用于将一行作为现有行的子级添加到 GtkTreeStore。这意味着只有当其父级可见且处于展开状态时,新行才可见。考虑以下示例

GtkTreeIter iter1;  /* Parent iter */
GtkTreeIter iter2;  /* Child iter  */

gtk_tree_store_append (store, &iter1, NULL);  /* Acquire a top-level iterator */
gtk_tree_store_set (store, &iter1,
                    TITLE_COLUMN, "The Art of Computer Programming",
                    AUTHOR_COLUMN, "Donald E. Knuth",
                    CHECKED_COLUMN, FALSE,
                    -1);

gtk_tree_store_append (store, &iter2, &iter1);  /* Acquire a child iterator */
gtk_tree_store_set (store, &iter2,
                    TITLE_COLUMN, "Volume 1: Fundamental Algorithms",
                    -1);

gtk_tree_store_append (store, &iter2, &iter1);
gtk_tree_store_set (store, &iter2,
                    TITLE_COLUMN, "Volume 2: Seminumerical Algorithms",
                    -1);

gtk_tree_store_append (store, &iter2, &iter1);
gtk_tree_store_set (store, &iter2,
                    TITLE_COLUMN, "Volume 3: Sorting and Searching",
                    -1);

创建视图组件

虽然有几种不同的模型可以选择,但只需处理一种视图小部件。它可以与列表或树储存器配合使用。设置 GtkTreeView 不是一件难事。它需要一个 GtkTreeModel 来了解从何处获取其数据。

GtkWidget *tree;

tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));

列和单元格渲染器

一旦 GtkTreeView 小部件具有模型,它将需要知道如何显示模型。它使用列和单元格渲染器来执行此操作。

单元格渲染器以某种方式用于绘制树模型中的数据。有许多单元格渲染器随 GTK 一起提供,包括 GtkCellRendererTextGtkCellRendererPixbufGtkCellRendererToggle。编写自定义渲染器相对容易。

GtkTreeViewColumnGtkTreeView 用于组织树视图中垂直列的对象。它需要知道为用户标记的列的名称、要使用哪种类型的单元格渲染器以及从模型中为给定行检索哪一部分数据。

GtkCellRenderer *renderer;
GtkTreeViewColumn *column;

renderer = gtk_cell_renderer_text_new (<!-- -->);
column = gtk_tree_view_column_new_with_attributes ("Author",
                                                   renderer,
                                                   "text", AUTHOR_COLUMN,
                                                   NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

此时,已经介绍了创建可显示树的所有步骤。模型已创建,数据存储其中,创建了树视图并将列添加到其中。

选择处理

大多数应用程序不仅需要处理显示数据,还需要接收来自用户的输入事件。要执行此操作,只需获取对选择对象和连接的引用即可获得 GtkTreeSelection::changed 信号。

/* Prototype for selection handler callback */
static void tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data);

/* Setup the selection handler */
GtkTreeSelection *select;

select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
g_signal_connect (G_OBJECT (select), "changed",
                  G_CALLBACK (tree_selection_changed_cb),
                  NULL);

然后检索所选行的

static void
tree_selection_changed_cb (GtkTreeSelection *selection, gpointer data)
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  char *author;

  if (gtk_tree_selection_get_selected (selection, &model, &iter))
    {
      gtk_tree_model_get (model, &iter, AUTHOR_COLUMN, &author, -1);

      g_print ("You selected a book by %s\n", author);

      g_free (author);
    }
}

简单示例

以下是在其他小部件上下文中使用 GtkTreeView 小部件的一个简单示例。它只是创建了一个简单的模型和视图,然后将它们组合在一起。请注意,该模型永远不会填充数据,这留作读者练习。可以在 GtkTreeModel 章节中找到更多相关信息。

enum
{
   TITLE_COLUMN,
   AUTHOR_COLUMN,
   CHECKED_COLUMN,
   N_COLUMNS
};

void
setup_tree (void)
{
   GtkTreeStore *store;
   GtkWidget *tree;
   GtkTreeViewColumn *column;
   GtkCellRenderer *renderer;

   /* Create a model.  We are using the store model for now, though we
    * could use any other GtkTreeModel */
   store = gtk_tree_store_new (N_COLUMNS,
                               G_TYPE_STRING,
                               G_TYPE_STRING,
                               G_TYPE_BOOLEAN);

   /* custom function to fill the model with data */
   populate_tree_model (store);

   /* Create a view */
   tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));

   /* The view now holds a reference.  We can get rid of our own
    * reference */
   g_object_unref (G_OBJECT (store));

   /* Create a cell render and arbitrarily make it red for demonstration
    * purposes */
   renderer = gtk_cell_renderer_text_new (<!-- -->);
   g_object_set (G_OBJECT (renderer),
                 "foreground", "red",
                 NULL);

   /* Create a column, associating the "text" attribute of the
    * cell_renderer to the first column of the model */
   column = gtk_tree_view_column_new_with_attributes ("Author", renderer,
                                                      "text", AUTHOR_COLUMN,
                                                      NULL);

   /* Add the column to the view. */
   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

   /* Second column.. title of the book. */
   renderer = gtk_cell_renderer_text_new (<!-- -->);
   column = gtk_tree_view_column_new_with_attributes ("Title",
                                                      renderer,
                                                      "text", TITLE_COLUMN,
                                                      NULL);
   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

   /* Last column.. whether a book is checked out. */
   renderer = gtk_cell_renderer_toggle_new (<!-- -->);
   column = gtk_tree_view_column_new_with_attributes ("Checked out",
                                                      renderer,
                                                      "active", CHECKED_COLUMN,
                                                      NULL);
   gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);

   /* Now we can manipulate the view just like any other GTK widget */
   ...
}