GTK+中的树状列表构件(GtkTreeView)


GTK+中的树状列表构件(GtkTreeView)

在本章的GTK+程序设计教程中,我们将向大家重点介绍非常常用也有点复杂的构件——GtkTreeView 。

GtkTreeView 构件是一个高级的构件,利用他你就可以制作出漂亮的普通列表或者是树状的列表。这个构件里可以包含一或者多行。他的构架呢?正是采用了大名鼎鼎的MVC (Model View Controller) 设计框架。也就是说数据和显示方式是进行了一种分离的操作。

之前我们有说过复杂这个问题,于是在GtktreeView构件中确实还有着其他几个独立的对象结构(objects)。其中 GtkCellRenderer 就决定了在GtkTreeViewColumn. 中的数据究竟是如何来进行显示呈现的。GtkListStore 和 GtkTreeStore 的功能为体现模型(model)的作用。也就是说他们是用来处理和分析将要在GtkTreeView显示的数据的。 GtkTreeIter 则是一个数据结构被用于在GtkTreeView构件中,对行中的数据进行操作。 GtkTreeSelection 则是用来处理选项的。

一个简单的列表构件示例(Simple List View)

在这个例子中将向大家展示一个简单的列表效果。显示的数据仅仅是文本。

#include <gtk/gtk.h>

enum
{
LIST_ITEM = 0,
N_COLUMNS
}; static 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);
} static 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;
char *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);
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, 270, 250);
gtk_window_set_title(GTK_WINDOW(window), "List View"); list = gtk_tree_view_new();
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_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
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, "Capote");
add_to_list(list, "Saving private Ryan");
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;
}

在我们上面的这个示例代码中,我们将向大家展示的是5个条目并布置于GtkTreeView 构件中。我们首先在window中放置一个GtkVBox 构件。 在这个 GtkVBox 构件中含有两个构件:GtkTreeViewGtkLabel

list = gtk_tree_view_new();
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);

上面的代码生成了一个 GtkTreeView 构件并且栏数被设置为FALSE即只有一栏。

label = gtk_label_new("");
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);

生成了一个 GtkLabel构件,并且把它放置在GtkTreeView构件的下方,设置为居中。

init_list(list);

调用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);

在初始化函数中,我们生成了只有一栏的GtkTreeView。

store = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING);

gtk_tree_view_set_model(GTK_TREE_VIEW(list),
GTK_TREE_MODEL(store));

接下来我们又生成了一个GtkListStore 构件(a model) 然后把它与list 构件绑定。

g_object_unref(store);

这个 model 被自动的销毁,以释放内存空间。

add_to_list(list, "Aliens");

上面就是在调用add_to_list()函数,实现向list 中在增加一个选项的功能。

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()来获得model。我们生成新的一行并把行中的数据交给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);

这个就很好理解了,把changed 信号与 GtkTreeSelection绑定,我们就可以与回调函数 on_changed()建立了联系。

gtk_tree_model_get(model, &iter, LIST_ITEM, &value,  -1);
gtk_label_set_text(GTK_LABEL(label), value);

在这个回调函数里,我们取得了对应行的数据,当然是通过iter 来获取的。

Figure: List View

高级列表(Advanced List View)

在第二个例子中,我们将在前者的基础上填加一些额外的功能。我们将实现能够列表中填加或者去处其中的数据项。

#include <gtk/gtk.h>

enum
{
LIST_ITEM = 0,
N_COLUMNS
}; GtkWidget *list; static void
append_item(GtkWidget *widget, gpointer entry)
{
GtkListStore *store;
GtkTreeIter iter; const char *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);
} static 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);
}
} static 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);
} static 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);
sw = gtk_scrolled_window_new(NULL, NULL);
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_widget_set_size_request (window, 370, 270); 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(TRUE, 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_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 (sw), list);
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;
}

与前面的例子中的label不同的是,我们生成了三个按钮和一个单行文本输入框。我们将实现能够动态的为列表增加一个新的数据项或者去处选中的数据项以及全部数据项。

sw = gtk_scrolled_window_new(NULL, NULL);
... 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_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5);
...
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;

上面的代码是用于检查是否在列表中还存有剩下的数据项。很显然,我们能够把列表清除的一干二净。

Figure: Advanced List View

树状视图(Tree View)

接着,我们将向大家展示如何运用构件GtkTreeView来去显示有等级差异的数据项。在先前的两个例子中,我们我们用到了列表试图,现在我们介绍树状视图。

#include <gtk/gtk.h>

enum
{
COLUMN = 0,
NUM_COLS
} ; void on_changed(GtkWidget *widget, gpointer statusbar)
{
GtkTreeIter iter;
GtkTreeModel *model;
char *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);
}
} static 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);
} static 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;
}

在我们上面的示例中,我们来完成一项任务:把脚本语言和传统编程语言对应的数据项,进行区分。“语言种类”作为其对应数据项中的顶层节点,也就是说是一行数据列表的“头头”。当前选种的数据项,将在状态栏中显示出来。

