|
|
Dieses Dokument ist verfübar auf: English Castellano Deutsch Francais Turkce |
von Hilaire Fernandes <hfernandes(at)april.org> Über den Autor: Hilaire Fernandes ist der Vize-Präsident von OFSET, einer Organisation, welche es sich zur Aufgabe gemacht hat, die Entwicklung freier Lernsoftware für das Gnome Desktop Projekt zu unterstützen. Er schrieb Dr. Geo, ein preisgekröntes Geometrieprogramm. Momentan arbeitet er an Dr. Genius, einem weiteren mathematischem Lernprogramm für Gnome. Übersetzt ins Deutsche von: Harald Radke <harryrat(at)postnuklear.de> Inhalt:
|
Zusammenfassung:
Diese Artikelserie zielt speziell auf die Neulinge unter den Lesern in Sachen Gnome Programmierung und GNU/Linux ab. Die gewählte Programmiersprache, Python, kommt ohne den sonst bei compilerorientierten Sprachen, wie etwa C, üblichen Ballast aus. Gewisse Grundkenntnisse über Python sind für das Verständnis dieses Artikels notwendig.
Die in diesem Artikel vorgestellten Programme benötigen folgende Werkzeuge:
Python-Gnome und LibGlade Installation vom Quelltext aus:
./configure make make install |
sollte es tun. (Ausführlichere Informationen kann man unter
[
http://www.linuxgazette.com/issue38/pollman.html] finden
Die Umgebungsvariable PYTHONPATH muss auf den Verzeichnispfad
zeigen, unter welchem die Python-Gnome Bindungen installiert worden sind.
Diese sind häufig zu finden unter:
/usr/local/lib/python1.5/site-packages oder
/usr/lib/python1.5/site-packages/.
Dort befinden alle für Gnome und LibGlade notwendigen Dateien,
zum Beispiel liegt dort libglade.py.
Die Variable PYTHONPATH
wird einfach dadurch gesetzt,
dass man folgende Zeilen in seine
.bash_profile Datei eintträgt:
PYTHONPATH=/usr/local/lib/python1.5/site-packages export PYTHONPATH |
Nicht vergessen, es kann sein, dass man seinen Python Code von einem Terminal aus ausführen muss, damit die Variable gesetzt ist.
Glade ist ein von Damon Chaplin entwickeltes Programm zur interaktiven Generierung von Programmen mit einer grafischer Oberfläche, in diesem Fall unter der Verwendung von Gnome/Gtk. In Glade kann die erzeugte Oberfläche in einer XML Datei gespeichert oder direkt in C Code überführt werden, um dann in einer C Quelltextdatei verwendet zu werden. Glade erlaubt desweiteren die Benennung der Handler/Funktionen, welche den jeweiligen Ereignissen der Oberfläche (Events) zugeordnet werden sollen. Beispielsweise kann die Funktion (genauer: deren Name) bestimmt werden, die bei der Auswahl eines bestimmten Menüpunktes aufgerufen werden soll.
LibGlade ist eine Bibliothek, welche Funktionen bietet, um eine grafische Oberfläche aus einer XML Datei heraus direkt zu erzeugen. Entwickelt wurde LibGlade von James Henstridge. Die jeweilige Applikation muss nur die XML Datei erkennen, im Allgemeinen besitzt sie die Endung .glade. LibGlade konstruiert dann aus ihr die Oberfläche. James Henstridge hat auch die LibGlade Python Bindungen geschrieben, welche neben anderen, im Gnome-Python Paket zu finden sind. LibGlade gestattet auch das (beinahe )automatische Verbinden der Funktionen, die in der .glade Datei deklariert worden, sind mit Funktionen im Python Code.
Folgendes Diagramm zeigt den Mechanismus allgemein. Um die Implmentierung der Python Bindungen zu verstehen, kann es manchmal notwendig sein, einen Blick auf die Gtk, Gnome und LibGlade Module von Python zu werfen, die in PYTHONPATH liegen, um sie mit der C Gtk/Gnome Entwickler Dokumentation zu vergleichen.
Für den Einstieg in die Gnome-Python Programmierung dient ein einfaches Farben Spiel, bei welchem Kinder Figuren der gleichen Farbe erkennen sollen. Dieses sehr graphisch orientierte Beispiel verwendet einige sehr schöne Gnome Widgets, wie etwa die Gnome Canvas und das Gnome Applikationsfenster. Die Spielregeln sind recht einfach: Das Spielfeld beinhaltet 16 Figuren, Kreise, Sterne und Quadrate, in verschiedenen Farben, die 8 Farbenpaare bilden. Sinn des Spieles ist es nun, nacheinander alle 8 Paare auszuwählen. Wer mag, kann erstmal einen Blick auf den Quelltext am Ende dieses Artikels werfen, um eine generelle Vorstellung zu bekommen und dann von hier aus weiterelesen.
Die Widgets
Nach dem Start von Glade erscheinen zwei Fenster. Eines ist eine Widget Werkzeugleiste namens Palette. Hier können aus den Kategorien GTK+ Basic, GTK+ Additional und Gnome die jeweiligen Objekte ausgesucht werden. Sind keine Gnome Widgets zu finden, wurde Glade möglicherweise ohne Gnome Unterstützung übersetzt. In diesem Fall sollte die Datei confiugre des Quelltext Paketes von Glade überprüft werden, mit configure --help werden die Optionen ausgegeben.
In dem anderen Fenster werden im Hauptteil die erzeugten Widgets aufgeführt.
Zuerst wird ein Gnome Applikationsfenster erzeugt. Dies ist ein Widget mit einem Menü und einer Werkzeugleiste. Beide sind frei beweglich. Am unteren Ende des Applikationsfensters befindet sich eine Statusleiste. Nachdem das Gnome Applikationsfenster erzeugt worden ist, kann man durch Öffnen des Widget Tree Dialoges (zu finden im Menü View von Glade) genauer herausfinden, welche Elemente genau dieses Widget enthält.
Nun wird eine Canvas im Hauptteil des Applikationsfenster erzeugt. Im Properties Dialog werden die maximale Größe und die Koordinaten der Canvas jeweils auf 400 gesetzt.
Als nächstes wird ein Gnome About Dialog generiert. Dessen Inhalt kann im Properties Dialog des Widgets geändert werden.
Diese Widgets sind alle in der Klasse Gnome der Werkzeugleiste Palette zu finden.
Nun sollten die nicht verwendetetn Iconknöpfe und Menüeinträge entfernt werden. In der Werkzeugleiste werden die Knöpfe Open und Save entfernt. Danach wird die Menüleiste editiert (mit der rechten Maustaste auf sie klicken und Edit Menu auswählen). Bis auf File->New, File->Exit, Setting->Preferences und Help->About werden alle Punkte entfernt.
Widget- und Handlernamen festlegen
Es werden die folgenden Namen den jeweiligen Elementen zugeordnet, sodass sie unter diesen in Python angesprochen werden können:
Die Namen der Handler sind die Namen der Funktionen, die bei Auftreten eines bestimmten Schnittstellenereignisses (Event) eines Widgets aufgerufen werden sollen. Dies bedeutet, dass später in Python Funktionen definiert werden, die, jedenfalls beinahe, diese Namen erhalten, wie später zu sehen sein wird. Beispielsweise soll beim Betätigen des Iconknopfes New eine Funktion aufgerufen werden, die das Spiel neu starten soll. Dazu muss in Glade ein Widget ausgewählt und dieses mittels des Signals Fensters (im Properties Dialog zu finden) entsprechend angepasst werden.
Im obigen Beispiel ist das richtige Signal clicked und der Handler der Funktionsname. Die folgende Tabelle führt alle Signale und Handler auf:
Im About Dialog:
Widget Name | Signal | Handler |
---|---|---|
about | clicked | gtk_widget_destroy |
about | close | gtk_widget_destroy |
about | destroy | gtk_widget_destroy |
Der Handler gtk_widget_destroy ist eine vordefinierte GTK Funktion, welche einfach das Widget schließt.
Für das Fenster colorApp werden von Glade automatisch die Signale und Handler der Menüpunkte eingetragen. Die jeweiligen Namen sind in unten stehender Tabelle zu finden. Wie man sieht, verwenden sowohl der Menüunterpunkt New, als auch der entsprechende Knopf den selben Handler, haben sie doch den gleichen Zweck:
Widget Name | Signal | Handler |
---|---|---|
button1 (new icon button on the toolbar |
clicked | on_new_activate |
new | activate | on_new_activate |
colorApp | destroy | on_exit1_activate |
exit1 | activate | on_exit1_activate |
about1 | activate | on_about_activate |
Der letzte Schliff
Es wird der Punkt Project Options über den Knopf Options der Glade Werkzeugleiste ausgewählt. In der Karte General, werden nun für das Projekt die unten gezeigten Einstellungen vorgenommen:
Die Datei, die die Widgets beschreibt, heisst color.glade. Der Pfad zum jeweiligen Heimatverzeichnis muss angepasst werden.
Nun kann die Datei vom File Menü aus gespeichert werden.
Von der Möglichkeit, selbst Quelltext zu erzeugen, wird hier nicht
Gebrauch gmeacht.
Soviel zu Glade, nun geht es mit Python weiter
Der vollständige Quelltext ist am Ende des Artikels zu finden. Er muss im gleichen Verzeichnis, wie die Datei color.glade abgespeichert werden.
from math import cos, sin, pi from whrandom import randint from gtk import * from gnome.ui import * from GDK import * from libglade import * |
Aus den Modulen math und whrandom werden einige, nicht Gnome spezifische, Funktionen wie etwa cos, sin, randint und die Konstante pi eingebunden. Die Gnome spezifischen Module sind gtk, GDK und gnome.ui. In C reicht es aus, gnome.h für alle Gnome Headerdateien einzubinden. In Python ist es notwendig, zuerst herauszufinden, in welchem Modul die Bindung für die jeweilige Gnome Funktion, die man haben will, zu finden ist. In einem Terminalfenster (Shell) kann man zum Beispiel mit folgendem Kommando nach dem Modul suchen, welches die Zeichenkette "canvas" beinhaltet:
cd /usr/local/lib/python1.5/site-packages/gnome grep canvas *.py |
Hierbei wurde angenommen, dass die Gnome Bindungen unter /usr/local/lib/python1.5/site-packages zu finden sind.
In diesem Beispiel für Python wird die Gnome Canvas für die Manipulation von Figuren, also der Sterne, Kreisen und Dreiecken, verwendet. Eine Canvas ist ein "Leinwand" für die Darsteullung beliebiger graphischer Elemente (etwa Ellipsen, Punkten, Linien oder Rechtecken), Textelemente oder gar anderer Widgets. Eine Canvas kann auch weitere Canvasgruppen enthalten. In solcher einer Gruppen werden dann die Elemente der Canavas plaziert, in diesem Fall die unterschiedlichen Figuren. Es gibt eine Standardgruppe, die sogenannte Wurzelgruppe (root canvas group). In dieser werden die Figuren plaziert.
Zuerst werden einige globale Variablen definiert:
Die erste Funktion, die aufgerufen wird, ist initColor. Sie erzeugt die Widgets aus der Datei color.glade heraus und verbindet die Handler mit den Widgets.
def initColor (): global rootGroup, canvas wTree = GladeXML ("color.glade", "colorApp") dic = {"on_about_activate": on_about_activate, "on_exit1_activate": mainquit, "on_new_activate":on_new_activate} wTree.signal_autoconnect (dic) canvas = wTree.get_widget ("canvas") rootGroup = canvas.root () |
Die Widgets werden mittels der Funktion GladeXML erzeugt. Dafür muss natürlich der Pfad der Datei color.glade entsprechend angegeben werden. Die Funktion generiert das Gnome Applikationsfenster colorApp, welches mit Glade definiert wurde und stellt es dar. Sie liefert ein Objekt, einer richtigen Klasse, zurück, dass einige nützliche Methoden bereitstellt.
Als nächstes werden Handler, die in Python definiert worden
sind (dazu später mehr) mit den in der Datei color.glade
definierten Widgets verbunden. Dazu muss eine Tabelle erzeugt werden,
in der entsprechende Schlüsseleinträge für jeden Handlername, der
in der Datei color.glade definiert worden ist, erstellt werden:
on_about_activate, on_exit1_activate und
on_new_activate. Jedem dieser Einträge wird der Name einer in Python
definierten Funktion zugwiesen.
Schließlich erledigt die Methode signal_autoconnect den Rest.
Zuletzt werden noch die Verweise auf die Canvas, die während des GladeXML Aufrufes erzeugt worden ist (ein GnomeCanvas Objekt in Python) und auf die Wurzelgruppe, ein GnomeCanvasGroup Objekt, gesichert.
Nützliche Tips
Es existiert keine wirkliche Dokumentation, welche die Gnome Bindungen für Python beschreiben. Jedoch kann man recht viel Material über die Gnome Programmierung in C finden, und zwar auf der Gnome WWW Site. Diese Dokumentation kann recht hilfreich sein, allerdings wird man schon ein wenig in die Gnome Bindung für Python selbst reinschauen müssen, um sie wirklich nutzen zu können:
Zu finden ist sie unter /usr/local/lib/python1.5/site-packages/gnome/ oder /usr/lib/python1.5/site-packages/gnome/
Was man erfährt:
Für jeden Einsatz von Gnome in Python kann man so an die entsprechende Dokumentation kommen. Der Leser sollte sich mit der jeweiligen Gnome Dokumentation auseinandersetzen und so mehr über diese Funktionen lernen.
Drei Handler werden mit der Oberfläche verbunden. Diese sind: on_about_activate, on_new_activate und mainquit. Letzterer ist eine Python Funktion, durch welche das Programm (und Python) verlassen wird.
def on_about_activate(obj): "display the about dialog" about = GladeXML ("color.glade", "about").get_widget ("about") about.show () |
Dieser Handler öffnet das About Fenster. Zuerst wird eine Referenz auf den about Dialog gesichert. GladeXML erzeugte ihn bereits durch das GladeXML Objekt. Wie oben schon erwähnt, ist GladeXML ein Python Objekt, u.a. mit der Methode get_widget. Diese liefert ein Objekt vom Typ GtkWidget zurück, welches eine Methode show besitzt.
Tips
Ein Blick auf GtkWidget in der Datei gtk.py zeigt, dass dieses die Methode show besitzt. Obiger Handler kann also auch so implementiert werden: GladeXML("color.glade","about").get_widget("about").show().
def on_new_activate (obj): global rootGroup, colorShape for item in colorShape: item.destroy () del colorShape[0:] buildGameArea (rootGroup) |
Diese Funktion erzeugt eine neue Spielfläche. Die noch existierenden Figuren werden zuerst zerstört. Die Formen sind Objekte vom Typ GnomeCanvasItem, abgeleitet von der Klasse GtkObject. In dieser Klasse ist auch die Methode destroy zu finden. Danach wird die neue Spielfläche erzeugt.
Definition der Figuren
Die Funktion buildGameArea steuert die Generierung des Spielfeldes und der GnomeCanvasGroup Gruppe. Die Figuren, vom Typ GnomeCanvasItem, werden über Aufrufe von buildShape erzeugt. Als Figuren kommen Kreise, Quadrate oder Sterne in Frage.
Die Figuren werden in Abhängigkeit der jeweiligen Form erzeugt:
item = group.add ("ellipse", x1 = x - r, y1 = y - r, x2 = x + r, y2 = y + r, fill_color = color, outline_color = "black", width_units = 2.5) [...] item = group.add ("rect", x1 = x - a, y1 = y - a, x2 = x + a, y2 = y + a, fill_color = color, outline_color = "black", width_units = 2.5) [...] item = group.add ("polygon", points = pts, fill_color = color, outline_color = "black", width_units = 2.5) |
Die Variable group verweist auf ein Objekt vom Typ GnomeCanvasGroup. Ein Blick in die Datei ui.py zeigt auf, dass die Klasse GnomeCanvasGroup ein Methode namens add besitzt. Deren erstes Argument tp erwartet eine Zeichenkette, die den Typ des Elementes, welches hinzugefügt werden soll, angibt. Als weitere Argumente sind Paare von Schlüsselwörtern und Werten. Für einen Überblick der gültigen Schlüsselwörter sollte man sich die Klassen GnomeCanvasRect, GnomeCanvasEllipse und GnomeCanvasPolygon in ui.py anschauen.
ellipse und rectangle sind ziemlich ähnlich, die beiden Abszissen- und Ordinatenwerte beschreiben die obere linke, sowie die untere rechte Ecke ihres umschließenden Rechteckes (top-left und bottom-right). Der Koordinatenursprung der Canvas ist standardmäßig oben links (top-left). polygon erwartet points als Werte, ein Liste von Koordinatenpaaren, die die Punkte des Polygones bestimmen. Die weiteren Argumente sind selbsterklärend.
Ereignisbehandlung der Formen
Jeder Figur, die erzeugt wird, wird ein Ereignis (Event) zugeordnet. Dies wird am Ende der Funktion buildShape getan.
item.connect ('event', shapeEvent) colorShape.append (item) |
Dafür wird einfach die Methode connect der Klasse GtkObject, eine Oberklasse von GnomeCanvasItem, verwendet. Als erstes Argument wird das Signal übergeben. Da GnomeCanvasEvent mit einem einzigen Signal, namentlich event, alle Ereignisse abdeckt, wird einfach event übergeben. Der zweite Parameter ist der Name des Handlers, hier shapeEvent. Optional kann noch ein drittes Argument übergeben werden, was aber hier nicht notwendig ist. Das wars !
Hier nun der Event-Handler für die Figuren:
def shapeEvent (item, event): global selectedItem, itemToSelect, colorShape if event.type == ENTER_NOTIFY and selectedItem != item: #highligh outline item.set(outline_color = 'white') elif event.type == LEAVE_NOTIFY and selectedItem != item: #unlight outline item.set(outline_color = 'black') elif event.type == BUTTON_PRESS: #select the item if not selectedItem: item.set (outline_color = 'white') selectedItem = item elif item['fill_color_gdk'] == selectedItem['fill_color_gdk'] \ and item != selectedItem: #destroy both item item.destroy () selectedItem.destroy () colorShape.remove (item) colorShape.remove (selectedItem) selectedItem, itemToSelect = None, itemToSelect - 1 if itemToSelect == 0: buildGameArea (rootGroup) return 1 |
Wird dieser Handler aufgerufen, so verweist item auf die jeweilige Figur, die von dem Ereignis betroffen ist. Desweiteren steht in event die Art des Ereignisses. An GdkEvent Ereignissen interessant sind hier nur die folgenden drei:
Jeglicher Python Code, der nicht für Gnome relevant war, wurde ausgelassen, ihn zu verstehen sollte nicht so schwer sein. Ziel dieses Artikels war es, dem Leser zu zeigen, wie er selbständig herausfinden kann, was wo wie funktioniert: Dazu wurde in die Gnome Bindungen von Python, die Gnome C Headerdateien und in die Dokumentation zur Programmierung von Gnome in C geschaut. Natürlich wurde desweiteren gezeigt, wie einfach und doch mächtig Gnome Canvas und Glade/LibGlade sind. Der Leser kann von hier aus weiter experimentieren und den Quelltext erweitern (die Quelltextdateien sind hier zu finden).
#!/usr/bin/python # Couleur - Teo Serie # Copyright Hilaire Fernandes 2000 # Release under the terms of the GPL licence version 2 # You can get a copy of the license at http://www.gnu.org # # Select shapes with same color # from math import cos, sin, pi from whrandom import randint from gtk import * from gnome.ui import * from GDK import * from libglade import * width, itemToSelect = 400, 8 selectedItem = rootGroup = canvas = None # to keep trace of the canvas item colorShape =[]; def on_about_activate(obj): "display the about dialog" about = GladeXML ("color.glade", "about").get_widget ("about") about.show () def on_new_activate (obj): global rootGroup, colorShape for item in colorShape: item.destroy () del colorShape[0:] buildGameArea (rootGroup) def shapeEvent (item, event): global selectedItem, itemToSelect, colorShape if event.type == ENTER_NOTIFY and selectedItem != item: #highligh outline item.set(outline_color = 'white') elif event.type == LEAVE_NOTIFY and selectedItem != item: #unlight outline item.set(outline_color = 'black') elif event.type == BUTTON_PRESS: #select the item if not selectedItem: item.set (outline_color = 'white') selectedItem = item elif item['fill_color_gdk'] == selectedItem['fill_color_gdk'] \ and item != selectedItem: #destroy both item item.destroy () selectedItem.destroy () colorShape.remove (item) colorShape.remove (selectedItem) selectedItem, itemToSelect = None, itemToSelect - 1 if itemToSelect == 0: buildGameArea (rootGroup) return 1 def buildShape (group, number, type, color): "build a shape of 'type' and 'color'" global colorShape w = width / 4 x, y, r = (number % 4) * w + w / 2, (number / 4) * w + w / 2, w / 2 - 2 if type == 'circle': item = buildCircle (group, x, y, r, color) elif type == 'squarre': item = buildSquare (group, x, y, r, color) elif type == 'star': item = buildStar (group, x, y, r, 0.4, randint (3, 15), color) elif type == 'star2': item = buildStar (group, x, y, r, 0.6, randint (3, 15), color) item.connect ('event', shapeEvent) colorShape.append (item) def buildCircle (group, x, y, r, color): item = group.add ("ellipse", x1 = x - r, y1 = y - r, x2 = x + r, y2 = y + r, fill_color = color, outline_color = "black", width_units = 2.5) return item def buildSquare (group, x, y, a, color): item = group.add ("rect", x1 = x - a, y1 = y - a, x2 = x + a, y2 = y + a, fill_color = color, outline_color = "black", width_units = 2.5) return item def buildStar (group, x, y, r, k, n, color): "k: factor to get the internal radius" "n: number of branch" angleCenter = 2 * pi / n pts = [] for i in range (n): #external points of the star pts.append (x + r * cos (i * angleCenter)) pts.append (y + r * sin (i * angleCenter)) #internal points of the star pts.append (x + r * k * cos (i * angleCenter + angleCenter / 2)) pts.append (y + r * k * sin (i * angleCenter + angleCenter / 2)) pts.append (pts[0]) pts.append (pts[1]) item = group.add ("polygon", points = pts, fill_color = color, outline_color = "black", width_units = 2.5) return item def getEmptyCell (l, n): "get the n-th non null element of l" length, i = len (l), 0 while i < length: if l[i] == 0: n = n - 1 if n < 0: return i i = i + 1 return i def buildGameArea (group): global itemToSelect, selectedItem itemColor = ['red', 'yellow', 'green', 'brown', 'blue', 'magenta', 'darkgreen', 'bisque1'] itemShape = ['circle', 'squarre', 'star', 'star2'] emptyCell = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] itemToSelect, i, selectedItem = 8, 15, None for color in itemColor: # two items of same color n = 2 while n > 0: cellRandom = randint (0, i) cellNumber = getEmptyCell (emptyCell, cellRandom) emptyCell[cellNumber] = 1 buildShape (group, cellNumber, itemShape[randint (0, 3)], color) i, n = i - 1, n - 1 def initColor (): global rootGroup, canvas wTree = GladeXML ("color.glade", "colorApp") dic = {"on_about_activate": on_about_activate, "on_exit1_activate": mainquit, "on_new_activate":on_new_activate} wTree.signal_autoconnect (dic) canvas = wTree.get_widget ("canvas") rootGroup = canvas.root () initColor () buildGameArea (rootGroup) mainloop () |
|
Der LinuxFocus Redaktion schreiben
© Hilaire Fernandes, FDL LinuxFocus.org Einen Fehler melden oder einen Kommentar an LinuxFocus schicken |
Autoren und Übersetzer:
|
2002-02-24, generated by lfparser version 2.25