Packing GTK+ applications with setuptools

The only reason this page exists is to remove the links in my file.


from setuptools import setup

REQUIRED = ["PyGObject", ...]


Pass the PyPI package name, for GTK+ applications you will need at least the GObject Python bindings to successfully run the import gi command.

Non-code files

from setuptools import setup

PACKAGES = ["my_package"]
PACKAGE_DIR = {"my_package": "my_package_dir"}
PACKAGE_DATA = {"my_package": ["ui/*", "logging.yaml"]}


The package "my_package" will be built from the files in "my_package_dir" folder into the "my_package" folder.

Non-code files like Glade files are passed as a list to the package_data option. In the example all files of the subfolder "ui" and the file "logging.yaml" are integrated into the package.

Desktop files

The locations of .desktop files and icons are defined by the Freedesktop specifications. Desktop entries for example are located in


While running install the suitable prefix will be identified so only relative paths are required to be passed to the data_files option.

from setuptools import setup

rel_app_path = "share/applications"
rel_icon_path = "share/icons/hicolor/scalable/apps"

            (destination_dir, ["list", "of", "files"]),
            (rel_app_path, ["my_app.desktop"]),
            (rel_icon_path, ["my_appicon.svg"]),


This step only copies the files into the specific directories. The correct path declaration WITHIN the .desktop file has to be customized during the install command which will be accomplished by a custom function.

Customizing existing commands

The .desktop file includes information about the program to be executed as well as a corresponding icon, keywords etc. Because the target installation location may vary the file has to be adapted during the installation process.

To run own methods in existing commands you will have to create an instance of the specific command class (install, build, bdist etc.) and customize the "run" method.

In setuptools this information is passed to the cmd_class option.

from setuptools import setup
from setuptools.command.install import install

class CustomInstall(install):

    def run(self):
        self.my_function(args, go, here)

    def my_function(self, *args):

    cmdclass={"install": CustomInstall}

A list of available commands can be obtained by

$ python --help-commands

Creating new commands

Setuptools enables you to simply create your own commands. It may be useful to create an 'uninstall' command to get rid of all the files dumped to the system during installation to avoid to do that manually.

from setuptools import setup, Command

class UnInstall(Command):

    description = "description shown by --help-commands"
    user_options = [("myoption",
                     "myoption description shown by cmd --help")]

    def initialize_options(self):
        # method must exist
        # define all options with default value
        self.myoption = None

    def finalize_options(self):
        # method must exist

    def run(self):
        # method must exist
        # code to be executed goes here
        print("This is a custom command.")

    cmdclass={"uninstall": UnInstall}

NoN: jetzt noch toller

Knights of Ni - sag niemals nie

Nach den kürzlichen Änderungen an der Oberfläche gibt es nun erfreuliche Fortschritte darunter zu vermelden.


  1. Man kann NoN jetzt richtig (mit Hilfe von setuptools) installieren und deinstallieren.
  2. Vom Programm generierte Dateien werden nun wie üblich im eigenen Verzeichnis im Homeverzeichnis gespeichert.
  3. Der Programmablauf wurde beschleunigt und die Dateizugriffe massiv reduziert, indem die Posts-/Pages-Informationen für den wiederholten Abruf geparst und in einer JSON-Datei abgelegt werden.

Installation via setuptools

Bei der Installation via setuptools werden sowohl das Programm inklusive der UI als auch ein Desktopeintrag installiert. Nach der Installation lässt sich das Programm in GNOME also bequem über [super] + [n] (... [o] ... [n]) + [enter] starten.

Um dies alles auch wieder loszuwerden, gibt es ein zusätzliches uninstall-Kommando. Beide Kommandos sind mit der Option --user möglich:

# (De-/)Installation im lokalen Nutzerverzeichnis
$ python install --user
$ python uninstall --user

# systemweite (De-/)Installation
$ sudo python install
$ sudo python uninstall


Die Nutzerdaten liegen jetzt in ~/.non. Dort befinden sich

  1. die Konfigurationsdatei config.yaml, in der der Pfad zur aktuellen/letzten Nikola-Seite und die Bookmarks gespeichert sind,
  2. die Logdatei non.log sowie
  3. die geparsten Daten der aufgerufenen Nikola-Seiten jeweils als JSON-Datei.

Speicherung von (Meta-)Daten

Für die Anzeige im TreeStore wurden die Daten bisher sowieso intern als Dictionary gespeichert. Das Ineffiziente daran war allerdings, dass die Daten bei jedem Refresh immer neu abgerufen wurden. Dies ist nicht notwendig und mit zunehmender Größe der Seite auch sehr zäh.

Die Lösung bestand also darin, die Daten des Dictionary zum einen für den späteren Gebrauch in einer Datei zu speichern und zum anderen es nur durch eine Update-Funktion auf aktualisierte Inhalte zu überprüfen.

Auf diese Weise werden beim ersten Aufruf einer Nikola-Konfiguration alle Posts/Pages, Tags und Kategorien oder bei erneutem Aufruf die Daten aus der entsprechenden JSON-Datei eingelesen. Die Aktualisierung erfolgt über den Abgleich der letzten Dateimodifikation. Die JSON-Datei wird beim Wechsel in eine andere Nikola-Seite (z.B. über ein Bookmark) oder bei Programmende gespeichert.

