I am building a GTK4 application in Python. My code base consists of custom widgets with .py and .ui files of the same name.
I have built a simple custom widget derived from Gtk.Button, let’s call it MyButton. It serves as a template for multiple buttons in my applications. Its users will listen to its clicked signal to execute some logic. I would like to connect this clicked signal to an action in the corresponding .ui file.
However, I noticed that when doing so for custom widgets, the signal will not get caught, while connecting it in the .py file would work.
Consider the following example code for my button:
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="MyButton" parent="GtkButton">
<property name="width-request">32</property>
<property name="height-request">32</property>
<property name="halign">center</property>
<property name="valign">center</property>
</template>
</interface>
and
import os
import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk
@Gtk.Template(filename=os.path.join(os.path.dirname(__file__), 'my_button.ui'))
class MyButton(Gtk.Button):
__gtype_name__ = 'MyButton'
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.init_template()
Now, I want to use this button in my main application:
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="MainWindow" parent="GtkApplicationWindow">
<property name="title">Custom Widget Test</property>
<property name="default-width">400</property>
<property name="default-height">300</property>
<child>
<object class="GtkBox" id="main_box">
<property name="orientation">vertical</property>
<property name="spacing">20</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="MyButton" id="my_button">
<property name="tooltip-text">Add new item</property>
<property name="icon-name">list-add-symbolic</property>
<signal name="clicked" handler="on_my_button_clicked"/>
</object>
</child>
</object>
</child>
</template>
</interface>
and
import sys
import os
import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gio
from .my_button import MyButton
MyButton.__gtype__ # Force registration of the custom widget type
@Gtk.Template(filename=os.path.join(os.path.dirname(__file__), 'main.ui'))
class MainWindow(Gtk.ApplicationWindow):
__gtype_name__ = 'MainWindow'
MY_button = Gtk.Template.Child()
def __init__(self, app):
super().__init__(application=app)
self.init_template()
def on_my_button_clicked(self, button):
print(f"My button was clicked!")
class TestApp(Gtk.Application):
def __init__(self):
super().__init__(
application_id="com.example.MyButtonTest",
flags=Gio.ApplicationFlags.DEFAULT_FLAGS
)
self.window = None
def do_activate(self):
if not self.window:
self.window = MainWindow(self)
self.window.present()
def do_startup(self):
Gtk.Application.do_startup(self)
def main():
app = TestApp()
return app.run(sys.argv)
if __name__ == "__main__":
exit_code = main()
sys.exit(exit_code)
When you run this, you would notice that nothing happens when clicking the button. However, when changing MainWindow.__init__ to
def __init__(self, app):
super().__init__(application=app)
self.init_template()
self.my_button.connect('clicked', self.on_my_button_clicked)
it will work. What can I do to make it work in the .ui file, too?