Document `Gtk.Builder` constructor scope argument
I'm in the process of porting a GTK3 application to GTK4. The GTK3 version used Gtk.Builder.connect_signals
, which was removed in GTK4. I've found that the most direct replacement for it would be using a BuilderCScope
.
This works fine when I don't need to access the widget that produced the signal. However, it seems that the widget is never passed in as an argument to the callback, which is especially problematic when defining one callback used by many similar UI elements.
Minimal example
test.ui
<?xml version='1.0' encoding='UTF-8'?>
<interface>
<requires lib="gtk" version="4.6"/>
<object class="GtkApplicationWindow" id="window">
<property name="title">UI XML Test</property>
<child>
<object class="GtkBox">
<child>
<object class="GtkButton">
<property name="label">Button 1</property>
<signal name="clicked" handler="hello"/>
</object>
</child>
<child>
<object class="GtkButton">
<property name="label">Button 2</property>
<signal name="clicked" handler="hello"/>
</object>
</child>
</object>
</child>
</object>
</interface>
main.py
#!/usr/bin/python
import sys
import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk
def hello(button, *a):
print("Hello from " + button.get_label())
class MyApp(Gtk.Application):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.connect('activate', self.on_activate)
def on_activate(self, app):
builder = Gtk.Builder()
# Connect signals using BuilderCScope
scope = Gtk.BuilderCScope()
scope.add_callback_symbol("hello", hello)
builder.set_scope(scope)
builder.add_from_file("test.ui")
self.win = builder.get_object("window")
self.win.set_application(self)
self.win.present()
app = MyApp(application_id="com.example.GtkApplication")
app.run(sys.argv)
Expected behavior
Pressing either button should print Hello from Button 1
or Hello from Button 2
to the console, depending on which button was pressed.
Actual behavior
When pressing either of the buttons, the error TypeError: hello() missing 1 required positional argument: 'button'
is printed to the console, and any further interaction with the window results in a segfault.
Using the C library directly
As a sanity check, I ported the same minimal example application to C:
main.c
/ gcc $(pkg-config --cflags gtk4) $(pkg-config --libs gtk4) -o main main.c
#include <assert.h>
#include <gtk/gtk.h>
#include <glib/gstdio.h>
static void hello (GtkWidget *widget, gpointer data) {
assert(widget != NULL);
GtkButton *button = GTK_BUTTON(widget);
g_print("Hello from %s\n", gtk_button_get_label(button));
}
static void activate (GtkApplication *app, gpointer user_data) {
GtkBuilder *builder = gtk_builder_new();
// Connect signals using BuilderCScope
GtkBuilderScope *scope = gtk_builder_cscope_new();
gtk_builder_cscope_add_callback_symbol(GTK_BUILDER_CSCOPE(scope), "hello", G_CALLBACK(hello));
gtk_builder_set_scope(builder, scope);
gtk_builder_add_from_file(builder, "test.ui", NULL);
GObject *window = gtk_builder_get_object(builder, "window");
gtk_window_set_application(GTK_WINDOW(window), app);
gtk_window_present(GTK_WINDOW(window));
g_object_unref(builder);
}
int main (int argc, char *argv[]) {
GtkApplication *app = gtk_application_new("com.example.GtkApplication", G_APPLICATION_DEFAULT_FLAGS);
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;
}
The C version of the same program does not exhibit the same issue. Clicking on the buttons prints Hello from Button 1
or Hello from Button 2
to the console as expected.
Additional info
I'm using the extra/gtk4 1:4.10.4-1
and extra/python-gobject 3.44.1-4
packages from the Arch Linux repositories.