Nikola: zen-Theme mit Fork Awesome
Aktualisiertes zen-Thema für Nikola
Ein Nachteil von Nikola ist meiner Meinung nach die stiefmütterliche Behandlung der Themes. Das von mir genutzte zen-Thema hatte ich bereits auf Font Awesome 4-Icons geupdated.
Inzwischen gibt es nicht nur Version 5 von Font Awesome, sondern auch einen Fork des Projektes namens Fork Awesome mit ein paar neuen Icons. Die Installation und Verwendung ist grundsätzlich die gleiche wie bei Font Awesome, weshalb ich an dieser Stelle nur das gepackte Theme verlinke. Alles weitere wurde bereits im letzten Artikel gesagt.
Download
Kivy: Starter-Paket
Kivy ist ein plattformunabhängiges GUI-Toolkit, mit dem man Python-Programme erstellen kann, die auf Linux, Windows, Mac und mobilen Plattformen (iOS, Android) laufen - die reptiloide eierlegende Wollmilchsau sozusagen.
Für mein nächstes kleines Vorhaben werde ich darauf zurückgreifen, deshalb gibt es hier das kleine Link-Starterpaket:
Text
Videos
Empfehlenswert sind auch die folgenden Videoreihen:
Neu: der Kettlebattle-Generator
Der Kettlebattle-Generator - eine kleine Flask-App für die großen Muskelpartien

Kettle...was?
Genau. Okay, du weißt nicht, was Kettlebattle ist, kein Problem. Die Menschen, die diesem Unsinn nachgehen, dürften sich zahlenmäßig aktuell im einstelligen Bereich bewegen (Stand: Januar 2018).
Kettlebattle ist ein kollaboratives Kettlebell-Home-Workout ab zwei Mitspielern.
Die Frontlinie der "Battle" verläuft dabei nicht zwischen den Mitspielern, sondern zwischen Motivation, Schweinehund, Sofa und Hanteln. Team up!
Spielregeln
- Die Kontaktaufnahme
- Der potentielle Mitspieler wird mit "Kettlebattle?" kontaktiert.
- Der Workout-Vorschlag
- Hat der Mitspieler die Kontaktaufnahme positiv beantwortet, schlägt der Initiator ein Workout vor, das der Mitspieler akzeptieren muss. Fairness geht vor. Wer nach einem langen Tag mitten in der Nacht seinem Mitspieler ein hartes Workout vorschlägt, hat schnell keine Mitspieler mehr.
- Do you even lift?
- Do it.
- Das Finisher-Selfie
- Der krönende Abschluss jeder Kettlebattle ist das Finisher-Foto, das man dem Mitspieler nach dem Workout als Beweis seiner Sportlichkeit zuschickt. Hierbei ist absolute Ernsthaftigkeit zu wahren, es geht hier schließlich um Kettlebattle!
Der Generator
Der Kettlebattle-Generator ist eine Einseiten-Web-App, mit der sich aus einem zuvor definierten Übungspool ein Workout generieren lässt.
Habenwill!
- für die Ausführung wird Flask benötigt
- Download und Entpacken des Quellcodes von GitHub
- kbgen.py ausführen, dies startet den internen Flask-Server
- 127.0.0.1:5000 aufrufen
- Workout generieren
- Werte können per Spinner geändert werden
Konfiguration
In der kbgen.py gibt es 3 Variablen für individuelle Einstellungen:
- `REPS_PRESET`: Tupel mit 2 Werten (int) für die Preset-Buttons, Gesamtwiederholungen
- `KB_EX`: Liste von Übungen, die bei der Kettlebattle zur Auswahl stehen sollen, ein Listeneintrag ist ein Tupel nach dem Muster (Name, Minimum bei Preset 1, Minimum bei Preset 2)
- `MAX_EX`: Maximale Anzahl (int) von Übungen, die aus der Liste durchgeführt werden sollen, Minimumwerte werden immer gezählt
Verweise
- Kettlebell-Image: openclipart.org
- Layout: An Introduction to Python’s Flask Framework
- Kettlebattle ist eine Erfindung von ZeEvilKohl und encarsia (yours truly)
- GitHub-Repository
Pläne für später?
- Export in Textbildchen (leben wir nicht in tollen Zeiten?)
- Favoritenliste/Liste der letzten Kettlebattles
- Lokalisation
Zugegeben, darauf hat die Welt nicht gerade gewartet. Aber nun ist es irgendwie trotzdem da. Willkommen!
Kommentieren auf
Texteditor mit GtkSourceView
Inhalt
Text-Widget mit GtkSourceView
Gtk+ bietet mit Gtk.TextView ein Widget zum Anzeigen und Bearbeiten von Text/-dateien an. Wie beim TreeView-Widget werden die Daten (model) und die Anzeige (view) getrennt voneinander gehandhabt. Das datentragende Modell zu TextView ist TextBuffer.
GtkSourceView ist eine Erweiterung und Unterklasse von TextView, die Syntaxhighlighting, Farbschemata, Laden/Speichern, Vervollständigung und andere Funktionen unterstützt.
Im Beispiel wird ein Editor ergestellt, der bestimmte Dateien laden und speichern kann, sowie eine rudimentäre Suchfunktion und ein Widget zum Farbschemawechseln bereitstellt.

