Product SiteDocumentation Site

www.Holmes4.com Blogs 1.0

2016-02-26

Aligning Widgets in Python GTK3

Legal Notice

Copyright © 2016 W. David Ashley. All rights reserved.
The text of and illustrations in this document are licensed under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. The original author of this document designates it as the "Attribution Party" for purposes of CC-BY-SA. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
The author and licensor of this document waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
All trademarks are the property of their respective owners.

1.  Introduction

Aligning widgets in GTK has always been a mysterious topic for me. The book and tutorials on GTK always seem to gloss over it or in most case not cover it at all. Containers on the other hand are covered at length. But that does not really help you design a window layout that has the widgets nicely aligned for easy reading of the data.
This blog will show you how to align widgets with GTK 2.x and 3.x. The methods are similar but the implementations are slightly different.

2.  Aligning Widgets with GTK 2.x

The following example shows how to align widgets using a method that is well known. But if you search for examples of using the gtk.Alignment class on the web you will not find many good example. The following Python script is the best example I could come up with that covers most aspects of aligning widgets.

Example 1.  Aligning widgets in GTK 2.x

from __future__ import print_function
import pygtk
pygtk.require('2.0')
import gtk

class example01:
    """example01 class"""

    def delete_event(self, widget, event, data=None):
        return False

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def __init__(self):
        """__init__ Method:

        This method creates the GUI used to present the alignment options."""
        # create a new window
        window1 = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window1.connect("delete_event", self.delete_event)
        window1.connect("destroy", self.destroy)
        window1.set_border_width(10)
        window1.resize(300, 100)
        # build a vbox - this will hold all our other widgets
        vbox1 = gtk.VBox(True, 0)
        vbox1.can_focus = False
        window1.add(vbox1)
        # create two hboxes for the labels
        hbox1 = gtk.HBox(True, 0)
        hbox1.can_focus = False
        hbox1.position = 0
        vbox1.pack_start(hbox1, False, True, 5)
        hbox2 = gtk.HBox(True, 0)
        hbox2.can_focus = False
        hbox2.position = 1
        vbox1.pack_start(hbox2, False, True, 5)
        # build the aligned labels
        label1 = gtk.Label('Top left Aligned')
        label1.can_focus = False
        label1.position = 0
        align1 = gtk.Alignment(0, 0, 0, 0)
        align1.add(label1)
        hbox1.pack_start(align1, False, True, 0)
        label2 = gtk.Label('Top right Aligned')
        label2.can_focus = False
        label2.position = 1
        align2 = gtk.Alignment(1, 0, 0, 0)
        align2.add(label2)
        hbox1.pack_start(align2, False, True, 0)
        label3 = gtk.Label('Bottom left aligned')
        label3.can_focus = False
        label3.position = 0
        align3 = gtk.Alignment(0, 1, 0, 0)
        align3.add(label3)
        hbox2.pack_start(align3, False, True, 0)
        label4 = gtk.Label('Bottom right aligned')
        label4.can_focus = False
        label4.position = 1
        align4 = gtk.Alignment(1, 1, 0, 0)
        align4.add(label4)
        hbox2.pack_start(align4, False, True, 0)
        # show the window
        window1.show_all()

    def main(self):
        gtk.main()


if __name__ == "__main__":
    mainWindow = example01()
    mainWindow.main()
In he case of GTK 2.x the gtk.Alignment class is the object that specifies how the widget it contains should be aligned within a container. The parameters used on the creation of the object specify the x and y alignment. A float value is used to specify the percentage of horizontal or vertical alignment to be used (0 to 1.0). A value of zero specifies that the widget should be fully aligned in the specified direction. Other values specify the amount of free space (as a percentage) to be use between the widget and the fully aligned direction.

Example 2.  Example of aligning widgets

Libvirt Development Team
When you run the example above you should try resizing the window in multiple directions. You will see that the label text keeps its alignment within its container no matter how the window is resized. This is how you accomplish aligning multiple widgets so that they all keep the same alignment no matter how the window is resized.

3.  Aligning Widgets with GTK 3.x

Aligning widgets in GTK 3.x is very different on the surface, but we will be accomplishing the same effect using far fewer classes and with lower overhead.
The first thing to note about a GTK 3.x solution is that several classes we used in the previous example are now deprecated. The gtk.VBox, gtk.HBox, and gtk.Alignment classes are all now deprecated and should not be used in future programs. So how do we place widgets on the screen?
The answer is use the Gtk.Grid class to provide the basic layout and then tell the widget how to align itself using methods that actually existed in GTK 2.x but never seem to be used by anyone. The following example uses these techniques to create a window that works exactly as the GTK 2.x example worked.

