【发布时间】:2021-02-03 22:07:48
【问题描述】:
我有一个使用 GTK 构建的 Treeview,其中有几个父节点,前两个父节点有几个子节点。第三个父节点有一个复选框,当它处于活动状态时,我希望它折叠第二个父节点并禁用它的扩展器,以便它的子节点不再可见并且无法访问。我在 Stack Overflow 上看到了其他几篇提到类似内容的帖子:
Making rows in a GTK TreeView unselectable
Disable expanding of all top level nodes of a treeview
第一个使用gtk_tree_selection_set_select_function (),第二个使用gtk_tree_model_filter_set_visible_func ()。我尝试了两种方法都无济于事,但第二种方法对我有用(它应该隐藏父节点,而不是停用扩展器)。我想我忽略了一些东西。当我激活 Parent 3 的复选框时,打印语句会从 visible_func 内部出现,但过滤模型不会影响可见性。我想我忽略了visible_func 功能方面的一些东西,但我不确定是什么。一个最小的例子如下:
// gcc -Wall -Wextra -o treeview treeview.c $(pkg-config gtk+-3.0 --cflags --libs) -rdynamic -lm
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
int check1 = 0, check2 = 0, check3 = 0, check4 = 0, check5 = 0, check6 = 0, check7 = 0;
double scale1 = 1.0, scale2 = 1.0, scale3 = 1.0, scale4 = 1.0, scale5 = 1.0, scale6 = 1.0, scale7 = 1.0;
GtkTreeView *treeview;
GtkWidget *view;
bool second_parent_access = true;
// These are the columns in the TreeView, enum is used to easily step through iter (i.e. parent -> child)
enum
{
BUTTON_VIS = 0,
BOLDNESS = 1,
BUTTON = 2,
CURRENT = 3,
SCALE_VIS = 4,
SCALE = 5,
LABEL = 6,
NUM_COLS = 7
};
// Event Handler for GtkCellRendererToggle being toggled
void on_cellrenderertoggle_toggled(__attribute__((unused)) GtkCellRendererToggle *cell_renderer, gchar *path, __attribute__((unused)) gpointer user_data)
{
gboolean value;
gboolean active;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeView *treeview;
treeview = GTK_TREE_VIEW(view);
// Toggle the cell renderer's current state to the logical not
model = gtk_tree_view_get_model(treeview);
if (gtk_tree_model_get_iter_from_string(model, &iter, path))
{
// Get the current state of the toggle button
gtk_tree_model_get(model, &iter, BUTTON, &value, -1);
// Set the state of the toggle button to the opposite of what it was
gtk_tree_store_set(GTK_TREE_STORE(model), &iter, BUTTON, !value, -1);
}
active = !value;
// The form of the path is <parent>:<child> so for example we would have 3:4. We use the token ":" as
// the delimiter and split the string then convert from ASCII to integer.
gchar **split_result = g_strsplit(path, ":", 0); // split_result[0] = parent, split_result[1] = child
gint parent = g_ascii_strtoll(split_result[0], NULL, 10);
g_strfreev(split_result);
if (parent == 2) {
if (active) {
const gchar *path = "1";
GtkTreePath *second_parent_path = gtk_tree_path_new_from_string (path);
gboolean expanded;
// Find out if Second Parent is expanded, if so collapse it
expanded = gtk_tree_view_row_expanded(treeview, second_parent_path);
if (expanded == TRUE)
gtk_tree_view_collapse_row(treeview, second_parent_path);
second_parent_access = false;
}
else
second_parent_access = true;
}
}
// This event handler applies the changed text to the GtkCellRendererSpin cell in the TreeView.
static void scale_cell_edited(GtkCellRendererText *renderer, gchar *path, gchar *new_text, __attribute__((unused)) gpointer user_data)
{
GtkTreeIter iter;
GtkTreeModel *model;
GtkAdjustment *adjustment;
gdouble value;
// Retrieve the current value stored by the spin button renderer's adjustment
g_object_get(renderer, "adjustment", &adjustment, NULL);
value = gtk_adjustment_get_value(adjustment);
model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
// It is the responsibility of the application to update the model and store new_text at the position indicated by path
// This is a string and needs to be converted to a double
value = atof(new_text);
if(gtk_tree_model_get_iter_from_string(model, &iter, path))
{
gtk_tree_store_set(GTK_TREE_STORE(model), &iter, SCALE, value, -1);
}
}
// This function creates and returns the GtkTreeModel (which is the interface needed by the GtkTreeStore)
GtkTreeModel *create_and_fill_model(void)
{
GtkTreeStore *treestore;
GtkTreeIter toplevel, child;
// Creates a new tree store with NUM_COLS columns each of the types passed in
treestore = gtk_tree_store_new(NUM_COLS, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_BOOLEAN,
G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_FLOAT, G_TYPE_INT);
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &toplevel, BUTTON_VIS, FALSE, BOLDNESS, 500, CURRENT,
"First Parent", SCALE_VIS, FALSE, LABEL, 1, -1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, BUTTON_VIS, TRUE, BUTTON, check1, CURRENT,
"First Child", SCALE_VIS, TRUE, SCALE, scale1, LABEL, 2, -1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, BUTTON_VIS, TRUE, BUTTON, check2, CURRENT,
"Second Child", SCALE_VIS, TRUE, SCALE, scale2, LABEL, 3, -1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, BUTTON_VIS, TRUE, BUTTON, check3, CURRENT,
"Third Child", SCALE_VIS, TRUE, SCALE, scale3, LABEL, 4, -1);
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &toplevel, BUTTON_VIS, FALSE, BOLDNESS, 500, CURRENT,
"Second Parent", SCALE_VIS, FALSE, LABEL, 5, -1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, BUTTON_VIS, TRUE, BUTTON, check4, CURRENT,
"First Child", SCALE_VIS, TRUE, SCALE, scale4, LABEL, 6, -1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, BUTTON_VIS, TRUE, BUTTON, check5, CURRENT,
"Second Child", SCALE_VIS, TRUE, SCALE, scale5, LABEL, 7, -1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, BUTTON_VIS, TRUE, BUTTON, check6, CURRENT,
"Third Child", SCALE_VIS, TRUE, SCALE, scale6, LABEL, 8, -1);
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &toplevel, BUTTON_VIS, TRUE, BUTTON, check7, BOLDNESS, 500, CURRENT,
"Third Parent", SCALE_VIS, TRUE, SCALE, scale7, LABEL, 9, -1);
return GTK_TREE_MODEL(treestore);
}
// This function creates the container for the GtkTreeModel (interface) and the GtkTreeStore (data store)
GtkWidget *create_view_and_model()
{
//GtkWidget *view;
GtkTreeModel *model;
view = gtk_tree_view_new();
// COLUMN ONE POINTERS
// Nothing to be rendered
// COLUMN TWO POINTERS
// Nothing to be rendered
// COLUMN THREE POINTERS
GtkTreeViewColumn *column3;
GtkCellRenderer *check_box;
check_box = gtk_cell_renderer_toggle_new();
// COLUMN FOUR POINTERS
GtkTreeViewColumn *column4;
GtkCellRenderer *text;
text = gtk_cell_renderer_text_new();
// COLUMN FIVE POINTERS
// Nothing to be rendered
// COLUMN SIX POINTERS
GtkTreeViewColumn *column6;
GtkCellRenderer *mag_spin;
mag_spin = gtk_cell_renderer_spin_new();
GtkAdjustment * mag_adj;
// COLUMN SEVEN POINTERS
// Nothing to be rendered
// *********COLUMN ONE*********
// This stores a boolean value for visibility of Column THREE - there are no contents to be rendered
// *********COLUMN TWO*********
// This stores an integer value for font weight (i.e. "boldness" of text) of Column FOUR - nothing to render
// *********COLUMN THREE*********
// This is the checkbox column
column3 = gtk_tree_view_column_new();
// Set column title
gtk_tree_view_column_set_title(column3, "");
// Set fixed width for the column
gtk_tree_view_column_set_fixed_width(column3, 50);
// Pack tree view column into tree view
gtk_tree_view_append_column(GTK_TREE_VIEW(view), column3);
// Pack cell renderer into tree view column
gtk_tree_view_column_pack_start(column3, check_box, TRUE);
// Add the check box property to the column
gtk_tree_view_column_add_attribute(column3, check_box, "active", BUTTON);
// Add the visibility attribute to the column
gtk_tree_view_column_add_attribute(column3, check_box, "visible", BUTTON_VIS);
// Connect the signal for clicking the checkboxes in the treeview
g_signal_connect(G_OBJECT(check_box), "toggled", G_CALLBACK(on_cellrenderertoggle_toggled), NULL);
// *********COLUMN FOUR*********
// This is a string column listing the names for channel/current
column4 = gtk_tree_view_column_new();
// Set Column Title
gtk_tree_view_column_set_title(column4, "Label");
// Pack tree view column into tree view
gtk_tree_view_append_column(GTK_TREE_VIEW(view), column4);
// Pack cell renderer into tree view column
gtk_tree_view_column_pack_start(column4, text, TRUE);
// Set the properties on the cell renderer for adjusting boldness
g_object_set(text, "weight-set", TRUE, NULL);
// Add the boldness property to the column
gtk_tree_view_column_add_attribute(column4, text, "weight", BOLDNESS);
// Add the text property to the column
gtk_tree_view_column_add_attribute(column4, text, "text", CURRENT);
// *********COLUMN FIVE*********
// This stores a boolean value for visibility of Column SIX - there are no contents to be rendered
// *********COLUMN SIX*********
// This is a GtkSpinButton column for adjusting the magnification of each current
// Sets the GtkAdjustment for the scale
// Arguments are initial value, min value, max value, step increment, page increment, and page size
mag_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0));
// Set the properties on the cell renderer for adjusting values
g_object_set(mag_spin, "editable", TRUE, "adjustment", mag_adj, "digits", 3, NULL);
// Connect the signal to the event handler that applies the changed text to the GtkCellRendererSpin cell
g_signal_connect(G_OBJECT(mag_spin), "edited", G_CALLBACK(scale_cell_edited), NULL);
// Create the column and add the attributes
column6 = gtk_tree_view_column_new_with_attributes("Spin Button", mag_spin, "text", SCALE, NULL);
// Pack tree view column into tree view
gtk_tree_view_append_column(GTK_TREE_VIEW(view), column6);
// Add the visibility attribute to the column
gtk_tree_view_column_add_attribute(column6, mag_spin, "visible", SCALE_VIS);
// *********COLUMN SEVEN*********
// This stores an integer to label the row for when it needs to be referenced upon
// clicking to show the correct page in the GtkStack.
// *********CREATE THE TreeModel*********
// Hide the column header
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
// Connect the model created above
model = create_and_fill_model();
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
g_object_unref(model);
return view;
}
// This function allows Second Parent to become invisible when the user checks Third Parent's checkbox
static gboolean visible_func (GtkTreeModel *model, GtkTreeIter *iter, __attribute__((unused)) gpointer user_data)
{
GtkTreePath *path;
path = gtk_tree_model_get_path (model, iter);
gchar *path_string = NULL;
path_string = gtk_tree_path_to_string(path);
gboolean visible = TRUE;
if (g_strcmp0(path_string, "2") == 0 && second_parent_access) {
g_print("Turning visibility of Second Parent off.\n");
visible = FALSE;
}
return visible;
}
static void destroy(__attribute__((unused)) GtkWidget *widget, __attribute__((unused)) gpointer user_data)
{
gtk_main_quit ();
}
int main(int argc, char *argv[])
{
// Initialize Gtk - used in lieu of GtkApplication
gtk_init(&argc, &argv);
GtkWidget *window;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
// Link the GtkWidget containing the GtkTreeView to the Window container
view = create_view_and_model();
treeview = GTK_TREE_VIEW(view);
gtk_container_add(GTK_CONTAINER(window), view);
GtkTreeModel *tree_model;
GtkTreeModel *filtered_model;
tree_model = gtk_tree_view_get_model(treeview);
filtered_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (tree_model), NULL);
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filtered_model), visible_func, NULL, NULL);
gtk_tree_view_set_model(treeview, gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(filtered_model)));
g_signal_connect (window, "destroy", G_CALLBACK (destroy), NULL);
// Show Main Window Widget
gtk_widget_show_all(GTK_WIDGET(window));
gtk_main();
return 0;
}
【问题讨论】:
-
我可能遗漏了一些东西,但你不会把孩子们带走吗?
-
那么您是否认为我应该有两个单独的树视图,一个有孩子,一个没有孩子,然后每次都重建?问题是,对于仅停用固有扩展器小部件的小细节,这条路线似乎非常麻烦。起初我以为我可以让它变得不敏感,但 API 中似乎没有任何东西可以做到这一点......
-
扩展器仅在父母有孩子时显示。我想,去掉孩子,扩展器就消失了。