跳转到内容

GTK+ 示例/树形视图/杂项

来自维基教科书,开放世界中的开放书籍

本节讨论了一些似乎不适合放在其他地方的问题。如果您能想到其他应该在这里讨论的内容,请不要犹豫,发送邮件至 <tim at centricular dot net>。

从树形视图列小部件获取列号

[编辑 | 编辑源代码]

信号回调函数通常只传递一个指向 GtkTreeViewColumn 的指针,而应用程序程序员实际上只想了解哪个列号受到了影响。有两种方法可以找出列在树形视图中的位置。一种方法是编写一个小的辅助函数,根据给定的树形视图列对象查找列号,例如:[1]。Gtk3:将 'col->tree_view' 替换为 'gtk_tree_view_column_get_tree_view(col)'

  /* Returns column number or -1 if not found or on error */

  gint
  get_col_number_from_tree_view_column (GtkTreeViewColumn *col)
  {
    GList *cols;
    gint   num;

    g_return_val_if_fail ( col != NULL, -1 );
    g_return_val_if_fail ( col->tree_view != NULL, -1 );

    cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(col->tree_view));

    num = g_list_index(cols, (gpointer) col);

    g_list_free(cols);

    return num;
  }

或者,可以使用 g_object_set_data 和 g_object_get_data 在树形视图列上识别它是哪一列。这还有一个优点,即即使列在树形视图中重新排序,您仍然可以跟踪您的列(尽管此功能通常是禁用的)。使用方法如下

  ...

  enum
  {
    COL_FIRSTNAME,
    COL_SURNAME,
  };

  ...

  void
  some_callback (GtkWidget *treeview, ..., GtkTreeViewColumn *col, ...)
  {
    guint colnum = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(col), "columnnum"));

    ...
  }

  void
  create_view(void)
  {
    ...
    col = gtk_tree_view_column_new();
    g_object_set_data(G_OBJECT(col), "columnnum", GUINT_TO_POINTER(COL_FIRSTNAME));
    ...
    col = gtk_tree_view_column_new();
    g_object_set_data(G_OBJECT(col), "columnnum", GUINT_TO_POINTER(COL_SURNAME));
    ...
  }

"columnnum" 是上面示例中的一个随机字符串 - 您可以使用任何您想要的字符串,或者存储多个数据位(当然使用不同的字符串标识符)。当然,您也可以组合这两种方法,因为它们做的事情略有不同(第一种方法跟踪列在树形视图中的“物理”位置,第二种方法跟踪列对您的“含义”,与它在视图中的位置无关)。注释 [1]

此函数的灵感来自此邮件列表消息(感谢 Ken Rastatter 的链接和主题建议)。

列扩展器可见性

[编辑 | 编辑源代码]

隐藏列扩展器

[编辑 | 编辑源代码]

是否可以完全隐藏列扩展器?既可以又不可以。下面可能是最糟糕的黑客手段,无法保证它在即将发布的 Gtk+ 版本中或所有过去版本中都能正常工作(尽管后者很容易测试)。

您可以做的事情是创建一个空的树形视图列(例如,包含空字符串),并将其作为树形视图中的第一列。然后,您可以使用 gtk_tree_view_column_set_visible 隐藏该列。您会注意到,扩展器列现在会自动移动到以前位于第二列、现在位于第一列的可见列中。但是,如果您在调用 _set_visible 之后立即调用 gtk_tree_view_set_expander_column,则扩展器将移回隐藏的列,并且不再可见任何扩展器。

这意味着您必须自己处理行的展开和折叠,并使用相应的树形视图函数。虽然最后可以认为可以使用自定义单元格渲染器或 pixbuf 单元格渲染器实现自定义扩展器,但这可能是一项会让您忙超过五分钟的任务。如果您仍然尝试这样做,请准备好止痛药……

强制列扩展器可见性

[编辑 | 编辑源代码]

在某些情况下,即使相关行没有子节点,也应该显示扩展器,例如,当模型的一部分应该只有在请求时才能加载(例如,显示目录的内容)才会加载时。这是不可能的。只有当节点有子节点时,才会显示扩展器。

但是,这个问题存在一种解决方法:只需附加一个空的子行,并将节点设置为折叠状态。然后监听树形视图的 "row-expanded" 信号,并使用第一个新行填充已存在的行的内容,然后追加新的子行。有关更多详细信息,请参见此邮件列表主题。

