You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过点击GtkButton从GtkColumnView中移除选中项?

解决GtkColumnView选中条目移除并获取第二列数据的问题

核心修改点

需要调整回调参数传递并重写remove_cb函数,实现选中项的获取、数据读取和移除操作:

  1. 调整回调参数传递:在activate函数中,将SingleSelection对象作为user_data传给remove_cb,方便直接获取选中项:
// 原代码
// g_signal_connect (remove, "clicked", G_CALLBACK (remove_cb), model);
// 修改为
g_signal_connect (remove, "clicked", G_CALLBACK (remove_cb), selection);
  1. 重写remove_cb函数:
void remove_cb (GtkWidget *btn, gpointer user_data)
{
  GtkSingleSelection *selection = GTK_SINGLE_SELECTION(user_data);
  // 判断是否有选中项
  guint pos = gtk_single_selection_get_selected_position(selection);
  if (pos == GTK_INVALID_LIST_POSITION) {
    g_print("未选中任何条目!\n");
    return;
  }

  // 获取选中的自定义条目对象
  TestColItem *item = TESTCOL_ITEM(gtk_single_selection_get_selected_item(selection));
  
  // 方式1:直接访问成员变量获取第二列字符串
  g_print("选中的第二列数据:%s\n", item->str2);
  
  // 方式2:用GObject属性API读取(更符合GObject规范)
  // const char *str2;
  // g_object_get(item, "str2", &str2, NULL);
  // g_print("选中的第二列数据:%s\n", str2);

  // 获取排序模型和底层数据存储
  GtkSortListModel *sort_model = GTK_SORT_LIST_MODEL(gtk_single_selection_get_model(selection));
  GListStore *store = G_LIST_STORE(gtk_sort_list_model_get_model(sort_model));

  // 找到条目在原始存储中的位置并移除
  guint store_pos = g_list_store_find(store, item);
  if (store_pos != G_MAXUINT) {
    g_list_store_remove(store, store_pos);
  }
}

完整修改后的代码

#include <gtk/gtk.h>

#define TESTCOL_TYPE_ITEM (testcol_item_get_type())
G_DECLARE_FINAL_TYPE (TestColItem, testcol_item, TESTCOL, ITEM, GObject)

struct _TestColItem
{
    GObject parent_instance;
     char *str1;
     char *str2;
};

struct _TestColItemClass
{
    GObjectClass parent_class;
};

G_DEFINE_TYPE (TestColItem, testcol_item, G_TYPE_OBJECT)

enum {
    PROP_STR1 = 1,
    PROP_STR2 = 2,
    N_PROPERTIES
};

static void
testcol_item_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
    TestColItem *self = (TestColItem *)object;

    switch (prop_id) {
    case PROP_STR1:
        g_value_set_string(value, self->str1);
        break;
    case PROP_STR2:
       g_value_set_string(value, self->str2);
       break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}

static void
testcol_item_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
    TestColItem *self = (TestColItem *)object;

    switch (prop_id) {
    case PROP_STR1:
        g_free(self->str1);
        self->str1 = g_value_dup_string(value);
        break;
    case PROP_STR2:
        g_free(self->str2);
        self->str2 = g_value_dup_string(value);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    }
}

static void testcol_item_init(TestColItem *item)
{
    item->str1 = NULL;
    item->str2 = NULL;
}


static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };

static void testcol_item_class_init(TestColItemClass *class)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS(class);

    gobject_class->get_property = testcol_item_get_property;
    gobject_class->set_property = testcol_item_set_property;

    obj_properties[PROP_STR1] = g_param_spec_string("str1",
                                                "Str1",
                                                "first column",
                                                NULL,
                                                G_PARAM_READWRITE);

    obj_properties[PROP_STR2] = g_param_spec_string("str2",
                                                "Str2",
                                                "second column",
                                                NULL,
                                                G_PARAM_READWRITE);

    g_object_class_install_properties(gobject_class, N_PROPERTIES, obj_properties);
}


// Set label for factory widget
static void setup_cb(GtkSignalListItemFactory *factory,GObject  *listitem)
{
    GtkWidget *label =gtk_label_new(NULL);
    gtk_list_item_set_child(GTK_LIST_ITEM(listitem),label);
}

// Set text from the column1 passed item for label
static void bind_col1_cb(GtkSignalListItemFactory *factory, GtkListItem *listitem)
{
    GtkWidget *label = gtk_list_item_get_child(listitem);
    TestColItem *item = gtk_list_item_get_item(GTK_LIST_ITEM(listitem));
    const char *string = item->str1;
    g_print("value is %s\n",string); // Show on std out
    gtk_label_set_text(GTK_LABEL (label), string);
}
// Set text from the column2 passed item for label
static void bind_col2_cb(GtkSignalListItemFactory *factory, GtkListItem *listitem)
{
    GtkWidget *label = gtk_list_item_get_child(listitem);
    TestColItem *item = gtk_list_item_get_item(GTK_LIST_ITEM(listitem));
    const char *string = item->str2;
    g_print("value is %s\n",string); // Show on std out
    gtk_label_set_text(GTK_LABEL (label), string);
}

