You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:35:10