跳轉到內容

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 程式碼生成的簡短討論
華夏公益教科書