GTK+ 示例/树形视图/可编辑单元格
使用 GtkCellRendererText,您不仅可以显示文本,还可以通过双击单元格允许用户直接在树形视图中编辑单个单元格的文本。
要使此功能正常工作,您需要告诉单元格渲染器该单元格是可编辑的,您可以通过将相关文本单元格渲染器的 "editable" 属性设置为 TRUE 来实现。您可以根据每行设置(允许您将每个单独的单元格设置为可编辑或不可编辑),通过属性将 "editable" 属性连接到树模型中的布尔类型列;或者,您可以简单地...
g_object_set(renderer, "editable", TRUE, NULL);
...在创建渲染器时,这会将该特定渲染器列中的所有行设置为可编辑。
现在我们的单元格是可编辑的,我们也希望在单元格被编辑时收到通知。这可以通过连接到单元格渲染器的 "edited" 信号来实现
g_signal_connect(renderer, "edited", (GCallback) cell_edited_callback, NULL);
每当单元格被编辑时,就会调用此回调。我们可以传递一个指向模型的指针作为用户数据以方便起见,而不是传递 NULL,因为我们可能希望将新值存储在模型中。
"edited" 信号的回调看起来像这样(API 参考在这个特定情况下有点缺乏)
void cell_edited_callback (GtkCellRendererText *cell,
gchar *path_string,
gchar *new_text,
gpointer user_data);
树路径以字符串形式传递到 "edited" 信号回调。您可以使用 gtk_tree_path_new_from_string 将其转换为 GtkTreePath,或使用 gtk_tree_model_get_iter_from_string 将其转换为迭代器。
请注意,单元格渲染器不会为您更改存储中的数据。在单元格被编辑后,您只会收到 "edited" 信号。如果您不更改存储中的数据,旧文本将再次呈现,就好像什么也没发生一样。
如果您有多个(渲染器)列包含可编辑单元格,则无需为每个渲染器提供不同的回调,您可以对所有渲染器使用相同的回调,并将一些数据附加到每个渲染器,以便您以后在回调中检索这些数据以了解哪个渲染器/列已被编辑。例如,这样做
renderer = gtk_cell_renderer_text_new();
...
g_object_set_data(G_OBJECT(renderer), "my_column_num", GUINT_TO_POINTER(COLUMN_NAME));
...
renderer = gtk_cell_renderer_text_new();
...
g_object_set_data(G_OBJECT(renderer), "my_column_num", GUINT_TO_POINTER(COLUMN_YEAR_OF_BIRTH));
...
其中 COLUMN_NAME 和 COLUMN_YEAR_OF_BIRTH 是枚举值。然后,您可以在回调中使用以下方法获取列号
guint column_number = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(renderer), "my_column_num"));
您可以使用此机制将各种自定义数据附加到任何对象或小部件,并使用您喜欢的字符串标识符。
gtk-demo 中有一个可编辑单元格的良好示例,它是 Gtk+ 源代码树的一部分(位于 gtk+-2.x.y/demos/gtk-demo)。
您可以使用 gtk_tree_view_set_cursor(或者如果您在同一个树形视图列中打包了多个可编辑单元格渲染器,则使用 gtk_tree_view_set_cursor_on_cell)将光标移动到树形视图中的特定单元格,并且如果您想开始编辑该单元格。同样,您可以使用 gtk_tree_view_get_cursor 获取当前行和焦点列。使用 gtk_widget_grab_focus(treeview) 将确保树形视图具有键盘焦点。
正如 API 参考中指出的,树形视图需要被实现才能进行单元格编辑。换句话说:如果您想在程序启动时立即开始编辑特定单元格,您需要使用 g_idle_add 设置一个空闲超时,以便在窗口和其他所有内容都实现后立即执行此操作(在超时中返回 FALSE 以使其只运行一次)。或者,您可以使用 g_signal_connect_after 连接到树形视图的 "realize" 信号以实现相同的效果。
连接到树形视图的 "cursor-changed" 和/或 "move-cursor" 信号以跟踪光标的当前位置。
就像您可以设置 GtkCellRendererText 可编辑一样,您可以通过设置 "activatable" 属性来指定 GtkCellRendererToggle 是否应该在被点击时更改其状态 - 可以在创建渲染器时(在这种情况下,该列中的所有单元格都可点击)或通过属性将渲染器属性连接到模型的布尔类型列来实现。
连接到切换单元格渲染器的 "toggled" 信号,以便在用户点击切换按钮(或单选按钮)时收到通知。用户点击不会更改存储中的值,也不会更改渲染的值的外观。切换按钮只有在您更新存储中的值时才会更改状态。在此之前,它将处于 "不一致" 状态,这也是您应该从模型而不是从单元格渲染器中读取该单元格的当前值的原因。
"toggled" 信号的回调看起来像这样(API 参考在这个特定情况下有点缺乏)
void cell_toggled_callback (GtkCellRendererToggle *cell,
gchar *path_string,
gpointer user_data);
就像文本单元格渲染器的 "edited" 信号一样,树路径以字符串形式传递到 "toggled" 信号回调。您可以使用 gtk_tree_path_new_from_string 将其转换为 GtkTreePath,或使用 gtk_tree_model_get_iter_from_string 将其转换为迭代器。
尽管 GtkSpinButton 实现了 GtkCellEditable 接口(就像 GtkEntry 一样),但没有简单的方法可以获得在编辑模式下使用旋转按钮而不是普通条目 的单元格渲染器。
要获得此功能,您需要编写一个新的单元格渲染器,其工作原理与 GtkCellRendererText 非常相似,或者您需要编写一个新的单元格渲染器类,该类派生自文本单元格渲染器,并在编辑模式下更改行为。
最干净的解决方案可能是编写一个 'CellRendererNumeric',它执行文本单元格渲染器执行的所有操作,只是它有一个浮点类型属性,而不是 "text" 属性,还有一个额外的位数属性。但是,似乎还没有人这样做,因此您需要自己编写一个,或者找到另一种方法来在编辑模式下获得旋转按钮。
在本教程的代码示例中,有一个基于 GtkCellRendererText 的 CellRendererSpin 伪造实现,它在编辑模式下显示旋转按钮。但是,该实现不是很好,因此您需要确保它在您的特定环境中有效,并在需要时对其进行修改。