GNOME Builder: Using Python, Libadwaita and Blueprint

codenomad
5 min readDec 4, 2023

--

Now that I got my environment setup, I wanted to learn how to build an app for GNOME using Python. I decided I wanted to use Blueprint so I could avoid defining a UI with XML, and I wanted to use LibAdwaita widgets because… pretty 😍.

I’m picking up right where I left off in my previous post, so if you are following along this should ‘just work’.

First, I’m going to create a sample project. I opened up GNOME Builder, and created a new Python project using the GNOME Application template:

If I build the template application right now I get a basic HelloWorld. Do it. I dare you — if only to make sure GNOME Builder is working in your environment…

Now, I need to set up Blueprint so I can start defining my UIs.

Of course I’d prefer a drag-and-drop / RAD utility, but I haven’t found one that is stable enough. Users coming from OS X (Interface Builder) or Windoze (Visual Studio) will probably have a similar annoyance, but this is where we are. 🙈

Given this, I find Blueprint is a more pleasant way of defining a UI than XML. Bias alert: I hate working with XML files.

From a terminal at the root of my project, I created a few new folders:

mkdir -p subprojects src/view src/model

I’ve seen folks put the .blp files in a data/ui dir, but for now I’d like to keep them near my code. In the src/view folder they go…

You could also avoid using XML and Blueprint altogether by defining your widgets in code. However, keeping the definition outside of the logic code helps mitigate breakage when future library versions are released. Consider… Maybe an alternative to LibAdwaita is released that you want to use, or maybe you have a UX person building a UI for you to consume, or maybe Gtk 5 changes so much that you need a utility to regenerate your UI files altogether. The separation is good practice; it’s MVC.

Code it up

Now, I need to create a configuration file for grabbing the Blueprint compiler when meson kicks off. Per Blueprint’s documentation, I create a wrapper in the subprojects folder:

cat <<EOT >> subprojects/blueprint-compiler.wrap
[wrap-git]
directory = blueprint-compiler
url = https://gitlab.gnome.org/jwestman/blueprint-compiler.git
revision = main
depth = 1

[provide]
program_names = blueprint-compiler
EOT

I don’t want to include this folder in my source control so I added it to my .gitignore:

echo "/subprojects" >> .gitignore

I know I’ll need to re-evaluate this when I decide to use a build pipeline, but for now I’m just trying to figure things out.

Next, I created a new Blueprint file src/view/window.blp and added the following contents:

using Gtk 4.0;
using Adw 1;

template SampleWindow : Adw.ApplicationWindow {
default-width: 600;
default-height: 300;
title: _("Hello, Blueprint!");

Adw.ToolbarView {
[top]
Adw.HeaderBar {}

content: Label label {
label: "Yay, blueprint is setup!";
};
}
}

I moved src/window.pyto view/window.py. For reference it should look like:

from gi.repository import Adw
from gi.repository import Gtk

@Gtk.Template(resource_path='/org/template/sample/window.ui')
class SampleWindow(Adw.ApplicationWindow):
__gtype_name__ = 'SampleWindow'

label = Gtk.Template.Child()

def __init__(self, **kwargs):
super().__init__(**kwargs)

I’m noting the path in the decorator: /org/template/sample. I presume this is referencing the root of the flatpak so I need to generate a window.ui file at the root of my project for Builder to find.

Build Configuration

The build tool, meson, needs to know that I’m using the blueprint compiler to convert .blp into .ui files. I’ll also need a list of blueprint_files to convert. I modify mysrc/meson.build file right above the gnome.compile_resources line adding the following:

# Add a reference to the compiler to use
blueprint_compiler = find_program('blueprint-compiler')

# The blueprint files to be converted
blueprint_files = files(
'view/window.blp'
)

As you might expect, this list should grow as additional .blp files are created…

Now that I established which files meson should convert, I need to tell it how to convert them. I added this bit of configuration right below the blueprint_files variable:

# The list of the converted .ui files to inject into the build
ui_files = []

foreach blueprint_file : blueprint_files
path_as_string = '@0@'.format(blueprint_file)
filename = path_as_string.split('/')[-1]

ui_file = custom_target(path_as_string.underscorify(),
input: blueprint_file,
output: filename.replace('.blp', '.ui'),
command: [blueprint_compiler, 'compile', '--output', '@OUTPUT@', '@INPUT@'])
ui_files += ui_file
endforeach

This build magic will populate the ui_files variable with filepaths that can be injected as dependencies during the build process. In the gnome.compile_resources call, I added the argument dependencies: [ui_files]. It now looks like this:

gnome.compile_resources('sample',
'sample.gresource.xml',
gresource_bundle: true,
install: true,
install_dir: pkgdatadir,
dependencies: [ui_files]
)

Finally, I need to add the path to my window.py so Builder can include it in the final build. I updated the sample_sources list to include view/window.py:

sample_sources = [
'__init__.py',
'main.py',
'view/window.py',
]

That’s it for setting up Blueprint within GNOME Builder. I can now build my LibAdwaita app. I hit the play button in the Builder menu and the hamsters started running. Not long after I was greeted with my sample app:

This guide doesn’t really show much Blueprint, so be sure to check out the Blueprint documentation page to see a list of applications built using it. Those apps continue to be excellent examples for me, hopefully they are for you too.

Other notes:

There is a fantastic Workbench tool to help visualize your .blp or .ui files. I highly recommend it for learning the widgets. It’s available on flathub.

Cambalache is also helpful for learning. It doesn’t support exporting to Blueprint and it had some issues refreshing my widgets in real time, but I found it helpful when tinkering. It’s available on flathub.

What’s next?

More likely than not I’ll need to add additional python libraries if I want to do anything cool. I’ve been using Poetry in other personal and work projects, so I’d like to figure out how I can use it to manage my python dependencies within GNOME Builder.

--

--

codenomad

I'm a code nomad. I'm traveling, coding, sharing, and helping whenever I can.