// Create the custom object
static TestColItem * testcol_item_new(const char *str1, const char *str2)
{
    TestColItem  *item = g_object_new(TESTCOL_TYPE_ITEM, NULL);
    item->str1 = g_strdup(str1);
    item->str2 = g_strdup(str2);
    return item;
}   

void remove_cb (GtkWidget *btn, gpointer user_data)
{
  GtkSingleSelection *selection = GTK_SINGLE_SELECTION(user_data);
  guint pos = gtk_single_selection_get_selected_position(selection);
  if (pos == GTK_INVALID_LIST_POSITION) {
    g_print("未选中任何条目!\n");
    return;
  }

  TestColItem *item = TESTCOL_ITEM(gtk_single_selection_get_selected_item(selection));
  g_print("选中的第二列数据:%s\n", item->str2);

  GtkSortListModel *sort_model = GTK_SORT_LIST_MODEL(gtk_single_selection_get_model(selection));
  GListStore *store = G_LIST_STORE(gtk_sort_list_model_get_model(sort_model));

  guint store_pos = g_list_store_find(store, item);
  if (store_pos != G_MAXUINT) {
    g_list_store_remove(store, store_pos);
  }
}

// Activate
static void activate (GtkApplication* app, gpointer user_data)
{
    // Create and populate custom list model
    GListStore *store = g_list_store_new(G_TYPE_OBJECT); 
    g_list_store_append(store,testcol_item_new("1","ghi1"));
    g_list_store_append(store,testcol_item_new("2","aaa2"));
    g_list_store_append(store,testcol_item_new("3","jkl3"));

    // Set Windows in child scrolled window
    GtkWidget *window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Test");
    gtk_window_set_default_size (GTK_WINDOW (window), 200, 300);

    GtkWidget *pBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
    gtk_window_set_child (GTK_WINDOW(window), pBox);


    GtkWidget *scrolled_window = gtk_scrolled_window_new (); 
    gtk_box_append ((GtkBox  *)pBox, scrolled_window); 
    gtk_widget_set_hexpand (scrolled_window, TRUE);
    gtk_widget_set_vexpand (scrolled_window, TRUE);

    // Add column view.  Put in scrolled window.  
    GtkWidget *cv = gtk_column_view_new(NULL); // Null for now want to add sorter
    gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window),cv);
    gtk_column_view_set_show_column_separators (GTK_COLUMN_VIEW (cv),TRUE);
 
    // Setup sort model then overlay with with columnview
    GtkSorter *sorter = g_object_ref (gtk_column_view_get_sorter (GTK_COLUMN_VIEW (cv)));
    GtkSortListModel *model = gtk_sort_list_model_new (G_LIST_MODEL (store), sorter);
    GtkSingleSelection *selection = gtk_single_selection_new (G_LIST_MODEL (model));
    gtk_single_selection_set_autoselect (selection, TRUE);
    gtk_column_view_set_model (GTK_COLUMN_VIEW (cv), GTK_SELECTION_MODEL (selection));

    // Factory setup and bind for column 1 
    GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
    g_signal_connect(factory, "setup", G_CALLBACK(setup_cb),NULL);
    g_signal_connect(factory, "bind", G_CALLBACK(bind_col1_cb),NULL);
    GtkColumnViewColumn *column = gtk_column_view_column_new("Col1", factory);
    sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (TESTCOL_TYPE_ITEM,NULL,"str1")));
    gtk_column_view_column_set_sorter (column, GTK_SORTER (sorter));
    gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);

    // Factory setup and bind for column 2
    factory = gtk_signal_list_item_factory_new();
    g_signal_connect(factory, "setup", G_CALLBACK(setup_cb),NULL);
    g_signal_connect(factory, "bind", G_CALLBACK(bind_col2_cb),NULL);
    column = gtk_column_view_column_new("Col2", factory);
    sorter = GTK_SORTER (gtk_string_sorter_new (gtk_property_expression_new (TESTCOL_TYPE_ITEM, NULL, "str2")));
    gtk_column_view_column_set_sorter (column, GTK_SORTER (sorter));
    gtk_column_view_append_column (GTK_COLUMN_VIEW (cv), column);


    GtkWidget *remove = gtk_button_new_with_label ("Remove");
    gtk_box_append ((GtkBox  *)pBox, remove); 
    // 修改这里,传递selection作为user_data
    g_signal_connect (remove, "clicked", G_CALLBACK (remove_cb), selection);


    // Show window
    gtk_window_present (GTK_WINDOW (window));
}

// Init application
int main (int argc, char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

关键说明

  • 获取选中项:通过GtkSingleSelection的API获取当前选中的条目对象,这是Gtk4中处理列表选中的标准方式。
  • 读取第二列数据:可以直接访问自定义对象的成员变量,或者使用g_object_get读取属性,后者更符合GObject的封装规范,适合后续扩展。
  • 移除条目:由于使用了SortListModel做排序,不能直接用排序后的位置操作原始存储,需要通过g_list_store_find找到条目在GListStore中的真实位置再移除。

内容的提问来源于stack exchange,提问作者dibyendu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 15:58:10