Daten|teiler
Kopieren als Kulturtechnik

Mobiles Python III – ein simpler Vokabeltrainer

25. Februar 2010 von Christian Imhorst

Mit Python auf einem Nokia-Handy mit S60-Plattform herumzuspielen, macht einfach Spaß. Außerdem kann man sich damit gut die Zeit vertreiben, wenn es an der Supermarktkasse wieder etwas länger dauert, oder man untätig in der Bahn sitzt. Vor gut 3 Jahren habe ich im ersten Teil beschrieben, wie man Python auf dem Handy installiert. Geändert hat sich an der Installation seit damals nicht viel (s.u. „Exkurs: Installation von PyS60“), außer dass Python for S60 mittlerweile in der Version 1.4.5 vorliegt, aber immer noch Python 2.2.2 verwendet wird, und ich mittlerweile ein Nokia E71 habe. Im zweiten Teil habe ich beschrieben, wie man die Bluetooth-Konsole unter Gnu/Linux einrichtet. Der Vorteil von der Bluetooth-Konsole ist, dass man in der Python-Shell seines Gnu/Linux-Rechners arbeitet und die Anweisungen per Funk an das Telefon weitergereicht und dort ausgeführt werden. Doch was nützt die schönste Spielerei, wenn man nicht ein kleines Projekt hat?

Die Voraussetzung

Zur Zeit probiere ich etwas mit der relationalen Datenbank herum, die über das Modul anydbm eingebunden werden. Dabei ist anydbm nur eine Fassade, eine Schnittstelle zu verschiedenen Varianten von DBM-artigen Datenbanksystemen. Sollte dabei kein Datenbank-Modul verfügbar sein, wird das etwas langsame Modul dumbdbm benutzt. Bei PyS60 verbirgt sich hinter der Fassade das Modul e32dbm, welches man auch direkt laden könnte, allerdings auf Kosten der Plattformunabhängigkeit.

Ein Datenbankobjekt erzeugt man mit der Funktion open(), das man wie ein Dictionary verwenden kann, allerdings gibt es Einschränkungen:

  • Schlüssel und zugeordneter Wert dürfen nur vom Typ String sein,
  • außerdem sind nur wenige Dictionary-Methoden wie keys(), values(), iteritems() verfügbar.

Ein kleines Beispiel in der Python-Shell, um das Ganze zu veranschaulichen:

>>> import anydbm
>>> db=anydbm.open(r'e:\test.db', 'c')
>>> db['Mond'] = 'moon'
>>> db['Sonne'] = 'sun'
>>> db['Erde'] = 'earth'
>>> db.close()

Nachdem das Modul anydbm importiert ist, erzeugt man das Datenbankobjekt db und legt eine Datenbank mit dem Namen test.db auf der Speicherkarte im Verzeichnis e:\ an. Das c bedeutet übrigens create; man erstellt eine neue Datenbank, anstatt eine bereits vorhandene zu öffnen. Möchte man eine existierende Datenbank zum Lesen und Schreiben öffnen, schreibt man ein w für write und nur zum Lesen ein r für read. Anschließend werden die Schlüsselpaare eingetragen, dabei muss der Schlüssel als String in eckigen Klammern stehen, der Wert kann einfach als String eingetragen werden. Dass es sich um Strings, also Zeichenketten handelt, zeigen die einfachen Anführungszeichen, welche die Inhalte umschließen. Zum Schluss wird der neue Inhalt gespeichert, indem die Datenbank mit der close()-Methode geschlossen wird.

Mit einer for-Schleife kann man nach schauen, ob die Einträge auch alle vorhanden sind. Vorher muss man die Datenbank wieder öffnen, dieses mal nur, um sie zu lesen:

>>> db=anydbm.open(r'e:\test.db', 'r')
>>> for key, value in db.iteritems():
...    print key, value
...
Erde earth
Mond moon
Sonne sun
>>> db.close()

Um Schlüsselpaare der Datenbank hinzuzufügen, muss man sie im Lese- und Schreibmodus öffnen:

>>> db=anydbm.open(r'e:\test.db', 'w')
>>> db['Sterne'] = 'stars'
>>> for key, value in db.iteritems():
...    print key, value
...
Erde earth
Mond moon
Sonne sun
Sterne stars
>>> db.close()

Solange die Datenmenge noch überschaubar ist, wie in diesem Beispiel, kann man die Datenbank wie ein schönes praktisches Dictionary verwenden. Man sollte aber bedenken, dass es kein Dictionary ist. Schlüssel- und Wertepaare werden von anydbm nur im Bedarfsfall in den Hauptspeicher geladen. Sollte die Datenmenge auf so viele Einträge wachsen, dass sie vielleicht gar nicht mehr komplett in den Hauptspeicher des Telefons passt, sollte man folgende „Todsünden“, die Farid Hajji im Python-Praxisbuch zusammenfasst, beim Arbeiten mit anydbm auf jeden Fall vermeiden: Statt der Dictionary-Methoden keys() und items(), die eine riesige Liste von Schlüsseln aus der Datenbank laden, die eventuell die Grenzen des Hauptspeichers sprengen würde, sollte man besser iterkeys() und iteritems() in einer Schleife aufrufen, damit Schlüssel- und Wertepaare auch weiter nur im Bedarfsfall in den Hauptspeicher geladen werden.

Das zeigt gleichzeitig den Nachteil von anydbm: Einträge können z.B. nur im Nachhinein sortiert werden, wenn die gesamte Schlüsselliste in den Hauptspeicher geladen wurde:

>>> db=anydbm.open(r'e:\test.db', 'r')
>>> liste = db.keys()
>>> liste.sort()
>>> liste
['Erde', 'Mond', 'Sonne', 'Sterne']
>>> db.close()