Example 3.  Aligning widgets in GTK 3.x

from __future__ import print_function
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

gtk_version = float(str(Gtk.MAJOR_VERSION)+'.'+str(Gtk.MINOR_VERSION))
if gtk_version < 3.16:
    print('There is a bug in versions of GTK older that 3.16.')
    print('Your version is not new enough to prevent this bug from')
    print('causing problems in the display of this solution.')
    exit(0)

class example01:
    """example01 class"""

    def delete_event(self, widget, event, data=None):
        return False

    def destroy(self, widget, data=None):
        Gtk.main_quit()

    def __init__(self):
        """__init__ Method:

        This method creates the GUI used to present the alignment options."""
        # create a new window
        window1 = Gtk.Window()
        window1.connect("delete_event", self.delete_event)
        window1.connect("destroy", self.destroy)
        window1.set_border_width(10)
        window1.resize(300, 100)
        # create a grid
        grid1 = Gtk.Grid()
        grid1.height = 2
        grid1.width = 2
        grid1.set_column_homogeneous(True)
        grid1.set_row_homogeneous(True)
        window1.add(grid1)
        # build the aligned labels
        label1 = Gtk.Label('Top left Aligned')
        label1.can_focus = False
        label1.set_halign(Gtk.Align.START)
        label1.set_valign(Gtk.Align.START)
        grid1.attach(label1, 0, 0, 1, 1)
        label2 = Gtk.Label('Top right Aligned')
        label2.can_focus = False
        label2.set_halign(Gtk.Align.END)
        label2.set_valign(Gtk.Align.START)
        grid1.attach(label2, 1, 0, 1, 1)
        label3 = Gtk.Label('Bottom left Aligned')
        label3.can_focus = False
        label3.set_halign(Gtk.Align.START)
        label3.set_valign(Gtk.Align.END)
        grid1.attach(label3, 0, 1, 1, 1)
        label4 = Gtk.Label('Bottom right Aligned')
        label4.can_focus = False
        label4.set_halign(Gtk.Align.END)
        label4.set_valign(Gtk.Align.END)
        grid1.attach(label4, 1, 1, 1, 1)
        # show the window
        window1.show_all()

    def main(self):
        Gtk.main()


if __name__ == "__main__":
    mainWindow = example01()
    mainWindow.main()

Important

The example directly above will not work on versions of GTK prior to 3.16. There was a bug in the label widget that prevented the proper alignment of the text within the widget space. At the time of the publication of this blog a fully updated Fedora 23 is the only Linux distribution that has the required level of GTK to run this example properly. Other distributions will probably follow soon.
I hope by now you can see why I chose to use a label widget to show how alignment works in GTK. Almost all widget expand their size to fit their container. Thus any alignment you specify is essentially lost because the widget is always the same size as the container (or within the margins of the container).
The first time I tried this example out it did now work! This is because of a bug in the label widget. When you specify an alignment for the widget the label widget actually is realigned but you could not tell that because the text was not realigned within the widget. I racked my brain trying to figure out what dumb mistake I was making. Then I found a bug had been opened in the GTK bug database concerning this very issue. It was finally fixed in version 3.16 of GTK. If you do not have version 3.16 available then you will either have wait for your distribution to update GTK3 of install the libraries manually.
In any case, once you get the proper version of GTK installed/updated the example above will work exactly the same as the GTK 2.x example. The thing to note is the reduced number of classes used in this example. We can do this because it turns out that the gtk.Alignment was really just a surrogate for the set_halign and set_valign methods used in the above example. These are actually virtual functions that really belong to the Gtk.Widget class. All widget are derived from the Gtk.Widget class so all of them can be aligned using these methods.

4.  Conclusion

I hope this helps you understand how alignment works in GTK3. I learned a lot researching this topic and I highly suggest that you fully explore the GTK3 class hierarchy in depth. It has changed enough in GTK3 to make it worth the time you will invest.

5.  Last Note

This blog does not provide a way to automatically post comments. I have made that choice up front in order to keep the noise to a minimum. If you have a comment feel free to drop me an email and be sure to reference the title or date of the blog. Every so often I will gather them up and try to respond to them is a future blog.

6. Revision History

Revision History
Revision 1.0-02016-02-26W. David Ashley
Initial creation.
Revision 1.0-12016-02-27W. David Ashley
Added window example.