Why do i keep geting TypeError: argument child: Expected Gtk.Widget, but got __main__.GObjectMeta

This is the code I’m running as a small test of implementation for a bigger project.

xml = """\
<interface>
  <template class="wellcomewind" parent="Gtk.Widget">
      <object class="Gtk.Widget">
      <object class="GtkButton" id="hello_button">
        <property name="label">Hello World</property>
        <signal name="clicked" handler="hello_button_clicked" swapped="no" />
      </object>
    </child>
    </object>
  </template>
</interface>
"""
import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw, Gio, GLib


@Gtk.Template(string=xml)
class Foo(Gtk.Widget):
    __gtype_name__ = "wigit"
    hello_button = Gtk.Template.Child()

    @Gtk.Template.Callback()
    def hello_button_clicked(self, *args):
        print("hello world")
        pass







def on_activate(app):
    # … create a new window…
    win = Gtk.ApplicationWindow(application=app)
    # … with a button in it…
    btn = Gtk.Button(label='Hello, World!')
    # … which closes the window when clicked
    btn.connect('clicked', lambda x: print("hello world"))
    win.set_child(Foo)
    win.present()

# Create a new application
app = Gtk.Application(application_id='com.example.GtkApplication')
app.connect('activate', on_activate)

# Run the application



if __name__ == '__main__':

    app.run(None)

the output is

Traceback (most recent call last):
  File "/home/the-game/PycharmProjects/PythonProject2/scractch.py", line 43, in on_activate
    win.set_child(Foo)
TypeError: argument child: Expected Gtk.Widget, but got __main__.GObjectMeta

then it keeps running without any window showing and when i kill it it returns this

Traceback (most recent call last):
  File "/home/the-game/PycharmProjects/PythonProject2/scractch.py", line 43, in on_activate
    win.set_child(Foo)
TypeError: argument child: Expected Gtk.Widget, but got __main__.GObjectMeta
Traceback (most recent call last):
  File "/home/the-game/PycharmProjects/PythonProject2/scractch.py", line 56, in <module>
    app.run(None)
  File "/home/the-game/miniconda3/lib/python3.12/site-packages/gi/overrides/Gio.py", line 46, in run
    with register_sigint_fallback(self.quit):
  File "/home/the-game/miniconda3/lib/python3.12/contextlib.py", line 144, in __exit__
    next(self.gen)
  File "/home/the-game/miniconda3/lib/python3.12/site-packages/gi/_ossighelper.py", line 239, in register_sigint_fallback
    signal.default_int_handler(signal.SIGINT, None)
KeyboardInterrupt

Process finished with exit code 130 (interrupted by signal 2:SIGINT)

what am I doing wrong?

I’m not very good with GTK4, but you’ve got a few problems here:

  1. The XML template is just not correct. Even the syntax is wrong, and I actually get this when trying: (process:3688482): Gtk-WARNING **: 15:11:50.306: Failed to precompile template for class wigit: Error on line 8 char 13: Element “child” was closed, but the currently open element is “object”
  2. Your main concern: in set_child() you need to give an instance, here you’re merely passing it a type. Use set_child(Foo()).
  3. You need to match the __gtype_name__ to the class property of your template. As, is, you’d get (python3:3688853): Gtk-CRITICAL **: 15:13:32.675: Error building template class 'wigit' for an instance of type 'wigit': .:0:0 Parsed template definition for type 'wellcomewind', expected type 'wigit'. It is customary to use the same name as the Python class as well to avoid confusion, yet that’s not enforced.
  4. The type names in the the XML template don’t use . as scope separation: it’s GtkWidget, not Gtk.Widget. Otherwise, you’d get: (python3:3689270): Gtk-CRITICAL **: 15:15:22.172: Error building template class 'wigit' for an instance of type 'wigit': .:0:0 Invalid template parent type 'Gtk.Widget'
  5. Here my lack of GTK4 habit starts to show, but inheriting from GtkWidget directly will require some more work to handle the children. As-is, you’d get (python3:3689513): Gtk-WARNING **: 15:16:45.314: Trying to snapshot GtkButton 0x3bd81490 without a current allocation. How you want to deal with this depends on what you’re trying to achieve, but simply changing the parent from GtkWidget to e.g. GtkBox (and the same in the Python class) will start to work.