Die Spielerei mit anydbm hat mich auf die Idee gebracht, einen kleinen Vokabeltrainer für Python for S60 zu schreiben. Sicher, es gibt Programme wie pyAsk2, allerdings wurde die letzte Aktualisierung am Code im April 2008 vorgenommen. Außerdem legt pyAsk2 die Vokabeln in einer Textdatei ab, was Probleme mit Unicode-Zeichen nach sich zieht. Es klappt einfach nicht, Sonderzeichen wie Umlaute, spanische Buchstaben wie ñ oder Buchstaben mit Akzent in die Textdatei zu schreiben, ohne dass dabei das ganze Programm abstürzt. Da gefällt mir eine Datenbanklösung mit anydbm bzw. e32dbm besser: So wie man die Wörter und Buchstaben hinein wirft, bekommt man sie auch wieder heraus:

>>> db=anydbm.open(r'e:\test.db', 'w')
>>> db['Bär'] = 'bear'
>>> db['at sign @'] = 'At-Zeichen @'
>>> db['British pound £'] = 'Britisches Pfund £'
>>> for key, value in db.iteritems():
...    print key, value
...
British pound £ Britisches Pfund £
Erde earth
Mond moon
at sign @ At-Zeichen @
Bär bear
Sonne sun
Sterne stars
>>> db.close()

Mit der Anweisung del für Dictionaries kann man übrigens Schlüsselpaare leicht wieder entfernen:

>>> db=anydbm.open(r'e:\test.db', 'w')
>>> del db['Bär']
>>> del db['at sign @']
>>> del db['British pound £']
>>> db.close()

Ein weiterer Vorteil ist, dass man mit Hilfe der split()-Methode für Strings einen Datensatz in weitere Felder zerlegen kann, um z.B. weitere Informationen anzuhängen:

>>> db=anydbm.open(r'e:\test.db', 'w')
>>> db['Erde'] = 'earth|the third planet in order of distance from the sun'
>>> db['Sterne'] = 'stars|the fixed bodies in the sky, which are really distant suns'
>>> db['Sonne'] = 'sun|the round body in the sky that gives light and heat to the earth'
>>> db['Mond'] = 'moon|Spacemen landed on the moon'
>>> for key, value in db.iteritems():
...    value, definition=db[key].split('|')
...    print 'De: ', key, "\n", 'En: ', value, "\n", '    ', definition, "\n"
... 
De:  Erde 
En:  earth 
     the third planet in order of distance from the sun 
...

Das kann ja nicht nur eine Definition sein, wie in dem Beispiel, sonder auch ein Punktesystem, um einen Karteikasten abzubilden: Je höher die Punktezahl, desto weiter rutscht die Karte im Kasten nach hinten und wird weniger abgefragt.

Zarte Anfänge

Es folgt nun ein erster Entwurf des simplen Vokabeltrainers smpltrainr.py, wobei ich aber dazu sagen muss, dass ich noch nicht weit gekommen bin und auch noch nicht alle Ideen eingeflossen sind, die ich weiter oben besprochen habe:

import anydbm, os
 
path = 'e:\\dicts\\'
 
def dir_dict():
    """ create dir of dict if it is not there """
    try:
        os.listdir(path)
    except:
        os.mkdir(path)  
 
def open_dict():
    """ open dict or create one """
    global dict
    name = raw_input('Open dictionary: ')
    try:
        dict=anydbm.open(path + name, 'w')
    except:
        dict=anydbm.open(path +name, 'c')
 
def write_dict():
    """ write into dict """
    x = ' '
    while x != 'Q':
        word = raw_input('Word: ')
        meaning = raw_input('Meaning: ')
        dict[word] = meaning
        x = raw_input('Press Q for quit? ')
 
def ask_dict():
    """ ask from dict """
    for word, meaning in dict.iteritems():
        ask = raw_input(word + ' means ')
        if ask == meaning:
            print 'Correct'
        else:
            print 'Wrong. ' + word + ' means ' + meaning 
 
dir_dict() 
open_dict()
 
x = raw_input('Do you want to (A)sk from or to (W)rite into the dictionary? ')
if x == 'W':
    write_dict()
if x == 'A':
    ask_dict()
 
dict.close()

Wie man sieht, findet die Interaktion zwischen User und Programm noch in der Python-Shell statt. Das wird natürlich noch durch das UI Framework appuifw ersetzt, damit für die Interaktion native GUI-Elemente wie in Symbian verwendet werden. Außerdem ist der Funktionsumfang des Programms auch noch sehr klein. Da werde ich aber noch weiter dran arbeiten.

Exkurs: Installation von PyS60

Für die Installation auf einem Gerät mit der Plattform S60 3rd Edition, wie dem E71, benötigt man die Datei PythonForS60_1_4_5_3rdEd.sis, für die 2ed Edition entsprechend PythonForS60_1_4_5_2ndEd.SIS vom PyS60-Projekt. Dazu ist die Datei PythonScriptShell_1_4_5_3rdEd.SIS bzw. PythonScriptShell_1_4_5_2ndEd.SIS noch ganz hilfreich, damit man zum Start einen Python-Shell mit Beispiel-Skripten zur Verfügung hat. Die Endung .sis zeigt bei Symbian übrigens immer an, dass die Datei noch auf dem Gerät installiert werden muss. Bei der Installation ist es wichtig, dass Python in den Telefonspeicher installiert wird, und nicht auf die Speicherkarte. Diese Shell benutze ich allerdings schon gar nicht mehr, sondern die in Ped. Ped ist eine sehr komfortable integrierte Entwicklungsumgebung (IDE) für Python auf Nokias S60-Plattform, mit der man Python-Code direkt im Telefon schreiben und, dank einer integrierten Python-Shell, auch ausführen kann.

Geschrieben in Programmieren, Python