在回调中访问其他 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;
为了在回调中访问其他小部件,我将它们打包在定义如下的结构中:
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;