GTK3中g_signal_connect_data触发严重错误的排查求助
GTK3适配GTK4 Builder UI文件的问题排查与解决
问题描述
将GTK4入门教程的example-4.c适配为GTK3版本,采用XML格式UI描述文件builder.ui搭配C代码实现,使用编译命令:
gcc `pkg-config --libs --cflags gtk+-3.0` exemple4.c -o hello `pkg-config --libs gtk+-3.0`
运行后主窗口弹出但无内容,反复出现两次错误:
(hello:19840): GLib-GObject-WARNING **: 19:38:31.985: invalid (NULL) pointer instance
(hello:19840): GLib-GObject-CRITICAL **: 19:38:31.985: g_signal_connect_data: assertion 'G_TYPE_CHECK_INSTANCE (instance)' failed
原代码文件
exemple4.c
#include <gtk-3.0/gtk/gtk.h> #include <glib/gstdio.h> static void print_hello (GtkWidget *widget, gpointer data) { g_print ("Hello World "); } static void quit_cb (GtkWindow *window) { gtk_window_close (window); } static void activate (GtkApplication *app, gpointer user_data) { GtkBuilder *builder = gtk_builder_new (); gtk_builder_add_from_file (builder, "builder.ui", NULL); GObject *window = gtk_builder_get_object (builder, "window"); gtk_window_set_application (GTK_WINDOW (window), app); GObject *button = gtk_builder_get_object (builder, "button1"); g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); button = gtk_builder_get_object (builder, "button2"); g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); button = gtk_builder_get_object (builder, "quit"); g_signal_connect_swapped (button, "clicked", G_CALLBACK (quit_cb), window); gtk_widget_show (GTK_WIDGET (window)); g_object_unref (builder); } int main (int argc, char *argv[]) { #ifdef GTK_SRCDIR g_chdir (GTK_SRCDIR); #endif GtkApplication *app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); int status = g_application_run (G_APPLICATION (app), argc, argv); g_object_unref (app); return status; }
builder.ui
<?xml version="1.0" encoding="UTF-8"?> <interface> <object id="window" class="GtkWindow"> <property name="title">Grid</property> <child> <object id="grid" class="GtkGrid"> <child> <object id="button1" class="GtkButton"> <property name="label">Button 1</property> <layout> <property name="column">0</property> <property name="row">0</property> </layout> </object> </child> <child> <object id="button2" class="GtkButton"> <property name="label">Button 2</property> <layout> <property name="column">1</property> <property name="row">0</property> </layout> </object> </child> <child> <object id="quit" class="GtkButton"> <property name="label">Quit</property> <layout> <property name="column">0</property> <property name="row">1</property> <property name="column-span">2</property> </layout> </object> </child> </object> </child> </object> </interface>
问题原因分析
- UI布局语法不兼容:GTK4中
GtkGrid的子控件布局使用<layout>标签,但GTK3不支持该标签,导致UI文件加载失败,所有控件对象(包括按钮)返回NULL,触发信号连接的断言错误,窗口无内容。 - API版本差异:GTK3没有
gtk_window_close()函数,该函数是GTK4新增,调用会导致未定义行为。 - 代码小问题:
g_print中的换行符未正确转义,会引发编译警告;仅调用gtk_widget_show()无法确保子控件全部显示。 - 编译命令冗余:重复添加
pkg-config --libs gtk+-3.0参数,虽不影响编译但无必要。
解决办法
1. 修改builder.ui适配GTK3语法
将<layout>标签替换为GTK3支持的<packing>标签,指定GtkGrid的行列位置:
<?xml version="1.0" encoding="UTF-8"?> <interface> <object id="window" class="GtkWindow"> <property name="title">Grid</property> <property name="default-width">300</property> <property name="default-height">200</property> <child> <object id="grid" class="GtkGrid"> <property name="margin">10</property> <property name="row-spacing">10</property> <property name="column-spacing">10</property> <!-- Button 1 --> <child> <object id="button1" class="GtkButton"> <property name="label">Button 1</property> </object> <packing> <property name="left-attach">0</property> <property name="top-attach">0</property> </packing> </child> <!-- Button 2 --> <child> <object id="button2" class="GtkButton"> <property name="label">Button 2</property> </object> <packing> <property name="left-attach">1</property> <property name="top-attach">0</property> </packing> </child> <!-- Quit Button --> <child> <object id="quit" class="GtkButton"> <property name="label">Quit</property> </object> <packing> <property name="left-attach">0</property> <property name="top-attach">1</property> <property name="width">2</property> </packing> </child> </object> </child> </object> </interface>
2. 修改C代码适配GTK3 API
替换不兼容的API,修复换行符,添加错误检查:
#include <gtk-3.0/gtk/gtk.h> #include <glib/gstdio.h> static void print_hello (GtkWidget *widget, gpointer data) { g_print ("Hello World\n"); // 修复换行符 } static void quit_cb (GtkWindow *window) { gtk_widget_destroy (GTK_WIDGET (window)); // 替换GTK4的gtk_window_close } static void activate (GtkApplication *app, gpointer user_data) { GtkBuilder *builder = gtk_builder_new (); GError *error = NULL; // 添加UI文件加载错误检查 if (!gtk_builder_add_from_file (builder, "builder.ui", &error)) { g_printerr("Failed to load UI file: %s\n", error->message); g_error_free(error); return; } GObject *window = gtk_builder_get_object (builder, "window"); if (!window) { g_printerr("Failed to get window object\n"); g_object_unref(builder); return; } gtk_window_set_application (GTK_WINDOW (window), app); GObject *button = gtk_builder_get_object (builder, "button1"); if (button) { g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); } button = gtk_builder_get_object (builder, "button2"); if (button) { g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); } button = gtk_builder_get_object (builder, "quit"); if (button) { g_signal_connect_swapped (button, "clicked", G_CALLBACK (quit_cb), window); } gtk_widget_show_all (GTK_WIDGET (window)); // 确保所有子控件显示 g_object_unref (builder); } int main (int argc, char *argv[]) { #ifdef GTK_SRCDIR g_chdir (GTK_SRCDIR); #endif GtkApplication *app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); int status = g_application_run (G_APPLICATION (app), argc, argv); g_object_unref (app); return status; }
3. 优化编译命令
去掉重复的libs参数:
gcc `pkg-config --cflags --libs gtk+-3.0` exemple4.c -o hello
验证效果
编译后运行./hello,主窗口将显示三个按钮:点击Button1/Button2会输出Hello World,点击Quit会关闭窗口,无错误输出。
内容的提问来源于stack exchange,提问作者ceceteud
相关产品推荐
相关产品推荐

