示例代码中GTK3调用ClosureNotify函数,GTK4为何不调用?
GTK3与GTK4绘图回调销毁通知行为差异的原因
运行以下示例代码及Makefile后,GTK3版本程序会输出“drop!”(即调用ctx_destroy_notify函数),而GTK4版本程序无该输出。即使将窗口显示并关闭的代码替换为g_object_run_dispose(G_OBJECT(window)),结果仍一致。两者行为不同的核心原因如下:
示例C代码
#include <gtk/gtk.h> static void draw_func( GtkDrawingArea* drawing_area, cairo_t *cr, #ifdef GTK3 #else int width, int height, #endif gpointer user_data ) { printf("draw!\n"); } struct Ctx { char *text; GtkWidget *drawing_area; }; static void ctx_destroy_notify( gpointer data #ifdef GTK3 , GClosure* closure #endif ) { struct Ctx *ctx = data; printf("drop!\n"); free(ctx->text); g_object_unref(ctx->drawing_area); free(ctx); } static void activate (GtkApplication* app, gpointer user_data) { GtkWidget *drawing_area = gtk_drawing_area_new(); GtkWidget *window = gtk_application_window_new (app); struct Ctx *ctx = malloc(sizeof(struct Ctx)); ctx->text = strdup("Hallo"); ctx->drawing_area = g_object_ref(drawing_area); #ifdef GTK3 g_signal_connect_data(G_OBJECT(drawing_area), "draw", G_CALLBACK(draw_func), ctx, ctx_destroy_notify, G_CONNECT_AFTER); #else gtk_drawing_area_set_draw_func( GTK_DRAWING_AREA(drawing_area), draw_func, ctx, ctx_destroy_notify); #endif #ifdef GTK3 gtk_container_add (GTK_CONTAINER (window), drawing_area); #else gtk_window_set_child (GTK_WINDOW(window), drawing_area); #endif #ifdef GTK3 gtk_widget_show_all(window); #else gtk_widget_show(window); #endif gtk_window_close(GTK_WINDOW(window)); } int main (int argc, char **argv) { GtkApplication * app = gtk_application_new ("org.example.test", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); g_application_run (G_APPLICATION (app), argc, argv); printf("end of main loop\n"); g_object_unref (app); }
Makefile代码
.PHONY: test test: test3 test4 ./test3 ./test4 test3: test.c gcc -g -O0 -DGTK3 `pkg-config --cflags gtk+-3.0` -o test3 test.c `pkg-config --libs gtk+-3.0` test4: test.c gcc -g -O0 -DGTK4 `pkg-config --cflags gtk4` -o test4 test.c `pkg-config --libs gtk4`
程序输出
./test3 drop! end of main loop ./test4 end of main loop
行为差异原因
GTK3的信号连接机制
在GTK3中,g_signal_connect_data会将销毁函数与信号绑定:当窗口关闭后,容器会销毁子部件drawing_area,部件销毁时会断开所有关联的信号连接,此时触发ctx_destroy_notify,所以会输出“drop!”。GTK4的绘图回调管理逻辑
GTK4的gtk_drawing_area_set_draw_func对销毁函数的触发时机有不同的规则:如果绘图区域从未完成过绘制(比如窗口刚显示就被关闭,还没进入绘制流程),GTK4不会将回调的生命周期与部件销毁强绑定,或者会延迟清理逻辑。而程序在主循环结束后直接退出,导致销毁函数没有执行的机会。
如果在gtk_window_close前添加g_usleep(100000)让主循环运行一小段时间,让绘图区域完成至少一次绘制,GTK4版本会触发ctx_destroy_notify输出“drop!”——因为此时回调已被激活,关联数据的生命周期会和部件绑定。
内容的提问来源于stack exchange,提问作者Donald
相关产品推荐
相关产品推荐

