With the removal of Gtk.Container between Gtk3 and Gtk4, container widgets no longer implement add_child themselves. The Gtk.Buildable interface has an add_child function.
In gjs (well, typescript) with Gtk4, the following works:
function _setChildren(widget: Gtk.Widget, children: any[]) {
children = children.flat(Infinity).map(ch => ch instanceof Gtk.Widget
? ch
: new Gtk.Label({ visible: true, label: String(ch) }))
for (const child of children) {
widget.vfunc_add_child(
dummyBulder,
child,
type in child ? child[type] : null,
)
}
}
With pygobject, despite Gtk.Box (or other widgets) implementing Gtk.Buildable, I cannot find a way to call the add_child vfunc on them. Gtk.Box does not have a add_child or do_add_child function exposed. What is the intended method of calling interface vfuncs on widgets with pygobject, as is done in the gjs code? I need a generalized way to add a child to any widget implementing Gtk.Buildable in Gtk4.
First of all, all widgets implement GtkBuildable because GtkWidget implements that interface. The GtkBuildable.add_child virtual method is meant to be used to add a “child object” to a GtkBuildable instance inside UI definition files; for instance, that’s how <child> is implement by GtkWidget: GTK will call gtk_widget_set_parent() on any widget defined with <child>, but you can also use <child> to add ancillary objects like event controllers. Scaffolding widgets that can contain an arbitrary amount of widgets will override add_child() to call widget-specific API; for instance, GtkBox overrides GtkBuildable.add_child() in order to call gtk_box_append(). Another use case is for implementing specific child types, like “start” or “end” children.
If you want to implement GtkBuildable.add_child for generic children or custom child types in a widget with PyGObject, you override the virtual function in the same way as you’d override any class virtual function: by adding do_ to the name of the method. For instance (untested code):
class YourWidget(Gtk.Widget, Gtk.Buildable):
# ...
def do_add_child(self, builder, child, type):
if type == 'start':
self.add_start_child(child)
elif type == 'end':
self.add_end_child(child)
else:
# Always chain up to the parent's implementation
Gtk.Widget.do_add_child(self, builder, child, type)
Incidentally, this is not recommended any more for child types; if your widget accepts a specific child type in the UI definition data you want to use properties instead:
Sorry, in my original question I pasted the _getChildren function instead of the _setChildren function.
in the (now correct) gjs code, vfunc_add_child is called on a parent for each widget in an array of widgets. My issue is that pygobject does not expose gtk_buildable_add_child, however gjs does.
That’s just not a thing: gtk_buildable_add_child() is not a public symbol in GTK4. You cannot call it, and it’s not exposed in the introspection ABI that is consumed by language bindings like GJS and PyGObject. I have no idea what you’re calling.
So, let’s step back: what are you actually trying to achieve?
oh, what on earth is widget.vfunc_add_child(...) doing, then?
what I want is a generic means of setting a list of child widgets on a parent widget. effectively, I wish to port the following code for Gtk3 to Gtk4:
def _set_children(self, children):
children = map(lambda x: x if isinstance(x, Gtk.Widget) else Gtk.Label(visible=True, label=x), children)
if isinstance(self, Gtk.Container):
for child in self.get_children():
self.remove(child)
for child in children:
self.add(child)
else:
raise TypeError(f"{self.__gtype_name__} is not an instance of Gtk.Container")