在回调中访问其他 GTK 小部件时出现分段错误

Segmentation fault while accessing other GTK widgets in a callback

为了在回调中访问其他小部件,我将它们打包在定义如下的结构中:

typedef struct SettingsModel
{
    bool SyncEn;
    int DeviceId;
    int MeasurementId;
    GtkWidget *measurement_type_box;
    GtkWidget *sync_switch;
    GtkWidget *device_type_box;
} SettingsModel;

目的是在单击保存按钮时更新设置。相关代码片段:

static void click_save(GtkButton *self, gpointer user_data)
{
    SettingsModel *local_data = user_data;
    local_data->DeviceId = gtk_combo_box_get_active(GTK_COMBO_BOX(local_data->device_type_box));
    local_data->MeasurementId = gtk_combo_box_get_active(GTK_COMBO_BOX(local_data->measurement_type_box));
    local_data->SyncEn = gtk_switch_get_active(GTK_SWITCH(local_data->sync_switch));
}
save_settings_button = gtk_button_new_with_label("Save");
reset_settings_button = gtk_button_new_with_label("Reset");
SettingsModel settings;
settings.device_type_box = device_type_box; 
settings.measurement_type_box = measurement_type_box;
settings.sync_switch = sync_enabled_switch;

g_signal_connect(save_settings_button, "clicked", G_CALLBACK(click_save), &settings);
g_signal_connect(reset_settings_button, "clicked", G_CALLBACK(click_reset), &settings);

但是,当我点击保存按钮时,我收到以下错误:

(gui:28351): GLib-GObject-WARNING **: 16:52:26.673: invalid cast from 'GdkButtonEvent' to 'GtkComboBox'

(gui:28351): Gtk-CRITICAL **: 16:52:26.673: gtk_combo_box_get_active: assertion 'GTK_IS_COMBO_BOX (combo_box)' failed
zsh: segmentation fault  ./gui

似乎结构初始化错误或 GTK 不知何故认为回调应该采用 GdkButtonEvent?

此处可复制的最小示例:

#include <gtk/gtk.h>

enum 
{
    COL_ID = 0,
    COL_NAME,
    NUM_COLS
};

typedef struct SettingsModel
{
    bool SyncEn;
    int DeviceId;
    int MeasurementId;
    GtkWidget *measurement_type_box;
    GtkWidget *sync_switch;
    GtkWidget *device_type_box;
} SettingsModel;

static void click_save(GtkButton *self, gpointer user_data)
{
    SettingsModel *local_data = user_data;
    local_data->DeviceId = gtk_combo_box_get_active(GTK_COMBO_BOX(local_data->device_type_box));
    local_data->MeasurementId = gtk_combo_box_get_active(GTK_COMBO_BOX(local_data->measurement_type_box));
    local_data->SyncEn = gtk_switch_get_active(GTK_SWITCH(local_data->sync_switch));
}

static void click_reset(GtkButton *btn_reset, gpointer user_data)
{
    SettingsModel *local_data = user_data;
    local_data->DeviceId = 0;
    local_data->MeasurementId = 0;
    local_data->SyncEn = false;
    gtk_switch_set_active(GTK_SWITCH(local_data->sync_switch), FALSE);
    gtk_combo_box_set_active(GTK_COMBO_BOX(local_data->device_type_box), 0);
    gtk_combo_box_set_active(GTK_COMBO_BOX(local_data->measurement_type_box), 0);
}

static void app_activate(GApplication *app, gpointer *user_data)
{
    GtkWidget *win;
    GtkWidget *grid;

    g_assert(GTK_IS_APPLICATION(app));
    win = gtk_application_window_new(GTK_APPLICATION(app));
    grid = gtk_grid_new();

    GtkWidget *device_type_box;
    GtkWidget *measurement_type_box;
    GtkWidget *save_settings_button;
    GtkWidget *reset_settings_button;
    GtkWidget *sync_enabled_switch;
    GtkListStore *list_store_device;
    GtkListStore *list_store_measurement;
    GtkCellRenderer *column;

    list_store_device = gtk_list_store_new(NUM_COLS, G_TYPE_INT, G_TYPE_STRING);
    gtk_list_store_insert_with_values(list_store_device, NULL, -1, COL_ID, 0, COL_NAME, "foo", -1);
    device_type_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store_device));
    g_object_unref(list_store_device);
    column = gtk_cell_renderer_text_new();
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(device_type_box), column, TRUE);
    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(device_type_box), column,
                                "text", 1,
                                NULL);
    gtk_combo_box_set_active(GTK_COMBO_BOX(device_type_box), 0);
    
    list_store_measurement = gtk_list_store_new(NUM_COLS, G_TYPE_INT, G_TYPE_STRING);
    gtk_list_store_insert_with_values(list_store_measurement, NULL, -1, COL_ID, 0, COL_NAME, "foo", -1);
    gtk_list_store_insert_with_values(list_store_measurement, NULL, -1, COL_ID, 0, COL_NAME, "bar", -1);
    gtk_list_store_insert_with_values(list_store_measurement, NULL, -1, COL_ID, 0, COL_NAME, "something", -1);
    measurement_type_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store_measurement));
    g_object_unref(list_store_measurement);
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(measurement_type_box), column, TRUE);
    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(measurement_type_box), column,
                                "text", 1,
                                NULL);
    gtk_combo_box_set_active(GTK_COMBO_BOX(measurement_type_box), 2);

    save_settings_button = gtk_button_new_with_label("Save");
    reset_settings_button = gtk_button_new_with_label("Reset");

    sync_enabled_switch = gtk_switch_new();

    SettingsModel settings;
    settings.device_type_box = device_type_box; 
    settings.measurement_type_box = measurement_type_box;
    settings.sync_switch = sync_enabled_switch;

    g_signal_connect(save_settings_button, "clicked", G_CALLBACK(click_save), &settings);
    g_signal_connect(reset_settings_button, "clicked", G_CALLBACK(click_reset), &settings);

    gtk_grid_attach(GTK_GRID(grid), device_type_box, 1, 1, 1, 1);    
    gtk_grid_attach(GTK_GRID(grid), measurement_type_box, 1, 2, 1, 1);  
    gtk_grid_attach(GTK_GRID(grid), sync_enabled_switch, 1, 3, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), reset_settings_button, 4, 4, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), save_settings_button, 5, 4, 1, 1);

    gtk_window_set_child(GTK_WINDOW(win), grid);

    gtk_widget_show(win);
}

int main (int argc, char **argv) {
    GtkApplication *app;
    int stat;

    app = gtk_application_new("simon.app", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK(app_activate), NULL);
    stat = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);
    return stat;
}

您正在访问非法内存,因为您将非静态对象的地址传递给您的回调:

static void app_activate(GApplication *app, gpointer *user_data)
{
...
    SettingsModel settings;

    g_signal_connect(save_settings_button, "clicked", G_CALLBACK(click_save), &settings);
    g_signal_connect(reset_settings_button, "clicked", G_CALLBACK(click_reset), &settings);
...
}

当您的信号处理程序被调用时,函数 app_activate 已经返回给调用者并且 settings 不再有效。 您必须提供一些静态内存对象的地址,或者您需要为您的设置动态分配内存。

我建议将 settings 的定义更改为 static SettingsModel settings;