Mit Python und GTK+ ist eine GUI-Anwendung für Linux rasch geschrieben. Mit ein wenig Installationsarbeit läuft der gleiche Code auch unter Windows und Mac OS X.
Das Framework GTK+ taugt nicht nur für grafische Benutzeroberflächen unter Linux, sondern läuft auch unter Windows und Mac OS X. Python eignet sich als Programmiersprache, um mit GTK+ schnell Ergebnisse zu erzielen. Das zeigt sich unter anderem darin, dass diverse Komponenten von Gnome und Ubuntu mittlerweile mit Hilfe dieser Kombination realisiert sind.
Eines für drei
Dieser Artikel zeigt, wie man eine kleine Python-GTK+-Anwendung programmiert, die sich für mehrere Plattformen eignet: Linux, (eine Betaversion von Ubuntu 11.10 Oneiric Ocelot), Mac OS X 10.7.1 Lion und Windows 7, bei allen Systemen auf der 64-Bit-Intel-Architektur. Dabei kommt GTK+ [1] in Version 2.24 zum Einsatz, Python in Version 2.7. Für neuere GTK+-Ausgaben gibt es im Zusammenspiel mit Python noch keine produktionsreife Unterstützung auf diesen drei Systemen, insbesondere nicht mit Python 3. Einen Ausblick in Sachen Versionen gibt der Kasten “Die Zukunft: GTK+ 3, Python 3, Py Gobject”.
Die Zukunft: GTK+ 3, Python 3, Py Gobject
Unter Linux ist es heute bereits problemlos möglich, den Temperatur-Umrechner unter Python 3 und mit der GTK+-Version 3 zu realisieren. Dabei kommt zweckmäßigerweise auch das neue Python-Binding Py Gobject 2.90 [10] zum Einsatz. Es hat den Vorteil, dass es nicht mehr statisch implementiert ist, sondern über Gobject-Introspektion dynamisch funktioniert: Py Gobject 2.90 ist sozusagen nur noch ein kleiner Treiber, der Python beibringt, um auf die Gobject-Introspektionsdatenbank zuzugreifen, in der die verfügbaren Klassen mit ihren Fähigkeiten verzeichnet sind. Der Wartungsaufwand zusätzlicher Sprachbindungen für Glib, Gobject, GTK+ und die sonstigen Bibliotheken der G-Welt soll so drastisch sinken.
Für den Temperatur-Umrechner relevant und neu in GTK+ 3 sind ansonsten vor allem der neue Container »GtkGrid« , der »GtkTable« ablöst und einige Layouts einfacher umsetzbar macht, sowie das Anwendungsobjekt »GtkApplication« , das die laufende Anwendung sauber als Objekt abbildet. Damit wird es überflüssig, die Hauptschleife von Hand zu starten.
Leider ist die Installation von GTK+ 3 – ganz gleich auf welcher Python-Version – derzeit nur auf Linux-Systemen mit vertretbarem Aufwand möglich. Unter Mac OS X und Windows 7 fehlt es noch an fertigen Paketen, sodass der Anwender außerordentlich viel von Hand kompilieren muss. Unter Windows kommt als zusätzliche Hürde – insbesondere bei der 64-Bit-Version – hinzu, dass dafür erst einmal eine GNU-Toolchain wie Cygwin oder Min GW erforderlich ist.
Für Interessierte ist unter [4] der Temperaturumrechner als GTK+-3-Version verfügbar, lauffähig mit Python 2 oder Python 3.
Als Beispielprogramm dient ein Temperatur-Umrechner – nichts anderes als ein Dialogfenster, das fünf Zahlenfelder enthält, die eine Temperatur in unterschiedlichen Einheiten wie Fahrenheit und Celsius anzeigen. Ändert der Nutzer einen Wert in einem der Felder, ändern sich auch die anderen entsprechend.
Die Entwicklung einer GTK+-Anwendung beginnt damit, eine oder mehrere Benutzeroberflächen-Dateien im GTK-Builder-Format mit dem GUI-Tool Glade [2] zu erstellen. Für diesen Artikel erledigt das Glade 3.8, die letzte Version mit Unterstützung für GTK+ 2.x, der Einfachheit halber unter Linux.
Da Oneiric Ocelot von Haus aus nur noch 3.10 – die Glade-Version für GTK+ 3.0 und aufwärts – mitbringt, muss der Ubuntu-Benutzer Glade aus dem Quellpaket [3] kompilieren. Dazu sind die Pakete »intltool« , »libgtk2.0-dev« und »libxml2-dev« erforderlich, die sich per Paketverwaltung installieren lassen. Den ausgepackten Quelltext-Tarball von Glade übersetzt und installiert klassisch »./configure && make && sudo make install« . Unter anderen Linux-Distributionen ist es oft möglich, Glade 3.8 über den Paketmanager zu installieren, eventuell sogar parallel zu Glade 3.10.
Oberflächen-Bausatz
Nach dem Starten von Glade durch das Kommando »glade-3« oder per Startmenü erscheint ein Fenster mit einer Arbeitsfläche (in der Mitte), einer Palette mit Widgets (links), einer Baumdarstellung der Widget-Hierarchie (rechts oben) und einer umfangreichen Liste zum Bearbeiten der Eigenschaften des jeweiligen Widgets (Abbildung 1). Ein Klick auf das Symbol für Dialogfenster in der Widget-Palette fügt das Fenster als oberste Ebene der Hierarchie ein. Darin befinden sich Platzhalter, in die der Anwender weitere Widgets einfügt.
Am Ende der recht grafischen Arbeit steht ein Dialog mit einem Schließen-Knopf, einer Beschriftung und dem Tabellenbehälter »GtkTable« , der fünf weitere Beschriftungen und daneben jeweils ein Zahlenfeld enthält. Die Zahlenfelder sind jeweils mit je einem »GtkAdjustment« assoziiert, das nach dem MVC-Paradigma als Model fungiert – dies muss explizit von Hand passieren. Die Zahlenfelder sowie der Schließen-Knopf haben eindeutige Kennungen erhalten.
Die fertige Oberflächenbeschreibung lässt sich als Datei »temperatur.gtkbuilder« abspeichern. Listing 1 zeigt einen Ausschnitt daraus, der ahnen lässt, wie die Widget-Hierarchie und die verschiedenen Eigenschaftswerte für das Layout in XML festgehalten sind. Diese und andere Dateien zu diesem Artikel stehen ungekürzt unter [4] zum Download bereit.
Listing 1
temperatur.gtkbuilder (gekürzt)
01 <?xml version="1.0" encoding="UTF-8"?> 02 <interface> 03 <requires lib="gtk+" version="2.24"/> 04 <!-- interface-naming-policy project-wide --> 05 [...] 06 <object class="GtkDialog" id="fenster"> 07 <property name="can_focus">False</property> 08 <property name="border_width">12</property> 09 <property name="title" translatable="yes">Temperaturumrechner </property> 10 <property name="default_width">400</property> 11 <property name="type_hint">dialog</property> 12 <child internal-child="vbox"> 13 <object class="GtkVBox" id="dialog-vbox1"> 14 <property name="visible">True</property> 15 <property name="can_focus">False</property> 16 <property name="spacing">2</property> 17 <child internal-child="action_area"> 18 <object class="GtkHButtonBox" id="dialog-action_area1"> 19 [...] 20 <child> 21 <object class="GtkButton" id="schliessknopf"> 22 [...] 23 </object> 24 <packing> 25 <property name="expand">False</property> 26 <property name="fill">False</property> 27 <property name="position">0</property> 28 </packing> 29 </child> 30 </object> 31 </child> 32 [...] 33 </object> 34 </child> 35 </object> 36 </interface>
Der Code
Das Programm selbst findet sich in Listing 2. Es enthält knapp über 60 Zeilen Python und ist recht simpel gestrickt. Die gesamte Anwendung besteht aus der Definition der Klasse »TemperaturApp« (Zeile 11), die neben dem Konstruktor »__init__()« die Methoden »main()« , »delete_event()« , »destroy()« und »geaendert()« umfasst.
Listing 2
temperatur.py
01 #!/usr/bin/python
02 import pygtk
03 pygtk.require('2.0')
04 import gtk
05
06 felder = "kelvin-feld", "celsius-feld", "fahrenheit-feld", "reaumur-feld", "rankine-feld"
07 wert = {}
08 widget = {}
09 handler = {}
10
11 class TemperaturApp:
12
13 def delete_event(self, widget, event, data=None):
14 return False
15
16 def destroy(self, widget, data=None):
17 gtk.main_quit()
18
19 def geaendert(self, objekt, was):
20 wert[was] = widget[was].get_value()
21 if was == "celsius-feld":
22 wert["kelvin-feld"] = wert["celsius-feld"] + 273.15
23 elif was == "fahrenheit-feld":
24 wert["kelvin-feld"] = (5.0/9.0)*(wert["fahrenheit-feld"]-32.0) + 273.15
25 elif was == "reaumur-feld":
26 wert["kelvin-feld"] = (5.0/4.0)*wert["reaumur-feld"] + 273.15
27 elif was == "rankine-feld":
28 wert["kelvin-feld"] = (5.0/9.0)*wert["rankine-feld"]
29
30 if was != "celsius-feld":
31 wert["celsius-feld"] = wert["kelvin-feld"] - 273.15
32 if was != "fahrenheit-feld":
33 wert["fahrenheit-feld"] = (9.0/5.0)*(wert["kelvin-feld"]-273.15) + 32.0
34 if was != "reaumur-feld":
35 wert["reaumur-feld"] = (4.0/5.0)*(wert["kelvin-feld"]-273.15)
36 if was != "rankine-feld":
37 wert["rankine-feld"] = (9.0/5.0)*wert["kelvin-feld"]
38
39 for i in felder:
40 if was != i:
41 widget[i].handler_block(handler[i])
42 widget[i].set_value(wert[i])
43 widget[i].handler_unblock(handler[i])
44
45 def __init__(self):
46 builder = gtk.Builder()
47 builder.add_from_file("temperatur.gtkbuilder")
48 for i in (("fenster", "schliessknopf") + felder):
49 widget[i] = builder.get_object(i)
50 for i in felder:
51 handler[i] = widget[i].connect("value_changed", self.geaendert, i)
52 widget["schliessknopf"].connect_object("clicked", gtk.Window.destroy, widget["fenster"])
53 widget["fenster"].connect("delete_event", self.delete_event)
54 widget["fenster"].connect("destroy", self.destroy)
55 widget["fenster"].show_all()
56 widget["celsius-feld"].set_value(100)
57
58 def main(self):
59 gtk.main()
60
61 if __name__ == "__main__":
62 app = TemperaturApp()
63 app.main()
Die Methode »main()« in Zeile 58 startet lediglich die Hauptschleife, »destroy()« in Zeile 16 beendet diese beim Aufruf, »delete_event()« in Zeile 13 ist ein Ereignishandler für das Schließen des Programms durch den Fenstermanager und gibt nur »False« zurück. Die Anwendungslogik steckt in »geaendert()« ab Zeile 19. Es handelt sich um einen Signalhandler, der aufgerufen wird, wenn eines der Zahlenfelder seinen Wert ändert.
Um so wenig Code wie möglich fünfmal schreiben zu müssen, verwendet der Entwickler das Tupel »felder« mit den Kennungen aller Zahlenfeld-Widgets in der GTK-Builder-Datei sowie die Dictionaries »wert{}« , »widget{}« und »handler{}« (Zeilen 6 bis 9), die die Widget-Objekte selbst, ihre Werte für die Neuberechnung sowie die Kennungen speichern, unter denen jeweils »geaendert()« als Signalhandler an sie gebunden ist. Dabei dient das Tupel »felder« als Indexmenge.
Der Konstruktor »__init__()« mit dem für den Aufbau des Dialogfensters zuständigen Code ist ab Zeile 45 zu sehen. Er lädt die GTK-Builder-Datei in das Objekt »builder« und entnimmt daraus mit »get_object()« die Widgets. So weit wie möglich setzt das Programm For-Schleifen ein, um über die Widgets zu iterieren, etwa beim Binden von »geaendert()« als Signalhandler an das Signal »value_changed« der Zahlenfelder in Zeile 51.
Die Methode »geaendert()« zeigt in den Zeilen 39 bis 43, wozu die Kennungen nützlich sind, die »gtk.Widget.connect()« beim Binden eines Signalhandlers zurückgibt. Sie lassen sich einsetzen, um eine Endlosschleife zu verhindern: Wenn das Programm den neu berechneten Wert in ein Zahlenfeld schreibt, würde das eigentlich eine Emission des Signals »value-changed« und damit wiederum eine Neuberechnung auslösen. Daher sperrt der Code vor dem Setzen des Wertes den Signalhandler des betreffenden Felds mit der Widget-Methode »handler_block()« , hinterher entsperrt er sie mit »handler_unblock()« wieder.
Linux
Unter Linux läuft das Programm wie erwartet ohne Probleme. Da Ubuntu-Kernkomponenten auf Python und GTK+ basieren, sind die benötigten Pakete »python-gtk2« und »python-glade2« samt ihren Abhängigkeiten ohnehin schon installiert. Aufgerufen mit »./temperatur.py« sieht das Ergebnis aus wie in Abbildung 2.
Mac OS X
Mac OS X 10.7.1 liefert zwar ein ziemlich aktuelles Python (2.7.1) mit, allerdings ohne Py GTK. Der derzeit neueste und schickste Paket-Installationsmanager Homebrew [5] verweigert aus Prinzip Pakete in seine Sammlung aufzunehmen, die bereits der Paketmanager einer Programmiersprache anbietet. Und leider scheitern auch die Python-Lösungen »easy_install« und »pip« . Es bleibt als einfachste Lösung, ein vollständiges Python-System samt Py GTK mit dem etwas älteren Installationswerkzeug Macports zu kompilieren. Ist Macports nach der Anleitung auf der Homepage [6] installiert, dann reicht die einfache Befehlszeile »sudo port install py27-gtk« . Der darauf folgende Kompiliervorgang kann ein wenig dauern – es empfiehlt sich, Geduld und eine Kanne des koffeinhaltigen Lieblingsgetränks bereitzuhalten.
Dann muss der Mac-Anwender noch sicherstellen, dass die Umgebungsvariablen »PATH« und »PYTHONPATH« korrekt gesetzt sind. Wenn sich Macports wie verlangt in »/opt/local« befindet, heißt dies, die folgende Zeilen in die Datei »~/.profile« einzutragen und sich neu einzuloggen:
export PATH=/opt/local/bin:$PATH export PYTHONPATH=/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/
Der Aufruf von »./temperatur.py« führt dazu, dass das Icon des X-Servers im Dock auftaucht; dann startet der Temperatur-Umrechner, ungefähr wie auf Abbildung 3 zu sehen.
Es fällt auf, dass die Widgets das Standardaussehen von GTK+ haben – im Gegensatz zum Ubuntu-Look in Abbildung 2, aber auch zum nativen Look von Mac OS X. Daran lässt sich mit vertretbarem Aufwand auch wenig ändern. Es existieren ein natives Quartz-Backend GTK-OSX [7], das leider nicht gerade hingebungsvoll gepflegt wird, und auch eine Themen-Engine für GTK+, die native Anwendungen imitiert. Deren Installation ist allerdings sehr aufwändig.
Windows 7
Unter Windows ist zunächst ein Python-Interpreter erforderlich, und zwar in der 32-Bit-Version [8], denn die 64-Bit-Version unterstützt Py GTK derzeit nicht. Hat der Anwender das MSI-Paket mit dem Durchklicken des Installationsassistenten installiert, kann er als nächsten Schritt einfach das All-in-one-Installationspaket für Py GTK einspielen [9], das alle Abhängigkeiten mitbringt. Anschließend muss er unter Umständen noch der Pfad setzen:
set PYTHONPATH=%PYTHONPATH%;C:\Python27\Lib\site-packages
Danach kann er in der Eingabeaufforderung mit »temperatur.py« das Programm starten. Wer nun kaputte Zeichen sieht, sollte durch Öffnen in einem Editor sicherstellen, dass die GTK-Builder-Datei UTF8-kodiert ist. Stimmt alles, so sieht das Ergebnis aus wie in Abbildung 4. Unter Windows kommt die GTK+-Anwendung einem nativen Aussehen sichtlich näher als unter Mac OS X.
Installationssache
Wie dieser Artikel zeigt, ist die Portierung von einfachen Py-GTK-Programmen zwischen den drei großen Desktop-Plattformen kein Programmierproblem. Der wesentliche Aufwand besteht in der Installation der nötigen Pakete auf den Zielsystemen. Unter Mac OS X bleibt eine einfach zu installierende GTK+-Implementierung auf nativer Basis wünschenswert, unter Windows fehlt eine 64-Bit-taugliche Py-GTK-Version. Auf den beiden Nicht-Linux-Plattformen ist die neueste GTK+-Version 3.0 mit Python aber noch nicht reif für den Produktiveinsatz.
Infos
- GTK+: http://www.gtk.org
- Glade: http://glade.gnome.org
- Glade 3.8: http://launchpad.net/glade/3.8/3.8.0/+download/glade3-3.8.0.tar.gz
- Quelltexte zum Artikel: https://www.linux-magazin.de/static/listings/magazin/2011/11/gtk/
- Homebrew: https://github.com/mxcl/homebrew/
- Installation von Macports: http://www.macports.org/install.php
- Quartz-Backend GTK-OSX:http://gtk-osx.sourceforge.net
- Python für Windows: http://www.python.org/ftp/python/2.7.2/python-2.7.2.msi
- Py GTK für Windows: http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.24/pygtk-all-in-one-2.24.0.win32-py2.7.msi
- Py Gobject: http://live.gnome.org/PyGObject









