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

时间: 2024-10-10 09:09:46

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

Android中的树状(tree)列表

树状列表前端挺常用的,还有人专门写过Ztree,Android中有的时候也需要使用到树状列表,上篇文章写了一下ExpandableListView,ExpandableListView最多支持两级结构,Android中没有三层结构的组件,这个时候需要自己去扩展,可以扩展ExpandableListView,也可以选择扩展ListView.个人觉得扩展ListView更简单一点,多级列表你可以一次性加载出来,也可以分级加载出来,一般分级比较好,点了某一级可以控制点击事件然后去加载子级,这样实现起来

树状列表

alvintree文件 css样式 @charset "utf-8"; .alvintree_item{ height:30px;font-size:14px; font-family:微软雅黑; text-align:left; vertical-align:middle; line-height:30px; padding:0px 5px; background-image:url(../img/close.png); background-size:20px 20px; back

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

LigerUI 树状列表折叠显示

http://blog.csdn.net/haojuntu/article/details/8626040 —————————————————————————————————————————————————————————————————————————— function InitGrid(arrButtons) { //加载 菜单管理数据 $.post("Ajax_Menu.aspx", { view: 'ManageMenu' }, function(data) { gridRi

SQL语句中使用 with 递归实现表中数据树状显示

在开发过程中会遇到很多实现树状的功能,之前为了实现数据的树状显示一般都是通过程序里面的递归实现,今天试了一下通过sql语句实现具体如下: 表名:DeptInfo 字段:DeptId(部门编号),DeptName(部门名称),DeptUpId(部门上级ID),DeptPath(部门层级) 从DeptUpId和DeptPath中可看出表数据可能很乱: 为了实现表中数据树状显示,条例清晰具体代码实现如下: with dept as(select DeptId,DeptUpId from DeptInf

oracle中的树状查询

oracle中的树状查询 工作中经常会遇到将数据库中的数据以树的形式展现的需求.以下我们来看一下该需求在Oracle中如何实现. 首先我们需要有一个树形的表结构(当然有时候会出现表结构不是典型的树形结构,而是多表存储,需要根据多表连接查询生成树) 一.树型表结构:节点ID  上级ID  节点名称 二.用法: select 节点ID,节点名称,levelfrom 表名connect by prior 节点ID=上级节点IDstart with 上级节点ID=节点值 说明:1.常见的树形结构为公司组

mysql中递归树状结构&lt;转&gt;

在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方便的查了所有当前节点下的所有子节点.但很遗憾,在MySQL的目前版本中还没有对应的功能. 在MySQL中如果是有限的层次,比如我们事先如果可以确定这个树的最大深度是4, 那么所有节点为根的树的深度均不会超过4,则我们可以直接通过left join 来实现. 但很多时候我们无法控制树的深度.这时就需要在MySQL中用存储过程来实现或在你的程序中来实现这个递归.本文讨论一下几种实现的方法.

WPF - TreeView 仿VS2013解决方案资源管理器中的树状结构

效果图 先上效果图,若是你想要的效果,可以继续看下面的代码,不想浪费大家的时间. 样式定义 此处定义TreeView的样式,参考自MSDN,稍作修改. 注意:在TreeViewItem控件模板定义中绑定一个数据(Level)以及一个值转换器(LevelToMarginConverter),具体定义见下部分. <Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton"> <Se

GTK+重拾--10 GTK+中的组件(二)

(一):写在前面 在上面一个小节中,我们讲解了在GTK+2.0中的一些常用的构件,在这一小节中,我们将继续学习GTK+中常用的稍微复杂的构件,这里我们主要是学习GtkComboBox,GtkEntry,GtkIconView,GtkImage,GtkSeparator,GtkStatusBar.好了,现在我们开始我们的学习之旅. (二):GtkComboBox GtkComboBox构件的作用是让程序使用者根据不同的需求从很多选项中进行选择. 下面我们来看一下如何使用GTkComboBox. #