How to embed a theme directly in the app (PyGTK3)

Given an app I’m writing, I want to set the theme to the theme downloaded, like embedding it, without installing it in the system.

I’m currently using PyGTK3, and have plans to upgrade to PyGTK 4 someday.

Suppose I download a theme tar (or zip), like one of the variants here material-black COLORS Complete Desktop - pling.com. I created a assets/gtk/themes folder and extracted the theme tar there.

$ ls assets/gtk/themes/Material-Black-Plum-BE
chrome  cinnamon  COPYING  gnome-shell  gtk-2.0  gtk-3.0  gtk-4.0  index.theme  INSTALL_GDM_THEME.md  metacity-1  plank  unity  xfwm4

I want to be able to set the global theme to one of the embedded themes at startup.

Suppose the GTK_THEME environment variable is unset. If setting the variable overrides the one I set in the code, then it is ok, even desirable.

I tried using gtk_rc_set_default_files() from this post, in Python, but It can’t find the theme.

Gtk.rc_add_default_file("/data/code/project/assets/gtk/")

settings = Gtk.Settings.get_default()
settings.set_property("gtk-theme-name", "Material-Black-Plum-BE")

I tried many variations of the path and I didn’t work (nor selecting by code like in this example, nor showing in Gtk Inspector".

Also, this method is deprecated, so even if it worked, I would have to change it later. But the principle of adding an extra folder to look for themes is exactly what I need.

So, how do you embed themes directly into the app?
Examples can be in C, as long it’s translatable into Python.

Hi,

RC files were used in gtk+2. These APIs were kept in early gtk+3 versions until the transition to CSS styling was completed, and should be completely irrelevant as of now.

Do you mean the whole desktop theme for all applications, of just your app?

I mean for just my app.

The idea is to have a consistent themable look across platforms without asking to the user to install a theme.

Then it should be possible, yes.
Either with a gresource file to bundle the theme, or it maybe possible to directly load a CSS file.
Possibly with a few extra tweaks, like adapting the paths of some assets if needed.

Under assets/gtk/themes/Material-Black-Plum-BE/gtk-3.0/ there should be a gtk.css file, I suppose. If yes, can you try to load it with something like:

        cssp = Gtk.CssProvider()
        cssp.load_from_path("/path/to/the/gtk.css")
        d = Gdk.Display.get_default()
        Gtk.StyleContext.add_provider_for_display(d, cssp, Gtk.STYLE_PROVIDER_PRIORITY_THEME)
1 Like

Yes, there is, and I tried this already. It seems broken, like, it gets some parts of the theme, but other things seem broken compared with installing the theme and using it. Like icons. So I concluded that the theming is more than loading the gtk.css, and I gave up this way before trying to recreate part of GTK theming machinery. I could be wrong about this approach, but my tries didn’t go well.

This approach I didn’t try yet, and it seems promising. Can you give me any points on how it should work, like a link to a docs page that handles the subject?

Yeah, I suppose it tries to apply your theme on top of the default one, resulting in some mix-up. Not sure how to make it start from an empty state… And all resources like icons may not be resolved properly, so it may require some manual tweaks of the CSS.

That actually won’t be very different from loading the CSS directly. You can look at how libadwaita embeds its own theme and assets: src/stylesheet/adwaita-stylesheet.gresources.xml · main · GNOME / libadwaita · GitLab.
More about resources here: Gio.Resource.

Ah, good news: when using resources, gtk3 seems to automatically load
/org/gtk/libgtk/theme/<theme_name>/gtk.css
with <theme_name> the gtk-theme-name setting.

(see code here)

I suggest to try packaging the theme (i.e. the content under your “gtk-3.0” folder) as resource (again look at how libadwaita does it).