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

GTK4 ColumnView多列排序功能添加失败的技术求助

GTK4 ColumnView 多列排序正确实现方案

核心错误原因

出现g_list_model_get_n_items断言错误,大概率是未用GtkSortListModel包装原始数据模型,直接将GListStore等原始模型绑定到ColumnView导致的。GTK4的排序功能依赖GtkSortListModel作为中间层,负责处理排序逻辑,直接操作原始模型会触发无效访问断言。

分步实现代码

1. 定义数据结构(需注册为GObject类型)

排序需要基于GObject的类型系统,因此自定义数据需继承GObject:

#include <gtk/gtk.h>

typedef struct {
  GObject parent;
  gchar *name;
  gint age;
} Person;

typedef struct {
  GObjectClass parent_class;
} PersonClass;

GType person_get_type(void);
G_DEFINE_TYPE(Person, person, G_TYPE_OBJECT)

static void
person_init(Person *self) {}

static void
person_dispose(GObject *gobject) {
  Person *self = PERSON(gobject);
  g_free(self->name);
  G_OBJECT_CLASS(person_parent_class)->dispose(gobject);
}

static void
person_class_init(PersonClass *klass) {
  G_OBJECT_CLASS(klass)->dispose = person_dispose;
}

Person*
person_new(const gchar *name, gint age) {
  Person *self = g_object_new(PERSON_TYPE, NULL);
  self->name = g_strdup(name);
  self->age = age;
  return self;
}

2. 创建排序模型并绑定原始数据

必须用GtkSortListModel包装原始GListStore,并设置排序比较函数:

int main(int argc, char *argv[]) {
  gtk_init(&argc, &argv);

  // 创建窗口与ColumnView
  GtkWidget *window = gtk_window_new();
  GtkWidget *column_view = gtk_column_view_new(NULL);
  gtk_window_set_child(GTK_WINDOW(window), column_view);
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);

  // 1. 创建原始数据模型
  GListStore *store = g_list_store_new(PERSON_TYPE);
  g_list_store_append(store, person_new("Alice", 30));
  g_list_store_append(store, person_new("Bob", 25));
  g_list_store_append(store, person_new("Charlie", 35));

  // 2. 创建排序模型(核心:包装原始模型)
  GtkSortListModel *sort_model = gtk_sort_list_model_new(G_LIST_MODEL(store), NULL);

  // 定义排序比较函数
  static gint compare_name(gconstpointer a, gconstpointer b, gpointer data) {
    Person *p1 = PERSON(a);
    Person *p2 = PERSON(b);
    return g_strcmp0(p1->name, p2->name);
  }

  static gint compare_age(gconstpointer a, gconstpointer b, gpointer data) {
    Person *p1 = PERSON(a);
    Person *p2 = PERSON(b);
    return p1->age - p2->age;
  }

  // 3. 设置默认排序(按年龄升序)
  GtkSortDescriptor *age_desc = gtk_sort_descriptor_new(NULL, compare_age, GTK_SORT_ASCENDING, NULL);
  gtk_sort_list_model_set_sort_descriptors(sort_model, g_list_append(NULL, age_desc));

3. 配置列与排序触发

给每个ColumnViewColumn绑定对应的排序描述符,实现点击列头切换排序:

// 创建列表项工厂(用于渲染数据)
  static void setup_label(GtkSignalListItemFactory *factory, GtkListItem *item) {
    gtk_list_item_set_child(item, gtk_label_new(NULL));
  }

  static void bind_name(GtkSignalListItemFactory *factory, GtkListItem *item) {
    Person *p = gtk_list_item_get_item(item);
    gtk_label_set_text(GTK_LABEL(gtk_list_item_get_child(item)), p->name);
  }

  static void bind_age(GtkSignalListItemFactory *factory, GtkListItem *item) {
    Person *p = gtk_list_item_get_item(item);
    gchar *age_str = g_strdup_printf("%d", p->age);
    gtk_label_set_text(GTK_LABEL(gtk_list_item_get_child(item)), age_str);
    g_free(age_str);
  }

  // 名字列
  GtkListItemFactory *name_factory = gtk_signal_list_item_factory_new();
  g_signal_connect(name_factory, "setup", G_CALLBACK(setup_label), NULL);
  g_signal_connect(name_factory, "bind", G_CALLBACK(bind_name), NULL);

  GtkColumnViewColumn *name_col = gtk_column_view_column_new("Name", name_factory);
  GtkSortDescriptor *name_desc = gtk_sort_descriptor_new(NULL, compare_name, GTK_SORT_ASCENDING, NULL);
  gtk_column_view_column_set_sort_descriptor(name_col, name_desc);
  gtk_column_view_append_column(GTK_COLUMN_VIEW(column_view), name_col);

  // 年龄列
  GtkListItemFactory *age_factory = gtk_signal_list_item_factory_new();
  g_signal_connect(age_factory, "setup", G_CALLBACK(setup_label), NULL);
  g_signal_connect(age_factory, "bind", G_CALLBACK(bind_age), NULL);

  GtkColumnViewColumn *age_col = gtk_column_view_column_new("Age", age_factory);
  gtk_column_view_column_set_sort_descriptor(age_col, age_desc);
  gtk_column_view_append_column(GTK_COLUMN_VIEW(column_view), age_col);

  // 4. 将排序模型绑定到ColumnView(关键:不能直接绑定原始store)
  gtk_column_view_set_model(GTK_COLUMN_VIEW(column_view), G_LIST_MODEL(sort_model));

  g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  gtk_window_present(GTK_WINDOW(window));
  gtk_main();

  g_object_unref(store);
  return 0;
}

关键注意事项

  • 禁止直接将GListStore等原始模型绑定到ColumnView,必须通过GtkSortListModel中转
  • 确保GtkSortListModel的source模型不为空且未被提前释放
  • 比较函数中必须正确转换数据类型,避免空指针访问
  • 每个可排序的列必须设置sort_descriptor,否则点击列头会无响应或触发错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 07:05:03