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

GTK3 C语言中如何正确将控件数组作为参数传递给回调函数?

问题根源

你的问题核心是栈内存失效:entries是create_Enterprise_form函数内的栈局部数组,当函数执行完毕返回窗口对象后,栈上的entries数组会被系统回收。点击Save按钮触发回调时,访问的是已经失效的内存地址,此时entries指向的内容已经不是原来的GtkEntry对象,所以会出现Element 0 is not a GtkEntry的错误,甚至导致程序崩溃无响应。

而你注释的循环在create_Enterprise_form函数内部执行,此时栈数组还处于有效作用域内,所以能正确识别GtkEntry并打印文本。

解决方案

要解决这个问题,需要把存储GtkEntry的数组放到堆内存或者用GTK推荐的容器(比如GArray)来管理,确保回调触发时数组仍然有效。下面是两种可行的实现方式:

方法1:使用GArray(推荐,符合GLib/GTK编程习惯)

GArray是GLib提供的动态数组,会在堆上分配内存,生命周期可以手动控制,适合存储GTK控件对象。

回调函数修改

// Callback function for the "Save" button
void save_enterprise_website(GtkWidget *button, GArray *entries_array) {
    for (guint i = 0; i < entries_array->len; i++) {
        GtkWidget *entry = g_array_index(entries_array, GtkWidget*, i);
        if (GTK_IS_ENTRY(entry)) {
            const char *text = gtk_entry_get_text(GTK_ENTRY(entry));
            g_print("%s\n", text);
        } else {
            g_print("Element %u is not a GtkEntry\n", i);
        }
    }
}

创建表单函数修改

// Function to create the customization form for the company website template
GtkWidget *create_Enterprise_form(GtkApplication *app) {
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Enterprise_Website");
    gtk_window_set_default_size(GTK_WINDOW(window), 1280, 720);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    GtkWidget *grid = gtk_grid_new();
    gtk_container_add(GTK_CONTAINER(window), grid);

    const char *parameters[] = {
        "Name", "About Us", "Slogan", "Contact",
        "Body Color", "Body Font Family",
        "Header BG Color", "Header Text Color",
        "A Text Color", "Footer BG Color",
        "Footer Text Color", "Hero BG Color"
    };

    // 使用GArray存储GtkEntry,元素大小为GtkWidget*的字节数
    GArray *entries_array = g_array_new(FALSE, FALSE, sizeof(GtkWidget*));

    const char *default_texts[] = {
        "Company Name","A brief description of the company and its history", "A slogan for the company",
        "Address, phone number, contact form, etc.", "#f4f4f4", "Arial, sans-serif", 
        "#333", "#fff", "#fff", "#333", "#fff", "#f4f4f4"
    };

    for (int i = 0; i < G_N_ELEMENTS(parameters); i++) {
        GtkWidget *label = gtk_label_new(parameters[i]);
        GtkWidget *entry = gtk_entry_new();
        gtk_entry_set_text(GTK_ENTRY(entry), default_texts[i]);

        PangoFontDescription *font_desc = pango_font_description_from_string("Sans Bold 14");
        gtk_widget_override_font(label, font_desc);
        gtk_widget_override_font(entry, font_desc);
        pango_font_description_free(font_desc);

        int vertical_spacing = 10;
        gtk_widget_set_margin_bottom(label, vertical_spacing);
        gtk_widget_set_margin_bottom(entry, vertical_spacing);

        // 控件未显示时get_allocated_width返回0,直接设置固定宽度
        gtk_widget_set_size_request(entry, 600, -1);

        gtk_grid_attach(GTK_GRID(grid), label, 0, i, 1, 1);
        gtk_grid_attach(GTK_GRID(grid), entry, 1, i, 1, 1);

        // 将entry添加到GArray
        g_array_append_val(entries_array, entry);
    }

    GtkWidget *submit_button = gtk_button_new_with_label("Save");
    // 将GArray作为user_data传给回调
    g_signal_connect(submit_button, "clicked", G_CALLBACK(save_enterprise_website), entries_array);

    gtk_grid_attach(GTK_GRID(grid), submit_button, 1, G_N_ELEMENTS(parameters), 1, 1);

    gtk_widget_set_halign(grid, GTK_ALIGN_CENTER);
    gtk_widget_set_valign(grid, GTK_ALIGN_CENTER);

    // 将GArray绑定到window对象,窗口销毁时自动释放数组
    g_object_set_data_full(G_OBJECT(window), "entries_array", entries_array, (GDestroyNotify)g_array_unref);

    return window;
}

方法2:使用堆分配数组

如果不想用GArray,可以用g_malloc在堆上分配数组,同样能避免栈内存失效问题:

回调函数修改

void save_enterprise_website(GtkWidget *button, GtkWidget **entries) {
    for (int i = 0; i < 12; i++) {
        if (GTK_IS_ENTRY(entries[i])) {
            const char *text = gtk_entry_get_text(GTK_ENTRY(entries[i]));
            g_print("%s\n", text);
        } else {
            g_print("Element %d is not a GtkEntry\n", i);
        }
    }
}

创建表单函数修改

GtkWidget *create_Enterprise_form(GtkApplication *app) {
    // ... 其他代码不变 ...

    // 用g_malloc在堆上分配数组
    GtkWidget **entries = g_malloc(sizeof(GtkWidget*) * G_N_ELEMENTS(parameters));

    for (int i = 0; i < G_N_ELEMENTS(parameters); i++) {
        // ... 创建label和entry的代码不变 ...
        entries[i] = entry;
    }

    GtkWidget *submit_button = gtk_button_new_with_label("Save");
    g_signal_connect(submit_button, "clicked", G_CALLBACK(save_enterprise_website), entries);

    // ... 其他代码不变 ...

    // 将数组绑定到window,窗口销毁时释放内存
    g_object_set_data_full(G_OBJECT(window), "entries", entries, g_free);

    return window;
}
额外注意点
  • 原代码中gtk_widget_set_size_request(entry, 3 * gtk_widget_get_allocated_width(entry), -1)存在问题:控件未显示时gtk_widget_get_allocated_width返回0,导致设置宽度为0。建议直接设置固定宽度(比如600),或者在窗口realize信号触发后再调整大小。
  • 使用g_object_set_data_full可以确保窗口销毁时自动释放我们分配的内存,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:24:51