Glade
GtkSourceView
Die SourceView-Widgets befinden sich unterhalb einer eigenen gleichnamigen Hauptkategorie in der Seitenleiste.
- GtkSourceView: das eigentliche Editorwidget, das in einem ScrolledWindow platziert wird
- GtkSourceMap: Miniaturansicht und Unterklasse von SourceView
- GtkSourceStyleSchemeChooserWidget: Widget zur Auswahl eines StyleSchemes
In Glade lassen sich bereits viele Eigenschaften des Editorbereichs festlegen wie die Anzeige der Zeilennummern, Einrückung, Umbruchverhalten usw., die sich natürlich auch über set_property festlegen oder ändern lassen.
Beim StyleChooser-Widget wird das Signal button-release-event belegt, um das ausgewählte StyleScheme auf die SourceView-Widgets anzuwenden.
SourceMap
Das Widget muss mit der anzuzeigenden Quelle, einem SourceView-Widget, verknüpft werden (über "Allgemein > View"). Es wird dann der Inhalt des SourceView-Widgets verkleinert (standardmäßig mit Schriftgröße in 1pt) angezeigt. Durch scrollen in SourceMap verändert man gleichzeitig die Anzeige in SourceView.
Headerbar
Die Headerbar enthält verschiedene Buttons zum Laden, Suchen und Speichern:
- "Python file" und "Glade file" laden die entsprechenden Dateien dieses Beispieles in den Editor (Signal clicked)
- Die Sucheingabe ist ein Gtk.SearchEntry-Widget (Signale search-changed und activate)
- "Save .bak" und "Save" speichern die Dateien (Signal clicked)
Python
SourceView
Initialisierung
Widgets, die nicht zum Gtk-Modul gehören, müssen zunächst als initialisiert werden (siehe auch Vte-Terminal):
GObject.type_register(GtkSource.View)
Das SourceView-Widget besitzt bereits einen integrierten Textbuffer, welcher mit get_buffer abgefragt werden kann:
self.buffer = self.view.get_buffer()
Desweiteren werden noch Objekte zum Laden und Speichern von Dateien sowie fürs Syntaxhighlighting benötigt:
self.sourcefile = GtkSource.File() self.lang_manager = GtkSource.LanguageManager()
Datei laden
Die zu öffnende Datei muss dem GtkSource.File-Objekt im Gio.File-Format und anschließend an GtkSource.FileLoader übergeben werden. Die Information zum Syntaxhighlighting erhält der Buffer:
sourcefile.set_location(Gio.File.new_for_path("file")) buffer.set_language(self.lang_manager.get_language("language")) loader = GtkSource.FileLoader.new(buffer, sourcefile) loader.load_async(0, None, None, None, None, None)
Datei speichern
Analog zum Laden erfolgt das Speichern mit GtkSource.FileSaver. Im Beispiel speichert der "Save"-Button die bestehende Datei (es erfolgt keine "Überschreiben?"-Sicherheitsabfrage) und der "Save .bak"-Button speichert den Inhalt als neue Datei mit genannter Endung ab. Die Übergabe der Dateien erfolgt wie beim Laden Gio.File-formatiert:
#bestehende Datei überschreiben saver = GtkSource.FileSaver.new(buffer, sourcefile) #Datei unter anderem Namen speichern saver = GtkSource.FileSaver.new_with_target(buffer, sourcefile, targetfile) #Speichern ausführen saver.save_async(0, None, None, None, None, None)
Text hervorheben
Zunächst ist festzustellen, dass es sich bei den Funktionen suchen(/ersetzen)/markieren und Texthervorhebungen um zwei getrennt voneinander auszuführenden Mechanismen handelt, für die GtkSource.Settings eingerichtet werden müssen:
settings = GtkSource.SearchSettings() search_context = GtkSource.SearchContext.new(buffer, settings)
Alle Vorkommen eines Strings im TextView lassen sich auf zwei Arten visualisieren, einer naheliegenden und einer eleganten.
Die naheliegende Lösung ist die Ausführung von settings.get_search_text bei der Eingabe von Text in das Suchfeld (Signal search-changed):
Die andere Möglichkeit, bei der kein Signal benötigt wird, ist die direkte Anbindung der SearchSettings-Eigenschaft "search-text" an das Sucheingabefeld:
builder.get_object("search_entry").bind_property('text', settings, 'search-text')
Text markieren
GtkSource.SearchContext wird für die Suchen-/Ersetzen-Funktion innerhalb eines GtkSource.Buffer verwendet. Dieser wurde bereits mit den SearchSettings initialisiert.
Die Markierungsfunktionen und Cursorplatzierung erbt GtkSource.Buffer von Gtk.TextBuffer, die Suche wird mit SeachContexts forward2 ausgeführt.
def find_text(self, start_offset=1): buf = self.buffer insert = buf.get_iter_at_mark(buf.get_insert()) start, end = buf.get_bounds() insert.forward_chars(start_offset) match, start_iter, end_iter, wrapped = self.search_context.forward2(insert) if match: buf.place_cursor(start_iter) buf.move_mark(buf.get_selection_bound(), end_iter) self.view.scroll_to_mark(buf.get_insert(), 0.25, True, 0.5, 0.5) return True else: buf.place_cursor(buf.get_iter_at_mark(buf.get_insert()))
Durch die Signalbindung von activate im Suchfeld wird die Suche durch Drücken der Eingabetaste an der letzten Position fortgeführt. Für eine Rückwärtssuche muss analog zu forward2 oder forward_async backward2 oder backward_async verwendet werden.
StyleChooser
Das Widget zeigt die verfügbaren Stile an. Es ist nicht möglich, lokale Stile anzugeben oder sie zu verändern.
Der angewählte Style lässt sich dann einfach auf den gewünschten Buffer anwenden:
def on_signal_emitted(self, widget, event): buffer.set_style_scheme(widget.get_style_scheme())