Lost in the rabbit hole of Google Takeout

Backup your data. They said. All of it. They said. And then came Google Takeout


G+ post HTML file from Takeout

I get the 4, 20 and Google+

Takeout is Google's user data archive system for numerous products. My primary target me was to download an archive of my Google+ activities.

The approach is quite simple: choose the product in Takeout and wait until the archive(s) has/have been generated. The downloadable archive will be valid for a week but you can generate new archives at any time.

Lesson 1:
Choose zip as filetype if you use umlauts, there could be encoding issues in tgz files.

I remembered that there was an import plugin for Nikola and I imagined to throw in the archive and to get a usable local site in return. At this point of the article the reader may speculate that this didn't work in the slightest way.

Unpacking presents

The first inspection reveals:

  1. All G+ posts are located in Google+ stream/Posts as HTML files. These files appear usable.
  2. Image links just point to filenames. The path is missing so only images in the same directory are shown but
  3. Images are scattered among different directories (in Posts and Photos and their subfolders). The majority of image files are stored in Photos of posts in date corresponding subfolders.
  4. There are different date formats in peaceful co-existence:
Photos of posts/
 ├── 02.06.14
 ├── 02.06.16
 ├── 22. Juli 2013
 ├── 23.01.17
 ├── 2011-08-14
 └── 2012-03-13
  1. There is a corresponding JSON file for every image but not for HTML files.
  2. Strucure of HTML files:

Dumdidumdumdum...Inspektor Gadget

Lesson 2
You can open only single posts, there are a lot of deadlinks in image posts, but share and reaction information are displayed (public/private/collection/community post, +1, reshares and comments).

Your entry: Nikola

With low expectations I install the import plugin for Nikola and see what happens. Nothing. The posts once were provided as JSON files but not in recent days.

I brachiate through the files, importing HTML files first. The import plugin instantiates a new Nikola site, so I can just trial and error like hell. Then I care about deadlinks, then titles, it kepps getting better with every build.

The result is a static website of my Google+ stream including +1's and comments and a link to the original post.


In general the import is independent from any theme. I personally recommend hyde which even can be improved by the custom.css that is included in the archive.


  • local search function
  • filter posts by share status


In case you consider a publicly accessible stream backup you have to keep in mind that the imported data also includes all privately shared posts.


As a long-term heavy Google+ user you are used to inconsistencies and improvementent constantly getting worse so a Takeout archive is no more than a sparring partner to train with. It is only a matter of time until my version of the import plugin will go the way of all those Google messengers before.


static Google+ Nikola site (hyde theme)

If you want to try yourself:

Read more…

NoN: Fortschritte und Release

Knights of Ni - little release on the prairie

The GTK+ desktop client for static website generator Nikola has made some progress since mentioned last time here.

Nikola v8 will soon be released in June. I saved the current development status as a release because I cannot estimate how much effort I have to put into to make it ready for v8.


The submenu moved to the right side; on the left there is the GUI/teminal StackSwitcher; new: button that opens the Nikola handbook.
More deployment options
  • GitLab: use the "Deploy to GitHub" button, for help on configuration see this example Nikola site using GitLab.
  • Other: if there is DEPLOY_COMMANDS variable set in your the "Deploy" button will execute the 'default' preset.
NoN now runs as a GtkApplication.
Desktop entry
Open post/page in browser
Right click on an article to open it in the default webbrowser.
Bits and pieces
Bugfixes, improved logging, Python code is now conform to PEP8 (says pycodestyle).

Überarbeitete Oberfläche in Glade 3.22

Im Zuge der Veröffentlichung von GNOME 3.28 wurde auch Glade eine ordentliche Portion Zuwendung zuteil.


Das in GNOME 3.10 (Veröffentlichung 2013) eingeführte Headerbar-Widget ließ sich selbst bereits in Glade verwenden, die Anwendung selbst nutzte sie bisher nicht. Sie vereint Titelleiste, Menü, Toolbar und die Projekt-Tableiste. Durch diese Komprimierung wird viel leerer Raum eingespart (siehe Screenshots).


Die Dreiteilung der Oberfläche ist erhalten geblieben, es gibt aber zwei wesentliche Änderungen:

  1. Die Widgetauswahl erfolgt nicht mehr aus der linken Spalte, sondern sie ist oben auf der mittleren Arbeitsfläche als Buttonleiste mit Dropdown-Menü erreichbar.
  2. In der nun freien Spalte werden die Widgetstruktur des aktuellen Projekts angezeigt. Diese war zuvor in der oberen Hälfte der rechten Spalte zu finden.

Die Verlagerung der Widgetauswahl ermöglicht eine komplette Nutzung des vertikalen Platzes für die Anzeige der Struktur auf der linken sowie deren Eigenschaften auf der rechten Seite.


Beim ersten Aufrug der neuen Version startet ein Assistent, der etwas aufdringlich die Neuerungen anpreist. Dieser kann jederzeit über die Einstellungen in der Headerbar unter "Einführung in interaktive Benutzeroberflächen" erneut aufgerufen werden.





Glade 3.22


Glade 3.20