获取点击事件发生的单元格渲染器

[编辑 | 编辑源代码]

似乎在许多情况下,当人们想知道点击事件发生的单元格渲染器时,他们并不真正需要知道单元格渲染器,而是想要修改特定列中的单个单元格。为此,您不需要知道单元格渲染器。使用 gtk_tree_view_get_path_at_pos 从传递给 "button-press-event" 信号回调中的按钮事件的 x 和 y 坐标获取树路径(如果您使用 "row-activated" 信号捕获双击,则会直接将树路径传递到回调函数中)。然后使用 gtk_tree_model_get_iter 将该树路径转换为迭代器,并使用 gtk_list_store_set 或 gtk_tree_store_set 修改要修改的单元格中的数据。

如果您确实需要知道发生按钮按下事件的单元格渲染器,那就有点棘手了。以下是对如何处理此问题的建议(该函数尚未经过充分测试,如果不同列中一个渲染器渲染的内容宽度不同,则可能无法正常工作;请将有关如何修复或改进此函数的建议发送给作者)

static gboolean
tree_view_get_cell_from_pos(GtkTreeView *view, guint x, guint y, GtkCellRenderer **cell)
{
	GtkTreeViewColumn *col = NULL;
	GList             *node, *columns, *cells;
	guint              colx = 0;

	g_return_val_if_fail ( view != NULL, FALSE );
	g_return_val_if_fail ( cell != NULL, FALSE );

	/* (1) find column and column x relative to tree view coordinates */

	columns = gtk_tree_view_get_columns(view);

	for (node = columns;  node != NULL && col == NULL;  node = node->next)
	{
		GtkTreeViewColumn *checkcol = (GtkTreeViewColumn*) node->data;

		if (x >= colx  &&  x < (colx + checkcol->width))
			col = checkcol;
		else
			colx += checkcol->width;
	}

	g_list_free(columns);

	if (col == NULL)
		return FALSE; /* not found */

	/* (2) find the cell renderer within the column */

	cells = gtk_tree_view_column_get_cell_renderers(col);

	for (node = cells;  node != NULL;  node = node->next)
	{
		GtkCellRenderer *checkcell = (GtkCellRenderer*) node->data;
		guint            width = 0, height = 0;

		/* Will this work for all packing modes? doesn't that
		 *  return a random width depending on the last content
		 * rendered? */
		gtk_cell_renderer_get_size(checkcell, GTK_WIDGET(view), NULL, NULL, NULL, &width, NULL);

		if (x >= colx && x < (colx + width))
		{
			*cell = checkcell;
			g_list_free(cells);
			return TRUE;
		}

		colx += width;
	}

	g_list_free(cells);
	return FALSE; /* not found */
}

static gboolean
onButtonPress (GtkWidget *view, GdkEventButton *bevent, gpointer data)
{
	GtkCellRenderer *renderer = NULL;

	if (tree_view_get_cell_from_pos(GTK_TREE_VIEW(view), bevent->x, bevent->y, &renderer))
		g_print ("Renderer found\n");
	else
		g_print ("Renderer not found!\n");
}

Glade 和树形视图

[编辑 | 编辑源代码]

一个经常被问到的问题是,如何在 Glade 中向 GtkTreeView 添加列。[1] 答案基本上是,您不能这样做,而且您无法这样做。Glade/libglade 唯一能为您做的事情是为您创建一个没有任何内容的 GtkTreeView。您需要在应用程序启动时查找树形视图小部件(当然是在界面创建之后),并将您的列表存储或树存储连接到它。然后,您需要添加 GtkTreeViewColumns 和单元格渲染器来以您想要的方式显示模型中的信息。您需要在应用程序中完成所有这些操作。

另一种方法是从 GtkTreeView 派生您自己的特殊小部件,它按照您想要的方式设置所有内容,然后在 Glade 中使用“自定义小部件”功能。当然,这仍然意味着您必须编写所有代码来填充列和单元格渲染器,并自己创建模型。

  1. 不要使用 Glade 为您生成代码。使用 Glade 创建界面。它会将界面保存到一个 XML 格式的 .glade 文件中。然后,您可以使用 libglade2 从该 .glade 文件中构建您的界面(窗口等)。请参见 此邮件列表消息,了解关于为什么应该避免 Glade 代码生成的简短讨论
华夏公益教科书