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. call(this)引起的对闭包的重新理解

    call(this)引起的对闭包的重新理解.md 变量的作用域 全局变量局部变量 Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. 函数外部无法读取函数内的局部变量. 函数内部 ...

  2. php eval函数用法总结

    可以在php.ini禁止此函数,结果失败了 eval定义和用法  eval() 函数把字符串按照 PHP 代码来计算.  该字符串必须是合法的 PHP 代码,且必须以分号结尾.  如果没有在代码字符串 ...

  3. Linux oracle数据库自动备份自动压缩脚本代码

    Linux oracle数据库备份完成后可以自动压缩脚本代码. 复制代码代码如下: #!/bin/bash #backup.sh #edit: www.jbxue.com ##系统名称 sysname ...

  4. Linux文件保护禁止修改、删除、移动文件等,使用chattr +i保护

    不让用户修改.删除文件等,使用 chattr保护 chattr命令的用法:chattr [ -RV ] [ -v version ] [ mode ] files… 最关键的是在[mode]部分,[m ...

  5. python模块之os和os.path模块

    1.os模块os.listdir(dirname) 列出dirname下的目录和文件os.getcwd()函数得到当前工作目录,即当前Python脚本工作的目录路径.os.getenv()和os.pu ...

  6. Educational Codeforces Round 7 F - The Sum of the k-th Powers 拉格朗日插值

    The Sum of the k-th Powers There are well-known formulas: , , . Also mathematicians found similar fo ...

  7. TypeScript学习指南第二章--接口(Interface)

    接口(Interface) TypeScript的核心机制之一在于它的类型检查系统(type-checker)只关注一个变量的"模型(shape)" 稍后我们去了解这个所谓的形状是 ...

  8. spring beans源码解读

    spring beans下面有如下源文件包: org.springframework.beans, 包含了操作java bean的接口和类.org.springframework.beans.anno ...

  9. C#操作mongodb数据库

    1.下载驱动: 如下图:选择c#解决方案,右键,点击 “管理NuGet程序包(N)...” 在弹出的对话框中,输入MongoDB.Driver,进行搜索,然后选择安装. 2.引用命名空间: using ...

  10. CQRS学习——IOC,配置,仓储隔离以及QueryEntry[其三]

    从IoC开始说起 博主最早开始用的IoC容器叫AutoFac,那时候用它主要是为了生命周期管理——将EF上下文的生命周期限定为每请求.当然也总是每每听到IoC的好处,但是仍然不能理解其优势.最近在学习 ...