Sobald man anfängt, den englischen Sprachraum zu verlassen, bekommt man es mit Sonderzeichen zu tun. Im Deutschen sind das insbesondere ä, ö, ü, ß.
Wenn man dann zusätzlich noch unter Windows entwickelt, ist das Chaos perfekt. In den Apps treten dann in der Regel solche Probleme wie hier auf
Um das abzustellen und die Sonderzeichen korrekt darzustellen gibt es ein paar Wege, die ich hier einmal aufzeigen will.
Was passiert da überhaupt
Das Problem ist folgendes. Das .kv file oder auch die .json files, welche man nutzt um Menüs zu bauen werden im Hintergrund über die pyhton Standardfunktion open() eingelesen. Entsprechend der Dokumentation verwendet diese Funktion das Encoding der jeweiligen Plattform, solange man nichts anderes spezifiziert. Welches Encoding das ist, findet man leicht raus, indem man im interaktiven Modus einfach mal die folgenden Befehle eingibt
>>> import locale
>>> locale.getpreferredencoding()
'cp1252'
Bei einer Windowsmaschine ist das, wie oben die cp1252. Jetzt kommt der Knackpunkt. Intern arbeitet Python / Kivy aber mit utf-8.
Nehmen wir jetzt einfach mal das Beispiel von oben aus "für" wird "fA1/4". Woher kommt das.
Sehen wir uns zunächst die Quelle an. Das ist ein .json file, das in der utf-8 Codierung gespeichert ist. Das kann man bspw. mit dem Windows Editor nachsehen
Beim Einlesen der Datei verwenden wir den folgenden Code
def build_settings(self, settings):
settings.add_json_panel('Two Scales Widget', self.config, 'settings_two_scales_widget.json')
Schaut man sich dann einmal die Funktion add_json_panel() im Detail an findet man dass diese selbst eine weiter Funktion create_json_panel() aufruft, in der dann das .json file geöffnet wird.
Dort sieht man, dass die Funktion open() ohne Angabe einer Codierung aufgerufen wird. Damit wird also das utf-8 codierte .json File mit der Windows Codierung cp1252 geöffnet (decodiert) und im weiteren Verlauf wieder mit utf-8 verarbeitet. Man kann jetzt erahnen, dass das zu Problemen führen kann. Detaillierte Übersichten über verschiedene Codierungen und die Unterschiede findet man im Netz zu Hauf. Hier gibt es bspw. eine Übersicht von utf-8 mit cp1252, die unseren Fall ganz gut darstellt.
Die Lösung
Wie immer gibt es natürlich auch hier verschiedene Möglichkeiten, das Problem zu lösen. Man sollte aber aufpassen, was man tut, denn manch eine Modifikation kann auch zu nicht bedachten Problemen führen. Außerdem gibt es kein Patentrezept. Man muss immer das Zielsystem, dessen Codierung und seine Quelldaten kennen.
Dateien im richtigen Encoding abspeichern oder mit dem richtigen Encoding öffnen
Auslöser des Problems war ja, dass ich meine Dateien auf Windows mit utf-8 codiert hatte, die dann anschließend aber mit cp1252 decodiert wurden. Erster Hack ist natürlich, die Dateien einfach mit cp1252 abzuspeichern, so dass es beim Einlesen keine Probleme gibt. Beispiel. Ich erstelle zwei Dateien mit dem Text Hallöchen.
Datei eins ist utf-8 codiert und die andere cp1252 (ANSI). Diese öffne ich jetzt mit dem kleinen Skript hier und plotte die Texte
import locale
print('Print Befehl: Hallöchen')
print('Encoding meines Systems: {}'.format(locale.getpreferredencoding()))
with open('utf-8.txt', 'r') as file:
text = file.read()
print('utf-8 codierte Datei: {}'.format(text))
with open('cp1252.txt', 'r') as file:
text = file.read()
print('cp1252 codierte Datei: {}'.format(text))
Das erzeugt uns dann folgenden Output:
Damit kann ich mir auf meinem Windows Rechner Probleme ersparen. Natürlich muss ich aber aufpassen, wenn ich mit diesem Programm auf einen, bspw. Linux Rechner gehe, der standardmäßig mit utf-8 arbeitet. Der macht mir dann die gleichen Probleme mit meinen cp1252 Dateien.
Daher sollte man bei gewöhnlichen open() Befehlen, einfach das Encoding mitgeben.
with open('utf-8.txt', 'r', encoding = utf-8) as file:
text = file.read()
print('utf-8 codierte Datei mit utf-8 encoding geöffnet: {}'.format(text))
Und schon ist das Problem gelöst.
Das funktioniert aber natürlich nur für Funktionen, die ich selbst schreibe und bei denen ich das Encoding mitgeben kann. Was mache ich jetzt bei Kivy, wenn ich Settings erstelle oder das kv. File eingelesen wird????
Kivy Quellcode manipulieren
Da meine Programme häufig auf einem Windows Rechner entwickelt werden und später auf einem Raspberry Pi laufen, habe ich mich dafür entschieden, folgendes zu tun.
- Meine Dateien werden prinzipiell mit utf-8 codiert.
- Ich habe im Kivy Quellcode meiner Installation die beiden Funktionen create_json_panel() und load_file()
create_json_pane() modifizieren
Diese Funktion wird modifiziert, damit beim Erstellen von Settings nicht schief läuft.
Die Datei findet man in der Kivy Installation im Ordner uix und dort im File Settings.py.
Dort einfach in der Zeile 990 das gewünschte Encoding eintragen und fertig.
Schon sehen die Settings sauber aus:
load_file() modifizieren
Diese Datei wird modifiziert, damit beim Einlesen des kv Files alles funktioniert.
Die Funktion findet man im Ordner lang in der Datei builder.py. Zeile 289.
Damit sind jetzt auch Umlaute in den kv Files kein Problem mehr.
Spezialfall für kv File
Wer keine Settings erzeugt, sondern nur mit einem kv File arbeitet, kann auch ohne Modifikationen des Quellcodes auskommen. Normalerweise lädt Kivy automatisch das kv File basierend auf der Nameskonvention App Name - App = kv File Name. Also MySuperApp.py = mysuper.kv. Diese Datei wird dann mit den Problemen, die oben beschrieben sind geladen. Das muss aber nicht so sein. Man kann auch einen anderen Namen für sein kv File vergeben und das dann manuell laden. Hierbei kann man dann das Encoding angeben.
Beispiel:
App Name: MySuperApp.py
KV File: mysuperkvfile.kv
Und so lädt man dann diese Datei mit dem gewünschten Encoding mit einem kleinen Umweg. Man verwendet nicht die Funktion Builder.load_file(), sondern man lädt sich zunächst einmal selber mit der open() Funktion den Inhalt des kv files und übergibt diesen dann der Funktion Builder.load_string(). Dabei kann man dann in der open() Funktion ganz einfach das gewünscht Encoding festlegen.
Damit sind für mich die Probleme erstmal behoben. Natürlich muss man bei Neuinstallationen etc. aufpassen, dass man die Modifikationen wieder macht, aber für mich ist der Weg gangbar. Es sind bei Kivy auch ein paar Requests anhängig, dass man diese Funktionen modifiziert um das Thema abzustellen, aber ich sehe da wenig Bewegung.