基于GTK4、C和XML自定义Widget模板报错求助
问题:GtkWidget模板使用时触发无分配快照警告
测试GNOME Widget模板示例时,出现以下警告:
(_myWidget:11427): Gtk-WARNING **: 15:16:35.255: Trying to snapshot GtkEntry 0x55ac0c7d7650 without a current allocation (_myWidget:11427): Gtk-WARNING **: 15:16:35.255: Trying to snapshot GtkButton 0x55ac0c7b7b40 without a current allocation
相关代码文件
widget-class.c
#include <gtk/gtk.h> // own class with button and entry // Add the template registration to your class_init function G_DECLARE_FINAL_TYPE (ExampleWidget, example_widget, EXAMPLE, WIDGET, GtkWidget) #define EXAMPLE_TYPE_WIDGET (example_widget_get_type ()) struct _ExampleWidget { GtkWidget parent_type; GtkWidget *entry; GtkWidget *button; }; G_DEFINE_TYPE (ExampleWidget, example_widget, GTK_TYPE_WIDGET) //Clear the template children when disposing the widget instance static void example_widget_dispose (GObject *gobject) { gtk_widget_dispose_template (GTK_WIDGET (gobject), EXAMPLE_TYPE_WIDGET); G_OBJECT_CLASS (example_widget_parent_class)->dispose (gobject); } static void example_widget_class_init (ExampleWidgetClass *klass) { G_OBJECT_CLASS (klass)->dispose = example_widget_dispose; GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); gtk_widget_class_set_template_from_resource (widget_class,"/Prog/widget.ui"); //Bind the widgets defined inside the template file to the corresponding //members of the widget’s instance data structure gtk_widget_class_bind_template_child (widget_class, ExampleWidget, entry); gtk_widget_class_bind_template_child (widget_class, ExampleWidget, button); } //Initialize the template children when initializing the widgdet instance static void example_widget_init (ExampleWidget *self) { gtk_widget_init_template (GTK_WIDGET (self)); // It is now possible to access self->entry and self->button } static GtkWidget * example_widget_new(void) { ExampleWidget *widget = g_object_new(EXAMPLE_TYPE_WIDGET,NULL); return GTK_WIDGET(widget); } /****************************************************************/ static void activate (GtkApplication *app, gpointer user_data) { GtkWidget *window; window = gtk_application_window_new(app); //The example widget is created and installed here GtkWidget *widget_1 = example_widget_new(); gtk_window_set_child(GTK_WINDOW(window),GTK_WIDGET(widget_1)); gtk_window_present(GTK_WINDOW (window)); } int main (int argc, char **argv) { GtkApplication *app; int status; app = gtk_application_new ("org.gtk.examplewidget", G_APPLICATION_DEFAULT_FLAGS); g_signal_connect(app, "activate",G_CALLBACK(activate),NULL); status = g_application_run (G_APPLICATION(app), argc, argv); g_object_unref(app); return status; }
widget.ui
<interface> <template class="ExampleWidget" parent="GtkWidget"> <child> <object class="GtkEntry" id="entry"> </object> </child> <child> <object class="GtkButton" id="button"> <property name="label">Hello</property> </object> </child> </template> </interface>
resource.xml
<gresources> <gresource prefix="Prog"> <file preprocess= "xml-stripblanks">widget.ui</file> </gresource> </gresources>
问题原因与解决方法
问题根因
自定义的ExampleWidget直接继承自GtkWidget,但GtkWidget是最基础的部件类,本身没有布局子部件的能力——它不会为内部的Entry和Button分配尺寸空间,导致这些子部件在没有有效分配的情况下被尝试绘制,从而触发警告,最终子部件也无法正常显示。
解决方法(推荐方案)
让ExampleWidget继承自具备布局能力的容器类,比如GtkBox(GNOME中最常用的盒式布局容器),修改如下:
1. 修改widget-class.c的关键部分
// 把父类从GtkWidget改为GtkBox G_DECLARE_FINAL_TYPE (ExampleWidget, example_widget, EXAMPLE, WIDGET, GtkBox) ... // 对应的G_DEFINE_TYPE也修改父类 G_DEFINE_TYPE (ExampleWidget, example_widget, GTK_TYPE_BOX) // 在初始化函数里设置Box的布局参数 static void example_widget_init (ExampleWidget *self) { gtk_widget_init_template (GTK_WIDGET (self)); // 设置布局方向,比如垂直布局(也可用GTK_ORIENTATION_HORIZONTAL设为水平) gtk_box_set_orientation(GTK_BOX(self), GTK_ORIENTATION_VERTICAL); // 可选:设置子部件之间的间距 gtk_box_set_spacing(GTK_BOX(self), 10); }
2. 修改widget.ui的模板父类
<interface> <!-- parent从GtkWidget改为GtkBox --> <template class="ExampleWidget" parent="GtkBox"> <child> <object class="GtkEntry" id="entry"> </object> </child> <child> <object class="GtkButton" id="button"> <property name="label">Hello</property> </object> </child> </template> </interface>
修改完成后重新编译运行,警告会消失,Entry和Button也能正常显示在窗口中。
备选方案(不推荐)
如果必须继承GtkWidget,需要自己实现measure和size_allocate虚函数,手动计算并为子部件分配尺寸空间,这个过程比较繁琐,适合需要完全自定义布局的场景,一般情况下推荐使用上述容器继承的方案。
内容的提问来源于stack exchange,提问作者Holger
相关产品推荐
相关产品推荐