从上面的这些步骤中,我们可以清晰的看到,树状视图与列表视图的生成方法很相似。

GtkTreeStore *treestore;

这里我们当然要使用一个不同的model—— GtkTreeStore

treestore = gtk_tree_store_new(NUM_COLS,
G_TYPE_STRING);

我们生成的 GtkTreeStore只有一列。

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);

上面的代码在生成一个子数据项。

Figure: Tree View

GTK+中的树状列表构件(GtkTreeView)的更多相关文章

  1. Android中的树状(tree)列表

    树状列表前端挺常用的,还有人专门写过Ztree,Android中有的时候也需要使用到树状列表,上篇文章写了一下ExpandableListView,ExpandableListView最多支持两级结构 ...

  2. jquery写的树状列表插件-alvintree

    在做项目的时候遇到选择部门下人员的功能,可多选可单选,所以就想着使用树状列表来进行选择,但在网上找了很多,发现要么就是挺复杂,要么就是需要各种前端框架的支持,试了一个感觉难用,所以就想着自己写一个简便 ...

  3. oracle中的树状查询

    oracle中的树状查询 工作中经常会遇到将数据库中的数据以树的形式展现的需求.以下我们来看一下该需求在Oracle中如何实现. 首先我们需要有一个树形的表结构(当然有时候会出现表结构不是典型的树形结 ...

  4. selenium如何操作页面树状列表

    selenium如何操作页面树状列表??举个例子:我要怎么操作如下图所示的树状结构列表?我要对这个树状结构列表做什么操作? 一.思路 1.根据driver.find_element_by_xpath( ...

  5. php中构建树状图

    /** * 指定根层级的树状图 * @param array $list 初始数组 * @param int $root 最上级一条数据的id * @param string $pk 每一条数据的id ...

  6. mysql中递归树状结构<转>

    在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方便的查了所有当前节点下的所有子节点.但很遗憾,在MySQL的目前版本中还没有对应的功能. ...

  7. LigerUI 树状列表折叠显示

    http://blog.csdn.net/haojuntu/article/details/8626040 —————————————————————————————————————————————— ...

  8. jquery-treegrid树状表格的使用(.Net平台)

    上一篇介绍了DataTable,这一篇在DT的基础之上再使用jquery的一款插件:treegrid,官网地址:http://maxazan.github.io/jquery-treegrid/ 一. ...

  9. 手把手教学~基于element封装tree树状下拉框

    在日常项目开发中,树状下拉框的需求还是比较常见的,但是element并没有这种组件以供使用.在这里,小编就基于element如何封装一个树状下拉框做个详细的介绍. 通过这篇文章,你可以了解学习到一个树 ...

随机推荐

  1. 创建一个cocos2d-x工程添加一个自定义Scene并显示

    #include "cocos2d.h" class RunScene :public cocos2d::CCLayer { public: virtual bool init() ...

  2. mysql批量更新、多表更新、多表删除

    本文介绍下,mysql中进行批量更新.多表更新.多表删除的一些实例,有需要的朋友可以参考下. 本节主要内容: mysql的批量更新.多表更新.多表删除 一,批量更新: 复制代码代码示例: update ...

  3. Compass 使用手册

    在EDM中使用基准   定义和基准相关的术语     这一段定义了基准术语.可以在属性对话框中知道   项目属性           系统基准         系统基准在项目属性里设置,并且值为0.它 ...

  4. yum和rpm命令详解

    rpm,全称RPM Package Manager,是RedHat发布的,针对特定硬件,已经编译好的软件包.安装之后就可以使用,不需要自行编译,以及之前对软件和硬件的检测,目录的配置等动作. yum, ...

  5. How Does #DeepDream Work?

    How Does #DeepDream Work? Do neural networks hallucinate of electronic dogs? If you’ve been browsing ...

  6. Unity3D连接真机调试教程,可抓断点

    源地址:http://www.unity蛮牛.com/thread-19586-1-1.html <ignore_js_op> 未标题-1.jpg (52.33 KB, 下载次数: 0) ...

  7. Java 8 vs. Scala(二):Stream vs. Collection

    [编者按]在之前文章中,我们介绍了 Java 8和Scala的Lambda表达式对比.在本文,将进行 Hussachai Puripunpinyo Java 和 Scala 对比三部曲的第二部分,主要 ...

  8. POJ2200+全排列模拟

    简单. 手动的实现全排列 #include<stdio.h> #include<string.h> #include<stdlib.h> #include<a ...

  9. 李洪强漫谈iOS开发[C语言-024]-表达式与赋值运算符

  10. scrollView的几个属性contentSize contentOffset contentInset

    01-  ontentSize是scrollview可以滚动的区域 比如frame = (0 ,0 ,320 ,480) contentSize = (320 ,960), 代表你的scrollvie ...