转自: http://guoyinghui2012.blog.163.com/blog/static/20871720020127414710886/
有一种其它类型的内置模型,叫做GtkTreeStore,它组织了一个多行多级的树形结构。也可以用GtkTreeStore树形模型来实现一个列表,但是一般不推荐这么做,因为当对象假定它有一个或者多个孩子的时候,会带来额外的开销。
Figure 8-5展示了一个树形存储器,它包括两个根节点,每个都有自己的孩子。通过点击一行左边的扩展按钮,您可以显示或者隐藏它的孩子。这和GtkExpander构件提供的功能类似。
用GtkTreeStore实现的GtkTreeView,和GtkListStore实现的唯一区别在于,创建的存储模型不同。添加列和渲染器,在两种
方法中实现方式一样,因为列是视图的一部分,而不属于模型,因此Listing 8-2不包括setup_tree_view()的实现。
Listing 8-2将原始的Grocery List程序修改了一些,把产品分成类型。这个列表包括两个类型:清洁用品和食物,它们都有自己的孩子。初始化的时候,每类的数量都是零,因为这是实时计算的。
Listing 8-2 创建一个GtkTreeStore(treestore.c)
#include
enum
{
BUY_IT = 0,
QUANTITY,
PRODUCT,
COLUMNS
};
enum
{
PRODUCT_CATEGORY,
PRODUCT_CHILD
};
typedef struct
{
gint product_type;
gboolean buy;
gint quantity;
gchar *product;
} GroceryItem;
GroceryItem list[] =
{
{ PRODUCT_CATEGORY, TRUE, 0, "Cleaning Supplies" },
{ PRODUCT_CHILD, TRUE, 1, "Paper Towels" },
{ PRODUCT_CHILD, TRUE, 3, "Toilet Paper" },
{ PRODUCT_CATEGORY, TRUE, 0, "Food" },
{ PRODUCT_CHILD, TRUE, 2, "Bread" },
{ PRODUCT_CHILD, FALSE, 1, "Butter" },
{ PRODUCT_CHILD, TRUE, 1, "Milk" },
{ PRODUCT_CHILD, FALSE, 3, "Chips" },
{ PRODUCT_CHILD, TRUE, 4, "Soda" },
{ PRODUCT_CATEGORY, FALSE, 0, NULL }
};
/* The implementation of this function is the same as in Listing 8-1. */
static void setup_tree_view (GtkWidget*);
int main (int argc,
char *argv[])
{
GtkWidget *window, *treeview, *scrolled_win;
GtkTreeStore *store;
GtkTreeIter iter, child;
guint i = 0, j;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "Grocery List");
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
gtk_widget_set_size_request (window, 275, 300);
treeview = gtk_tree_view_new ();
setup_tree_view (treeview);
store = gtk_tree_store_new (COLUMNS, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_STRING);
while (list[i].product != NULL)
{
/* If the product type is a category, count the quantity of all of the products
* in the category that are going to be bought. */
if (list[i].product_type == PRODUCT_CATEGORY)
{
j = i + 1;
/* Calculate how many products will be bought in the category. */
while (list[j].product != NULL && list[j].product_type != PRODUCT_CATEGORY)
{
if (list[j].buy)
list[i].quantity += list[j].quantity;
j++;
}
/* Add the category as a new root element. */
gtk_tree_store_append (store, &iter, NULL);
gtk_tree_store_set (store, &iter, BUY_IT, list[i].buy,
QUANTITY, list[i].quantity, PRODUCT, list[i].product, -1);
}
/* Otherwise, add the product as a child of the category. */
else
{
gtk_tree_store_append (store, &child, &iter);
gtk_tree_store_set (store, &child, BUY_IT, list[i].buy,
QUANTITY, list[i].quantity, PRODUCT, list[i].product, -1);
}
i++;
}
gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
gtk_tree_view_expand_all (GTK_TREE_VIEW (treeview));
g_object_unref (store);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_container_add (GTK_CONTAINER (scrolled_win), treeview);
gtk_container_add (GTK_CONTAINER (window), scrolled_win);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
树存储器由gtk_tree_store_new()来初始化,它接受和gtk_list_store_new()同样的参数。这些包括列的数据的数量,紧跟着是一个队列,包括每列对应的数据类型。
向一个树存储器中添加行,与向列表存储中器添加行,稍有区别。通过gtk_tree_store_append()向树存储器中添加行,接受两个迭代器,而不是一个。当函数返回时,第一个迭代器指向插入的行位置,第二个迭代器指向新行的父亲的行位置。
gtk_tree_store_append (store, &iter, NULL);
在前面的gtk_tree_store_append()调用中,向列表中附加的根元素传递的NULL,它表示父迭代器。迭代器iter用于设置新行的位置。第一个迭代器不需要被重新初始化,因为当函数返回以后,当前内容会被覆盖。
跟在后面的第二个gtk_tree_store_append()调用,行作为孩子被添加到iter中。接着,在函数返回时,树存储器中的子树迭代器被设置成为新行的当前位置。
gtk_tree_store_append (store, &child, &iter);
正如列表存储器,有许多可用的函数可以向树存储器中添加行。这包括gtk_tree_store_insert()、
gtk_tree_store_prepend()和gtk_tree_store_insert_before()等。如果想查看完整的函数列表,您可
以参阅GtkTreeStore API文档。
在您想树存储器添加一行以后,它只是一个简单的空行。调用gtk_tree_store_set(),给一行添加一行。这个函数和
gtk_list_store_set()的工作方式相同。它接受树存储器,指向行位置的树迭代器,还有一个以-1结束的“列-数据”对。这些列对应的是
那些您设置单元渲染器属性使用的东西。
gtk_tree_store_set (store, &child, BUY_IT, list[i].buy, QUANTITY, list[i].quantity,
PRODUCT, list[i].product, -1);
除此之外,还提供了gtk_tree_store_clear(),它可以用于删除树存储器中的所有行。您就剩下一个没有数据的GtkTreeStore。如果这个对象没有其它地方使用,他就会被解引用。
在Listing
8-2中,调用gtk_main()之前,调用gtk_tree_view_expand_all()来展开所有的行。这是个递归函数,它会展开每个可能
展开的行,当然它只会影响到有父子关系的树模型。除此之外,您可以通过gtk_tree_view_collapse_all()来收起所有的行。默认状
态下,所有的行都是收起的。