Understanding GTK Layouts
GUI layout using the GTK UI toolkit with focus on the GtkBuilder XML format. Understanding concepts such as packing, alignment, fill and expand.
The purpose of this article is to create something for lazy people who want to get a good handle on GTK GUI layout quickly without reading and experimenting a lot. We will do that by demonstrating visually how different layout options work in GTK.
First we will focus on the concepts of alignment and packing because these concepts are the least obvious to beginners.
Through most of the examples I will work with a button placed inside a layout container. The button is of type GtkButton and the layout containers I will use in the examples are GtkBox and GtkGrid. Keep in mind that a GtkBox
can be oriented vertical or horizontal.
In the examples we will explore how a GTK component change position, expand and shrink depending on the alignment and packing options we choose.
Describing a GUI with the GtkBuilder XML Format
Before jumping into the examples, it is useful with an quick intro to the XML format used by Glade and GtkBuilder. Glade is a GUI designer application, which stores the GUI you have designed in an XML format which can be read by an instance of the GtkBuilder class.
Below you see a minimalist GUI defined in this format. All this code does is to create an empty window with the title Hello world and with the identifier mywin
. All GTK components can optionally have an identifier using the id
XML attribute.
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="mywin">
<property name="can_focus">False</property>
<property name="title">Hello world</property>
</object>
</interface>
These identifiers make it easier to access GUI components from you code. For instance if the XML file above is called hello.glade
I can load it with Julia code and change the title like this:
using Gtk
builder = GtkBuilder(filename="hello.glade")
win = builder["mywin"]
set_gtk_property!(win, :title, "goodbye mars")
Every GUI object is defined with the object
XML tag. Each object can have a number of properties defined with the property
tag. Every GUI object is placed into a parent container such as a GtkBox
using the child
tag. Within the child
tag you can specify the packing
for the object added as a child.
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="mywin">
<property name="can_focus">False</property>
<property name="title">Hello world</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkSpinButton">
<property name="visible">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
Now that you have some general overview we can go into more specifics. In the following examples, I will edit out the XML code outside the layout tags for clarity.
Aligning a GUI Object Inside a Container Cell
Here is an XML code example of defining a text label that is right aligned and centered vertically in its container cell.
<object class="GtkLabel">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="label">Name</property>
</object>
To better understand how alignment works, I will use an illustration to visualize the relation between the aligned component and its container. The first row shows the effect of setting different values for the halign
(horizontal alignment) property. The second row shows the effect of modifying the values for the valign
(vertical alignment) property.
You may notice that alignment works very similar to how alignment of text in a text works in a word processor or graphical designer tool. Alignment specifies how a GUI object is placed inside the cell it has been placed in. In the illustration you can see that the container has three cells and we have placed the GtkLabel
object in the first cell.
Specify Container Cell with Packing
Packing influences which container cell a GUI object is placed inside. For this reason the properties available to each packed component depends on container type. GtkBox
has different packing properties than GtkGrid
.
pack-type — where adding starts
With pack-type
we can specify whether we are adding components from start to end or opposite. For vertically oriented boxes start
means top, and end
means bottom. With start
a GUI component gets added to the first cell in a container, while end
will add it to the last cell in a container.
<child>
<object class="GtkButton">
<property name="label">click me!</property>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
The meaning of start
and end
will differ depending on whether the GtkBox
is configured to be vertical
or horizontal
(specified with the orientation
property)
position — direct positioning control
To get more fine grained control over where a GUI component is placed, we can use the position
property. This property can be combined with thepack-type
property. This combination only works if pack-typ
has been set to the start
value, otherwise it doesn't make sense.
<child>
<object class="GtkButton">
<property name="label">click me!</property>
</object>
<packing>
<property name="pack-type">start</property>
<property name="position">0</property>
</packing>
</child>
Numbering starts from 0. If the number of children varies and you want one component guaranteed to be placed at the end, it will make more sense to use the pack-type
property with the end
value.
fill & expand — space consumption
The fill
& expand
packing settings influence how much space a GUI component consumes. The button in the code example below consumes all the space within its layout cell, but the layout cell itself does not expand as you increase the size of the parent. In other words, the button will look fixed in size as you resize the containing window.
<child>
<object class="GtkButton">
<property name="label">click me!</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
Setting Margins and Spacing of a Container
You can specify margins and spacing for a container. The margins of a container creates space between the content of the container and its parent.
The spacing, in contrast, is between the children of the container.
<object class="GtkBox">
<property name="margin-start">10</property>
<property name="margin-top">6</property>
<property name="orientation">vertical</property>
<property name="spacing">15</property>
<child>
...
</child>
<child>
...
</child>
</object>
Takeaways
The first settings we looked at such as alignment and packing are related to how an individual GUI component relates to the cell it is placed in. The margin and spacing in contrast work on the container itself. Margin is about the relation to the parent object, while spacing relates to child objects.
Together these properties give you a lot of flexibility in laying out your graphical user interface in GTK.