Das Geheimnis der Sphinx
Inhalt
Ob ich die Tutorial-Artikel nicht als E-Book zusammenfassen könnte, wurde ich gefragt. Ich kann.
Folgt mir auf meiner abenteuerlichen und actiongeladenen Reise, in dem mit Kanonen auf Spatzen geschossen wird und ich den Geheimnissen der rätselhaften Sphinx auf die Spur komme.

Vorüberlegungen
Die GitHub-Page läuft mit dem statischen Seitengenerator Nikola, die standardmäßig reStructuredText-Quelldateien parst. Da kommt ein Dokumentationstool, das diese ebenso verarbeiten kann und die Ausgabe in verschiedene Formate ermöglicht, absolut gelegen. All das bietet Sphinx.
Die naheliegende, sich aber möglicherweise als naiv herausstellende, Überlegung war nun, die bestehenden Blogartikelquelldateien so vorzubereiten, dass sich mit wenig Aufwand gewünschte Ausgabeformate immer wieder neu generieren lassen.
Sphinx bietet Builder für
- EPUB, dem offenen E-Book-Standard, nativ von allen E-Reader-Fabrikaten außer den Kindles unterstützt
- PDF, das per LaTeX (verschiedene Engines verfügbar) erzeugt wird
Sphinx
Initialisierung
Nach der Installation erstellt man das Projektverzeichnis und initialisiert mit
$ sphinx-quickstart
das Grundgerüst. Fast alle Fragen können auf der Voreinstellung belassen werden. Im Projektverzeichnis befindet sich nun die Konfigurationsdatei conf.py sowie das Root-Dokument index.rst.
Die Dateien lassen sich nach dem Muster make Builder erzeugen, die in den Unterverzeichnissen _build/builder befinden:
$ make epub $ make latexpdf
conf.py
Epub
#HTML-Dateien vor dem Inhalt der index.rst einfügen epub_pre_files = [('info.xhtml', 'Info')] html_additional_pages = {'info': 'info.html'} #Titel erzeugen epub_cover = ('_static/cover.png', 'epub-cover.html') #Stichwortverzeichnis auslassen epub_use_index = False #Bezeichnung der Ausgabedatei epub_basename = output_basename #für die Generierung der info.xhtml benötigt, da sonst None html_last_updated_fmt = '%d. %B %Y'
LaTeX
Für die PDF-Ausgabe müssen eine Reihe von TeXLive-Paketen installiert sein (siehe Dokumentation). Als Alternativen seien an dieser Stelle das in Calibre integrierte Konvertierungstool ebook-convert und epub2pdf genannt.
#Papierformat (Standard ist US-Letter), leere Seiten vermeiden latex_elements = { 'papersize': 'a4paper', 'classoptions': 'oneside,openany' } #Logo auf der Titelseite latex_logo = '_static/logo.png'
Sonstiges
- Pygments
- Syntax-Highlighting, ebenfalls von Nikola unterstützt, hier wie dort bevorzuge ich das Theme "borland".
- Bezeichnung der Ausgabedatei
- Der Dateiname lässt sich für die verschiedenen Builder jeweils festlegen. Um für alle verwendeten Builder jeweils die gleiche Bezeichnung zu nutzen, verwende ich hier die eigene Variable output_basename. Diese wird demzufolge nicht von Sphinx unterstützt und nur innerhalb der conf.py verwendet (in den Variablen htmlhelp_basename, latex_documents, texinfo_documents, epub_basename).
pygments_style = 'borland' output_basename = 'gladepytutorial'
_static
In diesem Ordner befinden sich Stylesheets, Bilder und Skripte, die nach den vorgegebenen Dateien geladen werden. So kann man lokale individuelle Stylesheet-Anpassungen vornehmen, ohne das Theme selbst zu modifizieren. Hier befinden sich eine angepasste pygments.css, cover.png (Epub) und logo.png (PDF).
_templates
Analog zu _static befinden sich hier individuelle Templates. Diese werden standardmäßig mit der Template-Engine Jinja2 betrieben. Hier befindet sich info.html, die in der Epub-Ausgabe Verwendung findet.
Epub: zusätzliche Dateien einfügen
Sphinx bietet mit epub_pre_files (und analog epub_post_files) die Option, zusätzliche und nicht zur eigentlichen Dokumentation gehörenden (X)HTML-Dateien zum Epub hinzuzufügen. Diese müssen allerdings zunächst als zusätzliche HTML-Seiten generiert werden. Dafür wird in der conf.py die Variable html_additional_pages entsprechend gesetzt [1].
[1] | Es hat mich einen (EINEN!) Tag gekostet dies herauszufinden... |
index.rst
Dies ist das Hauptdokument, das von jedem Sphinx-Builder geparst wird. Die Bezeichnung wird in der conf.py in der Variable master_doc festgelegt.
Als reguläre reST-Datei kann sie beliebig viel Inhalt aufnehmen. Es ist allerdings zu empfehlen und im Normalfall vermutlich sowieso bereits der Fall, das Dokument in mehrere Dateien aufzuteilen. Sphinx stellt dafür die eigene toctree-Directive zur Verfügung.
.. toctree:: :maxdepth: 1 :numbered: :caption: Inhalt teildokument1 teildokument2 ...
Dateien außerhalb von toctree werden per include-Directive hinzugefügt.
Es ist auch möglich, Inhalte nur von bestimmten Buildern berücksichtigen zu lassen:
.. only:: latex .. include:: info.rst
Bonus: Mobi
"Ich habe doch einen Kindle und hätte auch gern so ein E-Book!"
Aber klar doch.
KindleGen
Amazon möchte zwar keine Epubs [2] unterstützen, aber sie bieten mit KindleGen ein Tool an, welches diese in die eigenen Formate (KF8, Mobi) überführt.
Auf diese Weise lässt sich mit
$ kindlegen input.epub
eine Mobi-Datei erzeugen.
[2] | oder Google-Apps... |
Problem: Encoding
Das aus dem Epub erstellte E-Book im Mobi-Format hat ein Darstellungsproblem mit einigen (Sonder-)Zeichen.
Abhilfe schafft hier die Zeile
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
an Stelle von
<meta charset="utf-8" />
innerhalb des HTML-Heads. Sphinx bietet dafür die meta-Directive, die allerdings für jede Datei gesetzt werden muss:
.. meta:: :http-equiv=Content-Type: text/html; charset=UTF-8
Nikola
Das Resultat des ersten Durchlaufs von Sphinx mit der Übersichtsseite und drei Artikeln lässt vorsichtig optimistisch werden. Trotzdem gibt es an diversen Stellen Optimierungsbedarf:
-
Die Nikola-eigenen Kurzverweise (slug) funktionieren nicht und erfordern eine Konvertierung in ":ref:"erenz.
-
Die Artikelüberschrift ist kein Gliederungselement und fehlt demzufolge im Inhaltsverzeichnis
-
Nikola-eigene Directives verursachen Fehler. Konvertierung von
- thumbnail -> image
- listings -> literalinclude
-
relative Pfade in image-Directives anpassen
-
Animierte GIFs ignorieren (erzeugen Fehler im LaTeX-Durchlauf, aber nicht im Epub)
-
Inhaltsverzeichnisse in den Artikel überflüssig
-
"Kommentieren auf G+"-Button entfernen
-
Für die Generierung der Mobi-Datei muss jede Datei eine Meta-Anweisung erhalten
Für eine zufriedenstellende Ausgabe ist es also erforderlich, die Ausgangsdateien hinsichtlich dieser Punkte per Skript zu modifizieren.
Automatisierung
Das ist er, der Elefant im Raum.
Sphinx läuft und die index.rst ist eingerichtet. Die Mission besteht nun aus folgenden Teilaufgaben:
- Sphinx soll sich der aktuellen Dateien der GitHub-Page bedienen.
- Diese Dateien sollen gemäß der oben genannten Punkte bearbeitet werden.
- Sphinx soll ein Epub und ein PDF erzeugen.
- KindleGen soll ein Mobi erzeugen.
- Die Dateien sollen im entsprechenden Ordner im GitHub Page-Verzeichnis abgelegt und deployt werden.
Let's do this.
Die diffizile Arbeit ist bereits erledigt: die Einrichtung von Sphinx und die Problemerfassung. Das Skript selbst arbeitet nun die oben genannten Punkte ab. Weiterhin gibt es der Übersichtlichkeit halber zwei weitere Dateien. Es befinden sich nun im Sphinx-Projektverzeichnis folgende neue Dateien:
- nibook.py: sammelt, kopiert, bearbeitet die Quelldateien, erstellt die E-Books und füttert die GitHub-Page (Code)
- index.lst: Liste von Dateinamen (ohne Endung), die im Dokument enthalten sein sollen
übersicht artikel1 artikel2 artikel5
- index.tmpl: aus dieser und der index.lst wird die index.rst generiert
.. generated by nibook, posts will be inserted after ".. include-start" .. some text documentation master file, created by sphinx-quickstart on Thu Oct 26 20:26:54 2017. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. meta:: :http-equiv=Content-Type: text/html; charset=UTF-8 .. only:: latex .. include:: info.rst **************************** Glade-Tutorial mit PyGObject **************************** .. toctree:: :maxdepth: 1 :numbered: :caption: Inhalt :name: mastertoc .. include-start