1 Like

ok so i fixed 1-4 i think

xml = """\
<interface>
  <template class="wigit" parent="GtkBox">
      <object class="GtkBox">
      <object class="GtkButton" id="hello_button">
        <property name="label">Hello World</property>
        <signal name="clicked" handler="hello_button_clicked" swapped="no" />
      </object>

    </object>
  </template>
</interface>
"""
import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw, Gio, GLib


@Gtk.Template(string=xml)
class Foo(Gtk.Widget):
    __gtype_name__ = "wigit"
    hello_button = Gtk.Template.Child()

    @Gtk.Template.Callback()
    def hello_button_clicked(self, *args):
        print("hello world")
        pass







def on_activate(app):
    # … create a new window…
    win = Gtk.ApplicationWindow(application=app)
    # … with a button in it…
    btn = Gtk.Button(label='Hello, World!')
    # … which closes the window when clicked
    btn.connect('clicked', lambda x: print("hello world"))
    win.set_child(Foo())
    win.present()

# Create a new application
app = Gtk.Application(application_id='com.example.GtkApplication')
app.connect('activate', on_activate)

# Run the application



if __name__ == '__main__':

    app.run(None)

i dont understant what " GtkWidget directly will require some more work to handle the children" means
and im geting

python:150590): Gtk-CRITICAL **: 13:35:50.734: Error building template class 'wigit' for an instance of type 'wigit': .:0:0 Invalid template parent type 'GtkBox'
Traceback (most recent call last):
  File "/home/the-game/miniconda3/lib/python3.12/site-packages/gi/_gtktemplate.py", line 170, in <lambda>
    lambda s: init_template(s, cls, base_init_template)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/the-game/miniconda3/lib/python3.12/site-packages/gi/_gtktemplate.py", line 193, in init_template
    raise RuntimeError(
RuntimeError: Handler 'hello_button_clicked' was declared with @Gtk.Template.Callback but was not present in template
Traceback (most recent call last):
  File "/home/the-game/PycharmProjects/PythonProject2/scractch.py", line 57, in <module>
    app.run(None)
  File "/home/the-game/miniconda3/lib/python3.12/site-packages/gi/overrides/Gio.py", line 46, in run
    with register_sigint_fallback(self.quit):
  File "/home/the-game/miniconda3/lib/python3.12/contextlib.py", line 144, in __exit__
    next(self.gen)
  File "/home/the-game/miniconda3/lib/python3.12/site-packages/gi/_ossighelper.py", line 239, in register_sigint_fallback
    signal.default_int_handler(signal.SIGINT, None)
KeyboardInterrupt

but now a empty window is displayed so we got some progress

You now don’t have the same parent type in the template and in your Python class (GtkBox vs. Gtk.Widget), yet those need to match.

Once that’s fixed, you’ll notice that you get a complaint about the XML not being correct. Basically, replace your <object class="GtkBox">…</object> with <child>…</child>: child objects need to be wrapped in a child element, and your current inner GtkBox don’t seem currently useful. See also Gtk.Builder

back to the original error

xml = """\
<interface>
  <template class="wigit" parent="GtkWidget">
      <child>
      <object class="GtkButton" id="hello_button">
        <property name="label">Hello World</property>
        <signal name="clicked" handler="hello_button_clicked" swapped="no" />
      </object>
    </child>
  </template>
</interface>
"""
import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw, Gio, GLib


@Gtk.Template(string=xml)
class Foo(Gtk.Widget):
    __gtype_name__ = "wigit"
    hello_button = Gtk.Template.Child()

    @Gtk.Template.Callback()
    def hello_button_clicked(self, *args):
        print("hello world")
        pass







def on_activate(app):
    # … create a new window…
    win = Gtk.ApplicationWindow(application=app)
    # … with a button in it…
    btn = Gtk.Button(label='Hello, World!')
    # … which closes the window when clicked
    btn.connect('clicked', lambda x: print("hello world"))
    win.set_child(Foo())
    win.present()

# Create a new application
app = Gtk.Application(application_id='com.example.GtkApplication')
app.connect('activate', on_activate)

# Run the application



if __name__ == '__main__':

    app.run(None)

I’m using temples because I couldn’t figure out buttons without using temples so please tell me if there is a better way to integrate buttons

In your last snippet, just replace the parent type from GtkWidget to GtkBox in both the template and the Python class and it’ll work.

I’m using temples because I couldn’t figure out buttons without using temples so please tell me if there is a better way to integrate buttons

Templates are fine, and usually a good idea when you’re building a non-trivial UI with fixed elements (e.g. not entirely dynamic). Again, just change your last example to use a GtkBox as parent rather than a GtkWidget, as the latter requires a bit more work for displaying children, and is rather meant for custom UI elements than simple children placement (use Gtk.Grid or Gtk.Box for most common cases).

However, buttons shouldn’t be a problem manually either: you can create them with Gtk.Button() and then add them to whichever container you like. E.g.

Sample with a manual button

class Foo(Gtk.Box):
    __gtype_name__ = "wigit"

    def __init__(self):
        super().__init__(orientation=Gtk.Orientation.VERTICAL)

        self.hello_button = Gtk.Button(label="Hello World")
        self.hello_button.connect('clicked', self.hello_button_clicked)
        self.append(self.hello_button)

    def hello_button_clicked(self, *args):
        print("hello world")

Same error, i probably messed up the GtkBox replacement

xml = """\
<interface>
  <template class="wigit" parent="GtkBox">
      <child>
      <object class="GtkButton" id="hello_button">
        <property name="label">Hello World</property>
        <signal name="clicked" handler="hello_button_clicked" swapped="no" />
      </object>
    </child>
  </template>
</interface>
"""
import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw, Gio, GLib


@Gtk.Template(string=xml)
class Foo(Gtk.Box):
    __gtype_name__ = "wigit"
    hello_button = Gtk.Template.Child()

    @Gtk.Template.Callback()
    def hello_button_clicked(self, *args):
        print("hello world")
        pass







def on_activate(app):
    # … create a new window…
    win = Gtk.ApplicationWindow(application=app)
    # … with a button in it…
    btn = Gtk.Button(label='Hello, World!')
    # … which closes the window when clicked
    btn.connect('clicked', lambda x: print("hello world"))
    win.set_child(Foo())
    win.present()

# Create a new application
app = Gtk.Application(application_id='com.example.GtkApplication')
app.connect('activate', on_activate)

# Run the application



if __name__ == '__main__':

    app.run(None)

What are some good resources for GTK 4?

i got something with

xml = """\
<interface>
  <template class="wigit" parent="GtkBox">
      <child>
      <object class="GtkButton" id="hello_button">
        <property name="label">Hello World</property>
        <signal name="clicked" handler="hello_button_clicked" swapped="no" />
      </object>
    </child>
  </template>
</interface>
"""
import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw, Gio, GLib




'''
class Foo(Gtk.Box):
    __gtype_name__ = "wigit"
    hello_button = Gtk.Template.Child()

    @Gtk.Template.Callback()
    def hello_button_clicked(self, *args):
        print("hello world")
        pass

'''

@Gtk.Template(string=xml)
class Foo(Gtk.Box):
    __gtype_name__ = "wigit"

    def __init__(self):
        super().__init__(orientation=Gtk.Orientation.VERTICAL)

        self.hello_button = Gtk.Button(label="Hello World")
        self.hello_button.connect('clicked', self.hello_button_clicked)
        self.append(self.hello_button)

    def hello_button_clicked(self, *args):
        print("hello world")






def on_activate(app):
    # … create a new window…
    win = Gtk.ApplicationWindow(application=app)
    # … with a button in it…
    btn = Gtk.Button(label='Hello, World!')
    # … which closes the window when clicked
    btn.connect('clicked', lambda x: print("hello world"))
    win.set_child(Foo())
    win.present()

# Create a new application
app = Gtk.Application(application_id='com.example.GtkApplication')
app.connect('activate', on_activate)

# Run the application



if __name__ == '__main__':

    app.run(None)

there are two buttons but only the one added by manulaly woks and the one added by UI doesn’t

For me, your last 2 examples both work. In the last indeed only the manual button works, but that’s because you removed the Gtk.Template.Callback() decorator, so it isn’t automatically connected for you. Add it back and they’ll both work.

If you have specific issues, please post the error messages, “Same error” is not very clear when there was many iterations, and especially as for me that snippet just work.

Remember though that it’ll still need to be cleaned up, for example, you have an unused button in on_activate().