{"id":1290,"date":"2021-01-29T13:21:24","date_gmt":"2021-01-29T12:21:24","guid":{"rendered":"http:\/\/hobbykeller.spdns.de\/?p=1290"},"modified":"2021-02-05T15:19:05","modified_gmt":"2021-02-05T14:19:05","slug":"creating-custom-widgets-for-pygtk-in-glade","status":"publish","type":"post","link":"https:\/\/hobbykeller.spdns.de\/?p=1290","title":{"rendered":"Creating custom widgets for PyGTK in Glade"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"alignleft\"><img loading=\"lazy\" decoding=\"async\" width=\"150\" height=\"150\" src=\"http:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2021\/01\/Glade_Custom_Widgets-150x150.png\" alt=\"Glade_Custom_Widgets\" class=\"wp-image-1291\"\/><\/figure><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">When developing GUI applications for Python, <a rel=\"noopener\" href=\"https:\/\/wiki.python.org\/moin\/PyGtk\" target=\"_blank\">PyGTK<\/a> expects the the coder to define the complete layout of the GUI with its objects and the corresponding wiring of events, signals and actions in the code. This can be quite tedious, and fortunately there is <a rel=\"noopener\" href=\"https:\/\/glade.gnome.org\/\" target=\"_blank\">Glade<\/a> which is UI designer that allows to graphically layout a window. It then produces an XML file that can be <a rel=\"noopener\" href=\"https:\/\/python-gtk-3-tutorial.readthedocs.io\/en\/latest\/builder.html#\" target=\"_blank\">added to the Python code<\/a>. One question that remains is: How can I add custom widgets that extend standard Gtk widgets to Glade? This is what I am going to demonstrate in this post.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">1. Definition of &#8220;custom widget&#8221;<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">There are two cases that are covered by the term custom widget:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>A group of two or more standard widgets that are dubbed <strong>composite widgets<\/strong> in Glade. The typical show case for composite widgets is a <a rel=\"noopener\" href=\"https:\/\/people.gnome.org\/~jpu\/docs\/2017-GUADEC\/GUADEC_2017_glade_talk.pdf\" target=\"_blank\" class=\"broken_link\">GtkLabel that serves as a description for a GtkEntry<\/a> next to it (there is also a <a href=\"https:\/\/www.youtube.com\/watch?v=iehwUvSu7JM\" data-type=\"URL\" data-id=\"https:\/\/www.youtube.com\/watch?v=iehwUvSu7JM\" target=\"_blank\" rel=\"noreferrer noopener\">YouTube video<\/a> corresponding to the slides).<\/li><li>An <strong>extension of an existing standard widget<\/strong> that is obtained by sub-classing and exposes and\/or overrides additional properties, methods, signals &#8211; the usual stuff you would expect with sub-classing.<\/li><\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">We will focus on the second case, i.e. a custom widget that we derive as a subclass from a standard Gtk widget. I will demonstrate this with the <code>AwsomeTextView<\/code> subclass that has been derived from the standard Gtk.TextView adapted from this <a rel=\"noreferrer noopener\" href=\"https:\/\/askubuntu.com\/questions\/30496\/how-to-add-a-pygtk-widget-to-the-glade-palette\" data-type=\"URL\" data-id=\"https:\/\/askubuntu.com\/questions\/30496\/how-to-add-a-pygtk-widget-to-the-glade-palette\" target=\"_blank\">askubuntu.com discussion<\/a>. Although this discussion dates back 10 years, for me it&#8217;s still the most useful set of instructions I have been able to find on the net.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">2. What we need to achieve the above<\/h1>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<p class=\"wp-block-paragraph\">We have two challenges to overcome in our custom widget exercise:<\/p>\n<\/div><\/div>\n\n\n\n<ol class=\"wp-block-list\"><li>Organize the housekeeping inside Glade: Glade needs to know what custom widget it should take to its toolbox, from which standard Gtk widget class it is derived, what placeholder icon is used in the toolbox, what properties and methods the widget exposes, what signals it should feature and connect to potential callback functions.<\/li><li>Import the custom widget into the code of our final application. This includes both the import of the code module belonging to our custom subclass as well as the <code>xml<\/code> information for the <code>Gtk.Builder<\/code>.<\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">2.1 Set up Glade to feature a custom widget<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In its current version 3.22, Glade needs two things for processing a custom widget: an <code>xml<\/code> file dubbed <strong>catalog file<\/strong> that holds information such as the name of the widget, the widget group it belongs to, the parent widget it extends, the icon that should be used for the widget in the placeholder, etc.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Secondly, we need a Python file called <strong>module file<\/strong> that holds the usual subclass code of the custom widget: name of the subclass, the parent class it extends from, additional properties and methods exposed, overrides, etc.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.1.1 Catalog XML file<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Older versions expect this file to be saved in some <code>\/usr\/...\/glade\/catalogs<\/code> directory or similar. In Ubuntu 20.04 LTS with the standard <code>apt<\/code> (not snap) installation of Glade, the directory would be <code>\/usr\/share\/glade\/catalogs<\/code>. More recent version of Glade allow to specify extra catalog paths that are scanned for catalog <code>xml<\/code> files.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As the catalog file is also needed for the final application, it is therefore recommendable to save directly in the application directory and add the directory path as an extra catalog path in the Glade Preferences. This way we avoid having to fiddle around with the <code>PYTHONPATH<\/code> environment variable on our system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">From other samples I found on the internet, I have composed the following catalog file <code>awsome_text_view.xml<\/code> for my <code>AwesomeTextView<\/code> widget that I want to add as a custom widget to the Glade toolbox:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"theme:sublime-text font:monospace font-size-enable:false toolbar:2 striped:false nums-toggle:false wrap-toggle:false lang:xhtml decode:true \">&lt;glade-catalog name=\"awesome_text_view\"\n    library=\"gladepython\"\n    domain=\"glade-3\"\n    depends=\"gtk+\"&gt;\n\n &lt;init-function&gt;glade_python_init&lt;\/init-function&gt;\n\n &lt;glade-widget-classes&gt;\n   &lt;glade-widget-class title=\"Awesome TextView\" name=\"AwesomeTextView\"\n                       generic-name=\"awesome_text_view\" parent=\"GtkTextView\"\n                       icon-name=\"widget-gtk-textview\"\/&gt;\n &lt;\/glade-widget-classes&gt;\n\n &lt;glade-widget-group name=\"python\" title=\"Python\"&gt;\n   &lt;glade-widget-class-ref name=\"AwesomeTextView\"\/&gt;\n &lt;\/glade-widget-group&gt;\n&lt;\/glade-catalog&gt;<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">A few words on the structure of catalog files:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The first part is the <strong>catalog header<\/strong> (lines 1-4). Among other attributes, this section includes the <code>catalog<\/code> <code>name<\/code>, the <code>library<\/code> name. Note that the catalog name does not have to be identical to the file name you use for storing the <code>xml<\/code> catalog file. <\/li><li>The <strong>init function<\/strong> (line 6) &#8211; which as far as I can observe &#8211; always seems to be <code>glade_python_init<\/code>. No idea why it must be there. For this demonstration at least, I found out that the init function line can be commented out.<\/li><li>The specification of each <strong>Glade widget class<\/strong> in the <code>&lt;glade-widget-classes><\/code> section. A lot of catalog examples I have come across omit the <code>parent<\/code> property.  This is important though, as it tells Glade from which existing class the new custom widget is expected to inherit. What is also often omitted is the <code>icon-name<\/code> property: This determines what little icon is shown when the toolbox is opened in Glade by clicking the button with the 3 vertical dots next to the <code>Display<\/code> button in the object header of Glade&#8217;s center pane.<\/li><li>Recent versions of Glade also expect the catalog to include a <code>glade-widget-group<\/code> section that determines under which <strong>widget group<\/strong> the widgets in the catalog are shown when clicking on the button with the 3 vertical dots right of the <code>Display<\/code> button. The title settings determines under which global category the widget is listed. The g<code>lade-widget-class-ref<\/code> <code>name<\/code> property has to be in line with the <code>name<\/code> property in the respective <code>glade-widget-class section<\/code> above.<\/li><\/ul>\n\n\n\n<div class=\"wp-block-simple-alerts-for-gutenberg-alert-boxes sab-alert sab-alert-warning\" role=\"alert\">The following question for the catalog file is still unresolved:<br>What&#8217;s the role of the <code>init<\/code> function in line 6?<button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">\u00d7<\/span><\/button><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Note that the catalog file is sufficient for Glade to show an icon in the toolbox, allow the user to drag the icon into the UI design window, work on properties and signals of the custom widgets (at least as far as properties and signals are concerned that are inherited from the base class of the Gtk standard widget) and to save it as a <code>.glade<\/code> file that can later be read by the <code>Gtk.Builder<\/code> of the app using the custom widget.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.1.2 Module Python file<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The module file is a typical Python class file. In our example, it just exposes another <code>set_font()<\/code> method (lines 11-12), to which we can pass a <code>Pango.FontDescription<\/code> compliant string like <code>'Monospace'<\/code>:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"theme:sublime-text font:monospace font-size-enable:false toolbar:2 nums-toggle:false wrap-toggle:false lang:python decode:true \" title=\"Module Python class file for custom widget in Glade\">from gi.repository import Gtk, Pango\n\n\nclass AwesomeTextView(Gtk.TextView):\n\n    __gtype_name__ = 'AwesomeTextView'\n\n    def __init__(self):\n        Gtk.TextView.__init__(self)\n\n    def set_font(self, font_description):\n        self.modify_font(Pango.FontDescription(font_description))<\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">On my Ubuntu, Glade has a <code>module<\/code> directory under <code>\/usr\/x86_64-linux-gnu\/glade\/modules<\/code> where module files traditionally were expected to save. For the sake of simplicity, we will put the module file directly into the application development directory.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Note that prior to subclassing, we have to import <code>Gtk<\/code> (and in our special case Pango) from the <code>gi.repository<\/code> (line 1).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>__gtype_name__<\/code> assignment (line 6) is related to the <a rel=\"noreferrer noopener\" href=\"https:\/\/pygobject.readthedocs.io\/en\/latest\/guide\/api\/gobject.html\" data-type=\"URL\" data-id=\"https:\/\/pygobject.readthedocs.io\/en\/latest\/guide\/api\/gobject.html\" target=\"_blank\">type identification in Python<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-simple-alerts-for-gutenberg-alert-boxes sab-alert sab-alert-primary\" role=\"alert\">I assume that the <code>__gtype_name__<\/code> (line 6) is important because it is the link between the class declaration in the catalog file and the class code in the module file. Therefore <code>__gtype_name__<\/code> must be assigned the same string as the <code>glade-widget-class<\/code> attribute name in the catalog file (line 9 in the catalog file of section 2.1.1).<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">2.2 The &#8220;test driver&#8221; app<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now lets test if our <code>AwesomeTextView<\/code> custom widget behaves as expected when we make use of it in an app. For sake of simplicity we will keep all files in the same development directory. What we need is a <code>.glade<\/code> file that defines what our GUI looks like and a Python application code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.2.1 Glade output file<\/h3>\n\n\n\n<div class=\"wp-block-media-text alignwide is-stacked-on-mobile\" style=\"grid-template-columns:22% auto\"><figure class=\"wp-block-media-text__media\"><img loading=\"lazy\" decoding=\"async\" width=\"454\" height=\"391\" src=\"http:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2021\/02\/glade_extra_cat_path.png\" alt=\"\" class=\"wp-image-1445 size-full\" srcset=\"https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2021\/02\/glade_extra_cat_path.png 454w, https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2021\/02\/glade_extra_cat_path-300x258.png 300w\" sizes=\"auto, (max-width: 454px) 100vw, 454px\" \/><\/figure><div class=\"wp-block-media-text__content\">\n<p class=\"wp-block-paragraph\">The first thing we have to do is make Glade aware that there is a custom catalog with information on our <code>AwesomeTextView<\/code> widget waiting for it in our app development directory. In order to achieve this, we open Glade, pick <code>Preferences<\/code> from the main menu and enter the path to our app development directory holding the custom catalog xml file under <code>Extra catalog paths<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Remember: The custom catalog file is the file we created in section 2.1.1. Make sure that the module file from section 2.1.2 is also located in this directory.<\/p>\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Saving our GUI in Glade leaves us with a <code>.glade<\/code> output file which is actually another xml file. In the next step we will have the <code>Gtk.Builder<\/code> class read all We can pass to the <code>Gtk.Builder<\/code> of our application. Although this file has a <code>.glade<\/code> extension, it is in reality an <code>xml<\/code> file and can be opened with any text editor. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Unlike out catalog file and the module file, this file is an output file by Glade and not something we have to feed into Glade &#8211; but as we need inject the trinity of catalog file, module file and Glade output file into our application code, I have presented the file here.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The test.glade file for our application looks as follows:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"theme:sublime-text font:monospace font-size-enable:false height-set:true height:300 toolbar:2 nums-toggle:false wrap-toggle:false plain:false plain-toggle:false lang:xhtml decode:true \" title=\"Glade output file for our test application\" >&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;!-- Generated with glade 3.22.2 --&gt;\n&lt;interface&gt;\n  &lt;requires lib=\"gtk+\" version=\"3.20\"\/&gt;\n  &lt;requires lib=\"awesome_text_view\" version=\"0.0\"\/&gt;\n  &lt;object class=\"GtkWindow\" id=\"win1\"&gt;\n    &lt;property name=\"can_focus\"&gt;False&lt;\/property&gt;\n    &lt;signal name=\"delete-event\" handler=\"on_win1_delete_event\" swapped=\"no\"\/&gt;\n    &lt;child type=\"titlebar\"&gt;\n      &lt;placeholder\/&gt;\n    &lt;\/child&gt;\n    &lt;child&gt;\n      &lt;object class=\"GtkBox\"&gt;\n        &lt;property name=\"visible\"&gt;True&lt;\/property&gt;\n        &lt;property name=\"can_focus\"&gt;False&lt;\/property&gt;\n        &lt;property name=\"orientation\"&gt;vertical&lt;\/property&gt;\n        &lt;child&gt;\n          &lt;placeholder\/&gt;\n        &lt;\/child&gt;\n        &lt;child&gt;\n          &lt;object class=\"AwesomeTextView\" id=\"atv1\"&gt;\n            &lt;property name=\"visible\"&gt;True&lt;\/property&gt;\n            &lt;property name=\"can_focus\"&gt;True&lt;\/property&gt;\n          &lt;\/object&gt;\n          &lt;packing&gt;\n            &lt;property name=\"expand\"&gt;False&lt;\/property&gt;\n            &lt;property name=\"fill\"&gt;True&lt;\/property&gt;\n            &lt;property name=\"position\"&gt;1&lt;\/property&gt;\n          &lt;\/packing&gt;\n        &lt;\/child&gt;\n        &lt;child&gt;\n          &lt;placeholder\/&gt;\n        &lt;\/child&gt;\n      &lt;\/object&gt;\n    &lt;\/child&gt;\n  &lt;\/object&gt;\n&lt;\/interface&gt;<\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Note that our glade file in line 5 references our <code>awesome_text_view<\/code> catalog from before. Further note that we have connected a <code>delete_event<\/code> to an handler <code>on_win1_delete_event<\/code> for our application window that we gave the id <code>win1<\/code>. We will have to provide that handler in the Python module file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2.2.2 Python application code<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Our <code>test.py<\/code> code looks as follows:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"theme:sublime-text font:monospace font-size-enable:false height-set:true height:300 toolbar:2 nums-toggle:false wrap-toggle:false plain:false plain-toggle:false lang:default decode:true \" title=\"Application code test.py using our custom AwesomeTextView widget\" >import awsome_text_view\nimport gi\ngi.require_version('Gtk', '3.0')\nfrom gi.repository import Gtk\n\n\nclass App():\n\n    builder = Gtk.Builder()\n\n    def __init__(self):\n        self.builder.add_from_file('test.glade')\n        win1 = self.builder.get_object('win1')\n        atv1 = self.builder.get_object('atv1')\n        atv1.set_font('Verdana')\n        win1.show()\n\n    def on_win1_delete_event(self, widget):\n        Gtk.main_quit()\n\n\nif __name__ == '__main__':\n    app = App()\n    Gtk.main()\n<\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>import<\/code> instruction in line 1 references our module file from section 2.1.2. This module file must be present in our application development directory under the same name &#8211; in this case <code>awsome_text_view.py<\/code>.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">3. Result<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Depending on whether we put &#8216;<code>Monospace<\/code>&#8216; or &#8216;<code>Serif<\/code>&#8216; as a string into the <code>set_font<\/code> method in the application code in line 15, we obtain the following two results for our AwesomeTextView test driver application:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"http:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2021\/01\/AwesomeTextView_Demo-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"519\" height=\"255\" src=\"http:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2021\/01\/AwesomeTextView_Demo-1.png\" alt=\"\" class=\"wp-image-1425\" srcset=\"https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2021\/01\/AwesomeTextView_Demo-1.png 519w, https:\/\/hobbykeller.spdns.de\/wp-content\/uploads\/2021\/01\/AwesomeTextView_Demo-1-300x147.png 300w\" sizes=\"auto, (max-width: 519px) 100vw, 519px\" \/><\/a><figcaption>Demonstration of AwesomeTextView class with different font settings<\/figcaption><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>When developing GUI applications for Python, PyGTK expects the the coder to define the complete layout of the GUI with its objects and the corresponding wiring of events, signals and<span class=\"more-button\"><a href=\"https:\/\/hobbykeller.spdns.de\/?p=1290\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\">Creating custom widgets for PyGTK in Glade<\/span><\/a><\/span><\/p>\n","protected":false},"author":2,"featured_media":1291,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[255,258,254],"tags":[279,277,280,286,288,287,278],"class_list":["post-1290","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gtk","category-gui","category-python","tag-custom-widget","tag-glade","tag-gtk-class-extension","tag-gtk-entry","tag-gtk-2","tag-gtk3","tag-python-gui"],"_links":{"self":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts\/1290","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1290"}],"version-history":[{"count":48,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts\/1290\/revisions"}],"predecessor-version":[{"id":1450,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/posts\/1290\/revisions\/1450"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=\/wp\/v2\/media\/1291"}],"wp:attachment":[{"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1290"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1290"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hobbykeller.spdns.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1290"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}