FH-Logo Fachhochschule Bielefeld, Labor für Technische Informatik
Studienarbeit - Für den Inhalt dieser Seite ist der Verfasser der Arbeit verantwortlich.

Python

Eine objektorientierte Scriptsprache für GUIs

Studienarbeit
an der Fachhochschule Bielefeld
Fachbereich Elektrotechnik


Inhaltsverzeichnis

 


Aufgabenstellung

Allgemein

Python ist wie Tcl und Perl eine Scriptsprache, die besonders schnelle Programmentwicklung unterstützt. Die Stärken liegen vor allem in der Entwicklung von grafischen Oberflächen. In der Studienarbeit sollen nun einfache Scripte zur Demonstration der Möglichkeiten die Python bietet erstellt werden. Dabei wird das Betriebssystem UNIX (bzw. Linux) verwendet.


In dieser Studienarbeit

Nach Absprache mit Herrn Mecklenburg wurde entschieden, das von Ihm in Tcl/Tk geschriebene Programm "et" in Python zu programmieren. Da diese Aufgabe etwas umfangreicher ist als in der allgemeinen Aufgabenstellung gefordert, werden die Möglichkeiten von Python nicht an mehreren, sondern nur an diesem einen Script demonstriert. Damit lassen sich zwar nicht alle Anwendungsgebiete von Python darstellen, jedoch wäre dies im Rahmen einer Studienarbeit auch nicht möglich.

Bei dem Programm "et" handelt es sich um eine grafische Bedienoberfläche für das PSST-Tool. Dieses Tool dient zum Plazieren von SDL-Prozessen auf parallelen Zielsystemen. Es analysiert den vom SDT-Tool erzeugten C-Code und legt im Dialog mit dem Benutzer die Verteilung des Systems fest.


Die Skriptsprache Python

Was ist Python?

Python ist eine interpretierte, interaktive, objektorientierte Programmiersprache. Die Entwicklung begann 1991 beim Stichting Mathematisch Centrum in Amsterdam. Als Guido van Rossum damals eine Testumgebung für ein neues Betriebssystem brauchte, aber keine geeignete Programmiersprache fand, entwickelte er kurzerhand Python. Damit war er in der Lage schnell kurze Testprogramme zu schreiben. Python hat eine sehr klare Syntax, eine übersichtliche Struktur und nur eine geringe Anzahl von Schlüsselwörtern. Nach kurzer Zeit stellten er und andere Anwender jedoch fest, daß Python für mehr als nur das Testen von Betriebssystemen nicht geeignet war, und so erweiterte man die Sprache; denn auf die Erweiterbarkeit von Python wurde schon bei der Entwicklung viel Wert gelegt. Neue Module lassen sich leicht in Python selbst oder wenn die Ausführungsgeschwindigkeit eine Rolle spielt, in C oder C++ programmieren. C-Programme (Module) können bei der Compilierung des Python-Interpreters bereits direkt integriert werden. Die zur Zeit aktuellste Distribution von Python ist die Version 1.4, die für fast alle Plattformen (UNIX, Linux, Windows 95, NT und 3.x, OS/2, Mac, ...) verfügbar ist, und zwar kostenlos, d. h. alles darf frei kopiert werden.

Der Name Python hat übrigens nichts mit der gleichnamigen Schlangenart zu tun, sondern kommt von der britischen Comedy-Gruppe 'Monty Pythons Flying Circus', deren Fan Guido van Rossum ist.


Der Interpreter

Wie oben bereits erwähnt wurde ist Python eine Interpretersprache. Es gibt drei Möglichkeiten ein Pythonprogramm auszuführen: interaktiv, mit explizitem oder mit implizitem Interpreteraufruf.
Um in den interaktiven Modus zu gelangen, startet man das Programm python ohne Argumente. Unter Linux meldet sich dieses daraufhin mit folgender Ausgabe:

  Python 1.4 (Apr 27 1997) [GCC 2.7.2]
  Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
  >>>

Im interaktiven Modus ist '>>>' der Standardprompt, der den Benutzer auffordert, einen Befehl einzugeben. Jeder Befehl wird vom Interpreter sofort ausgeführt und das Ergebnis ggf. angezeigt. Ist das Kommando am Ende einer Zeile noch nicht komplett, wird dies in der nächsten Zeile durch einen geänderten Prompt angezeigt ('...').

Möchte man ein Programm jedoch öfters aufrufen, wird man normalerweise nicht im interaktiven Modus arbeiten, da hier das Programm jedesmal wieder erneut eingegeben werden muß und auch keine Änderungen eines einmal eingegebenen Befehls mehr vorgenommen werden können, sondern schreibt das Programm mit einem x-beliebigen Texteditor. Ein Pythonprogramm erhält normalerweise die Endung 'py', dies ist jedoch nicht zwingend vorgeschrieben. Um das Programm zu starten, gibt es zwei Varianten: entweder ruft man python mit dem Programmnamen als Argument, oder man verwendet die #!-Konvention und schreibt in die erste Zeile z.B. #!/usr/local/bin/python, wodurch Python automatisch aufgerufen wird. Im Gegensatz zum interaktiven Modus werden die Befehle in einem Rutsch vom Interpreter eingelesen und dann ausgeführt.

Nun gibt es einige Leute die bei dem Wort 'Interpreter' schon sofort abwinken und auf einen Compiler schwören. Sicherlich hat ein Compiler auch seine Vorzüge, wenn es um die Ausführungsgeschwindigkeit geht und darum, daß das Ganze auch ohne ein weiteres Zusatzprogramm, nämlich dem Interpreter, läuft. Aber auch der Python-Interpreter hat mehrere Vorteile, die die Nachteile teilweise wieder wettmachen.

Ein Vorteil ist die Möglichkeit ein Programm schnell mal zu testen, da die Kompilation, die nach jeder kleinen Änderung notwendig wäre, entfällt. Beim Interpreter wird jede Anweisung erst zur Laufzeit übersetzt. Zur Geschwindigkeitssteigerung erzeugt Python jedoch einen sogenannten Bytecode, der eine interne Repräsentation des Programms darstellt. Dieser Bytecode ist dem tatsächlichen Programm noch ziemlich nahe und somit mit einer zeitaufwendigen Kompilation nicht zu vergleichen. Der Bytecode erhält die Endung 'pyc'.

Ein weiterer Vorteil ist die Tatsache, daß Python plattformunabhängig ist. Es wird kein Maschinencode erzeugt, wie bei einem Compiler. Ein Programm, das sie auf einem PC schreiben, würde ohne weiteres z. B. auch auf einem Apple Macintosh laufen.

Vorteil Nummer drei ist die Fehlertoleranz von Python. Python überprüft jede Anweisung auf eventuelle Fehler bevor sie ausgeführt wird. Dadurch sind Systemabstürze so gut wie ausgeschlossen. Tritt ein Fehler auf, wird von Python eine sogenannte Ausnahme (engl.: exception) erzeugt. Auf die Ausnahmebehandlung wird weiter unten noch etwas genauer eingegangen.

Diese Eigenschaften des Interpreters haben Python den Ruf gebracht, daß hiermit eine schnelle Entwicklung von Prototypen zu realisieren ist.


Struktur und Dokumentation

In diesem Kapitel soll kurz die Blockstruktur von Python und die Möglichkeiten der Dokumentation eines Programms bzw. Moduls dargestellt werden.

In jeder Sprache muß der Anfang und das Ende eines zusammengehörenden Blocks gekennzeichnet werden. In PASCAL werden dazu die Schlüsselwörter begin und end verwendet und in C die geschweiften Klammern. Nicht so in Python, hier bestimmt die Einrückung die Struktur des Programms. Somit sieht ein Pythonprogramm immer gut formatiert aus, da die Einrückung nicht freiwillig, wie bei den meisten anderen Sprachen, sondern zwingend vorgeschrieben ist. Dazu ein kleines Beispiel:

  def summe(x):
    if x==1:
      return 1
    return x+summe(x-1)

  print summe(10)

In diesem Beispiel wird eine Funktion summe definiert, die einen Parameter x erwartet und dann rekursiv alle Zahlen von 1 bis x addiert. Die letzte Zeile gibt das Ergebnis von summe(10) auf dem Bildschirm aus.

Wie man sieht, werden die Blöcke nur durch ihre Einrückung zusammengefaßt. Eine neue Ebene der Einrückung muß jeweils nach einem Doppelpunkt beginnen, wobei die Einrückungstiefe vollkommen gleichgültig ist; wichtig ist nur, daß diese innerhalb eines Blocks beibehalten wird. In diesem Fall wurde immer um 2 Zeichen eingerückt.

Eine Sonderbehandlung erfahren Kommentare, mehrzeilige Strings und Klammerkonstruktionen.

Die Dokumentation von Modulen und der darin definierten Klassen und Funktionen, z.B. für grafische Browser, ist in Python auf einfachste Weise möglich: Ist die erste Anweisung in einer Definition ein String, dann wird dieser an das Attribut __doc__ zugewiesen. Ein Modul test.py soll dies verdeutlichen:

  """Hier kommt die Dokumentation des Moduls hin. Da es sich
     meist um mehrzeiligen Text handelt, muß man die langen
     Strings (die mit drei Anführungszeichen) verwenden. """

  class Foo:
    """Die Klasse Foo dient der Erklärung von
       Dokumentationsstrings."""

    def bar(self):
      "Auch einzelne Methoden können Dokumentation haben"

In Python greift man auf Attribute eines Objekts zu, indem man zwischen Objekt und Attribut einen Punkt setzt. Lädt man das Modul mit import test, dann stehen die Strings test.__doc__, test.Foo.__doc__ und test.Foo.bar.__doc__ zur Verfügung.


Die Ausnahmebehandlung

Python unterstützt die strukturierte Ausnahmebehandlung. Wenn der Interpreter einen Fehler feststellt, wird eine Ausnahme generiert, und anschließend beginnt die Suche nach einer Behandlung dieser Ausnahme. Zuerst wird in der Funktion, in der der Fehler aufgetreten ist, geguckt, ob die Ausnahme abgefangen wird. Wenn dies nicht der Fall ist, leitet der Interpreter die Ausnahme an den Rufer weiter. Sollte die Ausnahme letztendlich im Hauptprogramm angekommen sein, ohne daß sie vom Programm abgefangen wurde, wird vom System eine Fehlermeldung ausgegeben und die Programmausführung unterbrochen. Etwas anders ist dies bei den Modulen für die grafische Oberfläche. Dort wird zwar auch eine Fehlermeldung ausgegeben, aber das Programm läuft weiter, d. h. die Fenster auf der grafischen Oberfläche bleiben erhalten.

Um die Ausnahme behandeln zu können, sprich abzufangen, muß man das Programmstück, bei dem die Ausnahme auftreten kann, in einen try-except-Block einschließen. Hinter der except-Anweisung kann als Argument die Ausnahme, auf die reagiert werden soll, angegeben werden. Ein optionales zweites Argument nimmt dann den Parameter auf, der den Fehler genauer beschreibt. Steht except für sich alleine, wird jede Ausnahme abgefangen. Nach einem try-Block dürfen beliebig viele except-Blöcke folgen, abgeschlossen von einem optionalen else-Block, der ausgeführt wird, wenn kein Fehler aufgetreten ist:

  try:
    f = open("test.txt", "r")   # Öffnet test.txt zum lesen
  except IOError,parameter:
    # parameter beschreibt den I/O-Error
  except:
    # Irgendein anderer Fehler
  else:
    # Kein Fehler aufgetreten

Außer den Standardausnahmen von Python, lassen sich auch eigene Ausnahmen erzeugen. Ausnahmen können Strings, Klassen oder Instanzen von Klassen sein. Die Beschreibung der Möglichkeiten würde an dieser Stelle jedoch zu weit führen.

Eine zweite Form auf Ausnahmen zu reagieren, ist der try-finally-Block. Tritt ein Fehler auf, springt das Programm in den finally-Block. Im Gegensatz zur except-Klausel, wird die Ausnahme hier allerdings nicht behandelt, sondern nur die Anweisungen in diesem Block ausgeführt. Damit hat man die Möglichkeit Aktionen anzugeben, die unbedingt ausgeführt werden sollen, bevor die Ausnahme eventuell vom Rufer abgefangen wird. Ein mögliches Einsatzgebiet ist das Schließen von Dateien, um bei einem Fehler keine geöffneten File-Objekte zurückzulassen.


Grafische Oberflächen

Es wird heute wohl kaum noch einen modernen Rechnerplatz ohne eine grafische Oberfläche, wie Microsoft Windows, X-Window, oder ähnliche, geben. Deshalb muß eine moderne Anwendung, damit sie sich gut verkaufen läßt, eine grafische Benutzerschnittstelle (GUI) haben. Dadurch werden die Programme zwar nicht besser, aber komfortabler für den Endanwender. Dieser braucht nicht mehr haufenweise Befehle auswendig lernen, sondern wählt nur noch die gewünschte Anweisung aus einem Menü aus oder klickt auf einen entsprechenden Knopf.

Wie oben schon beschrieben wurde, meldet auch Python sich nach dem Start nur mit einer kurzen Meldung und erwartet dann vom Anwender eine Eingabe. Ohne entsprechende Kenntnisse wird man jetzt nicht weiterkommen und kann nur hoffen, daß das Handbuch in greifbarer nähe liegt.

Um ein Pythonprogramm nun mit einer grafischen Benutzeroberfläche auszustatten, gibt es in Python mehrere Möglichkeiten.

Das am weitesten ausgereifte Modul ist Tkinter. Dieses basiert auf dem Software-Paket Tcl/Tk und wird zur Zeit fast ausschließlich eingesetzt. Da Tcl/Tk mittlerweile auch für fast alle Plattformen verfügbar ist, sind die Programme, die Tkinter verwenden, portabel.

Ein weiteres Modul ist Rivet. Dieses stellt Funktionen zur Programmierung von Tk zur Verfügung, ohne das man den Tcl-Interpreter braucht. Dadurch wird die Ausführung in Tk wesentlich beschleunigt. Ein Nachteil ist jedoch die unterschiedliche Programmierschnittstelle zu Tkinter. Diesen Nachteil macht das Modul Trinket wieder weg, indem es die gleichen Funktionen wie Tkinter bereit stellt. Die beiden letzten Module befinden sich allerdings noch in der Entwicklung und sind somit in der aktuellen Distribution von Python noch nicht enthalten.


Tk in Python

Da die letztgenannten Module für die meisten Anwender noch nicht zur Verfügung stehen, beschränkt sich die Programmierung hier auf Tkinter. Mit diesem Modul wurde eine Integration von Tk in Python vorgenommen. Der Hintergrund dafür war, daß mit Tk bereits eine leistungsfähige und durchdachte Bibliothek für grafische Oberflächen vorhanden ist. Das in Python geschriebene Modul Tkinter basiert auf dem Basismodul _tkinter, welches in C geschrieben wurde. Dieses verpackt die Anweisungen und schickt sie so an Tcl, daß sie dort ausgeführt werden können. Von diesem mehrstufigen Aufbau, wie er in Abb.1 dargestellt ist, bekommen weder der Programmierer noch der Anwender etwas mit. Tkinter stellt eine Reihe von Klassen bereit, mit denen die verschiedenen grafischen Elemente von Tk erzeugt und verändert werden können. Dadurch läßt sich vieles wesentlich einfacher schreiben, als dies in Tcl möglich wäre.

Abbildung 1: Beziehungen zwischen Python und Tcl/Tk.

Ein kleiner Nachteil von Tkinter ist allerdings der, daß Tcl/Tk auf dem gleichen Rechner auch installiert werden muß. Unter Linux sollte das kein Problem sein, da hier Tcl/Tk standardmäßig bei eigentlich jeder Distribution mitgeliefert wird, und auch für andere Plattformen wie etwa Microsoft Windows und Apple Macintosh sind die neuen Versionen kostenlos erhältlich.


Widgets

Tkinter basiert auf Tk, und Tk basiert seinerseits auf Widgets. Ein Widget steht für einen Bereich in einem Fenster, der ein bestimmtes Aussehen und eine bestimmte Funktion hat. Zum Beispiel steht ein Widget vom Typ Label für einen Bereich, in dem Text dargestellt werden kann. Tkinter stellt für alle gängigen Widgets von Tk eine entsprechende Klasse zur Verfügung. Die Klassen-Hierarchie von Tkinter zeigt Abb. 2.

Abbildung 2: Hierarchie aller Klassen von Tkinter.

Ein einfaches Beispiel, das ein Fenster zur Eingabe eines Textes ausgibt, zeigt das folgende Programm:

  import Tkinter

  tk = Tkinter.Tk()
  f = Tkinter.Frame(tk, relief='ridge', borderwidth=2)
  f.pack()
  l = Tkinter.Label(f, text='Name:')
  l.pack(side='left')
  e = Tkinter.Entry(f, width=25, relief='sunken', borderwidth=2)
  e.pack()

  Tkinter.mainloop()

Zuerst wird das Modul Tkinter geladen. Um nun eine grafische Oberfläche zu erstellen, muß zuerst eine Instanz (Objekt) der Klasse Tk erzeugt werden. Diese braucht man, um ein Applikations-Fenster (Hauptfenster) zu definieren. In dem Fenster werden dann ein Rahmen (Klasse Frame), ein Schriftfeld (Klasse Label) und ein Eingabefeld (Klasse Entry) erzeugt. Das Aussehen wird über Schlüsselwortparameter definiert. Die Parameter nennt man bei Widgets auch Ressourcen. relief gibt die Umrandungsart, borderwidth die Umrandungsdicke, text den auszugebenen String und width die Breite des Elements an. Mit der Methode pack wird jedes Widget angezeigt. Diese Methode unterstützt wiederum Schlüsselwortparameter, die angeben, wo das Widget plaziert werden soll. Für alle Ressourcen, die nicht gesetzt werden, nimmt Tkinter Standardwerte an.

Bis zu diesem Zeitpunkt ist auf dem Bildschirm noch nichts zu sehen. Erst mit der letzten Zeile, in der die Methode mainloop aufgerufen wird, wird die Applikation dargestellt. mainloop ist eine Endlosschleife, die auf Eingaben des Anwenders wartet und darauf entsprechend reagiert.

Abbildung 3: Dieses Fenster liefert das Programm unter Windows 95.

In Tk gibt es eine Widget-Hierarchie. Das heißt, jedes Widget hat einen Parent, der immer als Erstes bei der Erzeugung der Instanz angegeben wird. Durch diese Gruppierung hat man ein wichtiges Gestaltungsmittel für den Aufbau der grafischen Oberfläche. In dem Beispiel hat der Rahmen die Applikation tk als Parent, er ist also ein Teil des Hauptfensters. Das Schrift- und das Eingabefeld haben als Parent den Rahmen (Widget der Klasse Frame). Dies bedeutet, daß sie innerhalb des Rahmens, und nicht daneben im Hauptfenster, plaziert werden. Daraus ergibt sich folgende Baumstruktur:

Abbildung 4: Widget-Hierarchie des Programms.


Python und das Internet

Für die drei wesentlichsten Komponenten der Internetunterstützung, stehen in Python entsprechende Module zur Verfügung, und zwar für:

  • "traditionelle" Kommunikation (sockets, ftp, e-mail),
  • Webclients und Applets und
  • Webserver.

Da dieses Thema doch sehr umfangreich ist und es ganze Bücher gibt, die sich ausschließlich mit der Internetprogrammierung in Python beschäftigen, soll an dieser Stelle nur soviel gesagt werden:

Es gibt zur Zeit einen mit der Unterstützung von Guido van Rossum entwickelten und komplett in Python programmierter Webbrowser namens 'Grail'. Damit lassen sich in Python geschriebene Applets laden und auf dem eigenen Rechner ausführen. Und auch für einige Webserver wird bereits Python anstelle von Perl eingesetzt.


Das Beispielskript

In diesem Kapitel sollen einige Möglichkeiten von Python anhand eines Beispielskripts erläutert werden. Der Schwerpunkt wird dabei auf der Entwicklung der grafischen Oberfläche liegen.


Programmstruktur

Um sich in einem fremden Programmcode leichter zurechtzufinden, sollte dieser eine festgelegte Struktur haben. Auch in Python gibt es so eine (unverbindliche) Programmstruktur, die sich von der anderer Sprachen, wie z. B. Pascal oder C, eigentlich nicht unterscheidet. Ein Pythonprogramm besteht im allgemeinen aus folgenden Abschnitten:

  1. Dokumentation
  2. Modulimporte
  3. Klassen- und Funktionsdefinitionen
  4. Hauptprogramm

Auch das Beispielskript besteht aus diesen vier Abschnitten, auf die, bis auf die sich wohl selbst erklärende Dokumentation, hier eingegangen wird.


Module importieren

Nach der Dokumentation folgt der Abschnitt der Modulimporte. In diesem Abschnitt werden alle benötigten Standard-Pythonbibliotheken geladen. Es gibt drei Formen der import-Anweisung, die hier zur Demonstration auch alle verwendet wurden:

  import os
  import sys
  import string
  import Tkinter

Damit werden die vier Module geladen und unter ihrem Namen im aktuellen Namensraum zur Verfügung gestellt, d. h. möchte man z. B. die Funktion _test von Tkinter aufrufen, muß man Tkinter._test() eingeben.

  from Tkconstants import *

Mit dieser Anweisung wird das Modul geladen und alle Variablen, Klassen und Funktionen des Moduls direkt im aktuellen Namensraum zur Verfügung gestellt. Der Name des Moduls braucht also nicht mehr mit angegeben werden. Ausgenommen sind alle Namen die mit einem Unterstrich beginnen, wie z. B. die eben angesprochene Funktion _test. Diese Anweisung sollte man nur anwenden, wenn man sicher ist, daß es zu keinen Überschneidungen mit den Namen in anderen Modulen kommt. Bei dem Modul Tkconstants, in dem Konstanten für die Programmierung mit Tkinter definiert sind, kann dies normalerweise nicht passieren, da dort alle Namen durchgehend groß geschrieben sind.

  from Dialog import Dialog

Lädt das Modul und fügt nur die angegebenen Namen in den aktuellen Namensraum ein. In diesem Fall nur die Klasse Dialog.


Klassen und Funktionen

Da es sich bei Python um eine objektorientierte Programmiersprache handelt, kann man nicht nur Funktionen, sondern auch Klassen definieren. Eine Klasse definiert eine Menge von Objekten, die alle über die gleichen Methoden (Funktionen), die in der Klasse definiert sind, verfügen. Objekte sind Instanzen von Klassen.

In dem Beispielskript wurden drei Klassen definiert, die zeigen sollen wie dies in Python gemacht wird. Nun handelt es sich hier allerdings nicht nur um mein erstes Pythonprogramm, sondern auch um meinen ersten Versuch objektorientiert zu programmieren. Ich hoffe, daß es mir wenigstens einigermaßen gelungen ist.

Der folgende Programmauschnitt zeigt die Definition der Klassen und Funktionen:

  class Info:
    ···
  class SystemSetup(Info):
    ···
  class Placing(Info):
    ···

  def pipeopen(cmd):
    ···
  def readline(fd):
    ···

Die Klasse Info erzeugt ein Info-Fenster,die Klasse SystemSetup fragt nach einigen Voreinstellungen und mit der Klasse Placing können die Prozesse dann plaziert werden. Die beiden Klassen SystemSetup und Placing haben Info als Basisklasse und erben somit deren Methoden. Es handelt sich bei den Klammerausdrücken also nicht um Parameter die übergeben werden, wie bei Funktionen, sondern um die Basisklasse(n).


Das Hauptprogramm

Das Hauptprogramm wurde relativ kurz gehalten, da die meisten Aufgaben von den Methoden der Klassen ausgeführt werden:

  if __name__ == "__main__":
    tk = Tkinter.Tk()
    tk.withdraw()
    s = SystemSetup(tk)
    staticframes, signallist, processlist = s.start()
    if s.ok:
      p = Placing(tk, staticframes, signallist, processlist)
      p.start()

Der Name eines Moduls, einer Klasse oder einer Funktion wird in Python im Attribut __name__ des jeweiligen Objekts abgelegt. Dem Hauptprogramm wird grundsätzlich der Name __main__ zugewiesen. In dem Beispiel wird das Hauptprogramm von et.py daher nur aufgerufen, wenn das Programm direkt mit Python (als Hauptprogramm) gestartet wird. Importiert man et.py als Modul, stehen alle Klassen und Funktionen zur Verfügung, das Hauptprogramm wird allerdings nicht ausgeführt.

Im Hauptprogramm wird zuerst eine Instanz der Klasse Tk erzeugt und der Variablen tk zugewiesen. Da jede Klasse ihre eigenen Fenster erzeugt, wird das Hauptfenster nicht benötigt und somit durch Aufrufen der Methode withdraw der Instanz tk unsichtbar geschaltet.

Im weiteren Programmverlauf werden noch ein bzw. zwei weitere Instanzen erzeugt, und mit der Methode start wird die grafische Oberfläche aufgebaut.

Eine Eigenschaft von Python läßt sich hier noch sehr gut erkennen: Der Aufruf s.start() liefert 3 Werte zurück, die man direkt an 3 Variablen (durch Komma getrennt) zuweisen kann.


Die grafische Oberfläche

Die grafische Benutzerschnittstelle von et.py besteht, sieht man von dem Dialog-Fenster für Fehlermeldungen, Fragen, usw. ab, aus insgesamt fünf verschiedenen Fenstern. An zwei Fenstern soll nun beispielhaft die Gestaltung einer grafischen Oberfläche in Python erläutert werden. Die angegebenen Programmstücke sind Ausschnitte aus dem Beispielskript, wie es im Anhang komplett abgedruckt ist. Die folgenden Bilder wurden mit Windows 95 erzeugt. Unter Linux werden Fenster, je nach verwendetem Window-Manager, etwas anders aussehen.

Startet man das Programm, meldet sich dieses mit einem Fenster wie in Abb. 5. Aber wie erzeugt man nun mit Python dieses Fenster?

Abbildung 5: Fenter der Klasse SystemSetup.

Als erstes wird ein eigenes Fenster benötigt. Dazu benutzt man die Tkinter-Klasse Toplevel. Eine Instanz dieser Klasse erzeugt ein Widget, das vom Window-Manager kontrolliert wird. Wie bei jeder Erzeugung einer Instanz für ein Widget, ist das erste Argument der Parent des Toplevel-Widgets. Anschließend vergibt man am besten auch gleich den Namen des Fensters und des Icons:

  self.top = Tkinter.Toplevel(master)
  self.top.title("ET")
  self.top.iconname("ET")

Die Angabe von self vor einem Namen, innerhalb einer Methode, bezieht sich auf das Objekt selbst; somit handelt es sich hier um eine Instanzvariable. Instanzvariablen sind Variablen, von denen es eine Kopie pro Instanz (Objekt) gibt und auf die man nur über diese Instanz zugreifen kann.

In der oberen Hälfe des Fensters befinden sich die Eingabemöglichkeiten, deren Aufbau aus Frame-, Label- und Entry-Widget schon aus dem Kapitel Widgets bekannt ist:

  self.topframe = Tkinter.Frame(self.top, relief=RAISED, borderwidth=2)
  self.topframe.pack(side=TOP,fill=X)

  self.sdl_label = Tkinter.Label(self.topframe, text="SDL system name")
  self.sdl_label.pack(side=TOP, anchor=CENTER)

  self.sdl_entry = Tkinter.Entry(self.topframe, textvariable=self.sys_name)
  self.sdl_entry.pack(side=TOP, fill=X)

  self.midframe = Tkinter.Frame(self.top, relief=RAISED, borderwidth=2)
  self.midframe.pack(side=TOP, fill=X)

  self.exe_label = Tkinter.Label(self.midframe, text="executeable name")
  self.exe_label.pack(side=TOP, anchor=CENTER)

  self.exe_entry = Tkinter.Entry(self.midframe, textvariable=self.exe_name)
  self.exe_entry.pack(side=TOP, fill=X)
  self.exe_entry.insert(0, "test")

Neu bei der Methode pack (Geometrie-Manager) sind die Ressourcen fill und anchor. Mit fill wird angegeben, wie sich das Widget an den vom Geometrie-Manager reservierten Platz anpaßt, wenn das Fenster vom Anwender vergrößert wird. Durch X, dessen Wert im Modul Tkconstants definiert ist, paßt sich das Widget in X-Richtung an das veränderte Fenster an. Der Standardwert von fill ist None. None dient als Platzhalter für Variablen die eigentlich keinen Wert haben. Mit anchor gibt man die Ausrichtung des Widget im vom Geometrie-Manager reservierten Platz an.

In der Variable hinter dem Parameter textvariable, wird der String aus dem Eingabefeld abgespeichert. Mit der Methode insert wird der String "test" an der Position 0 in das Eingabefeld eingefügt.

Für die Eingabe der statischen Frames wurde ein Scale-Widget verwendet. Damit lassen sich über eine Skala nur vorgegebene Werte einstellen, und somit kann die Abfrage ob eine Eingabe sinnvoll ist entfallen:

  self.botframe = Tkinter.Frame(self.top)
  self.botframe.pack(side=TOP, fill=X, padx=5, pady=5)

  self.frm_label = Tkinter.Label(self.botframe, text="Number of static frames")
  self.frm_label.pack(side=TOP)
  self.frm_scale = Tkinter.Scale(self.botframe, from_=1, to=20,
                                 tickinterval=19, orient=HORIZONTAL)
  self.frm_scale.pack(side=TOP, fill=X)
  self.frm_scale.set(1)

Der Wertebereich der Skala reicht von 1 (Ressource from_) bis 20 (Ressource to) mit einer Skaleneinteilung von 19 (Ressource tickinterval), dadurch läßt sich der Wert in Einerschritten ändern. Mit dem Schlüsselwortparameter orient wird die Ausrichtung in horizontaler oder vertikaler Richtung angegeben, und mit der Methode set wird der Schieber zu Anfang auf 1 gesetzt.

Zum Schluß müssen noch die drei Knöpfe im unteren Teil des Fensters plaziert werden, um weitere Aktionen ausführen zu können:

  self.ok_button = Tkinter.Button(self.top, text="OK", command=self.ok_command)
  self.ok_button.pack(side=LEFT, expand=ON, padx=5, pady=5)

  self.cancel_button = Tkinter.Button(self.top, text="Cancel", command=self.cancel_command)
  self.cancel_button.pack(side=LEFT, expand=ON, padx=5, pady=5)

  self.info_button = Tkinter.Button(self.top, text="Info", command=self.info_command)
  self.info_button.pack(side=LEFT, expand=ON, padx=5, pady=5)

Einen Knopf läßt sich mit der Klasse Button erstellen. In diesem Beispiel haben die Knöpfe keinen Frame als Parent, sondern das Toplevel-Widget. Der Ressource command kann eine Funktion oder Methode übergeben werden, die immer dann aufgerufen wird, wenn der Knopf vom Benutzer gedrückt wird.

Sobald das Programm nun in die Hauptschleife (Methode mainloop) eintritt, wird ein Fenster wie in Abb. 5 auf dem Bildschirm erscheinen.

Bei dem zweiten Beispiel handelt es sich um ein Fenster, dessen genaues Aussehen von einer Variablen abhängig ist, und bei dem ein anderer Geometrie-Manager verwendet wird.

Abbildung 6: Dieses Fenster wurde mit dem Geometrie-Manager Grid
erzeugt. Die gestrichelten Linien dienen hier nur zur Verdeutlichung.

Die Erzeugung eines Fensters geschieht analog zum obigen Beispiel und braucht deshalb an dieser Stelle nicht noch einmal wiederholt werden. Unabhängig von der Anzahl der statischen Frames ist die erste Zeile des Fensters:

  self.heading1 = Tkinter.Label(self.env_top, text="enable")
  self.heading1.grid(row=0, column=1)

  self.heading2 = Tkinter.Label(self.env_top, text="env. name")
  self.heading2.grid(row=0, column=2)

Die Methode grid zum Plazieren der Widgets arbeitet mit Zeilen (row) und Spalten (column). Die Breite einer Zeile oder Spalte richtet sich nach dem breitesten Widget, das beim Aufbau des Fensters in dieser Zeile oder Spalte ist.

Der weitere Aufbau geschieht tabellenförmig, wozu der Geometrie-Manager grid sehr gut geeignet ist:

  for i in range(staticframes+1):
    if i == staticframes:
      text = "dynamic frames"
    else:
      text = "frame "+`i`
    self.framenames = Tkinter.Label(self.env_top, text=text)
    self.framenames.grid(row=1+i, column=0)
    self.enable.append(Tkinter.BooleanVar())
    self.enable_env.append(Tkinter.Checkbutton(self.env_top, variable=self.enable[i]))
    self.enable_env[i].grid(row=1+i, column=1)
    self.names.append(Tkinter.StringVar())
    self.env_names.append(Tkinter.Entry(self.env_top,textvariable=self.names[i]))
    self.env_names[i].grid(row=1+i, column=2)

In dem Beispiel für Abb. 6 wird die Schleife 4 mal durchlaufen (staticframes = 3). Jede Zeile besteht aus einem Label-, einem Checkbutton- und einem Entry-Widget. Ein Checkbutton ist ein Knopf, der entweder ein- oder ausgeschaltet sein kann. Da der Zustand der Checkbutton und der Inhalt der Eingabefelder in Variablen abgespeichert werden muß, benutzt man dafür Listen. In Python sind Listen veränderbare Folgen von Objekten, die einen indizierten Zugriff unterstützen. Mit der Methode append kann ein neues Element am Ende der Liste angefügt werden. Wie man damit eine Liste von Widgets erzeugt, verdeutlicht obiger Programmausschnitt.

Hiermit soll die Vorstellung der grafischen Möglichkeiten in Python beendet werden, denn die anderen Fenster sind in einer ähnlicher Art und Weise aufgebaut.


Zum Abschluß

Meine persönliche Meinung zu Python ist, daß es sich um eine leistungsfähige und leicht zu erlernende Sprache handelt, die sich für kurze Testprogramme genauso gut eignet, wie für umfangreichere Projekte. Und durch die immer größer werdende Anzahl von Python-Anwendern, kann man davon ausgehen, daß die Entwicklung auch in Zukunft weitergehen wird. Die neuesten Distributionen von Python kann sich jeder kostenlos im Internet besorgen.

Hier konnte nur ausschnittsweise auf auf die Möglichkeiten von Python eingegangen werden. Hinweise auf weiterführende Literatur und Internetadressen befinden sich am Ende der Studienarbeit.


Anhang

Das Beispielskript et.py

#!/usr/local/bin/python

#   *** Studienarbeit ***
# --------------------------
#  Programm : et.py
#  Sprache  : Python 1.4
#  Autor    : Markus Merten
#  Matr.-Nr.: 294621
#  Datum    : 19.06.1997
# --------------------------


# Module importieren

import os
import sys
import string
import Tkinter

from Tkconstants import *
from Dialog import Dialog


class Info:

   """
   ############################################################################
    Klasse         : Info
    Basisklasse    : -
    Beschreibung   : Wärend der Initialisierung wird ein Info-Fenster erzeugt.
                     Dieses stellt eine kurze Information zu diesem Programm
                     und der momentan ausfürbaren Aktionen dar.
                     Durch die Methoden 'show_info' und 'hide_info' wird das
                     Fenster sichtbar bzw. unsichtbar geschaltet.
    Init-Parameter : Üergeordnetes Widget (master), Hilfetext
   ############################################################################
   """

   def __init__(self, master, helptext):
      self.info_top = Tkinter.Toplevel(master)
      self.info_top.transient(master)
      self.info_top.geometry("400x300+347+234")
      self.info_top.positionfrom(who="user")
      self.info_top.withdraw()
      self.info_top.title("ET info")
      self.info_top.iconname("ET info")
      self.info_top.protocol("WM_DELETE_WINDOW", self.hide_info)
      logopath = os.environ["EPSYLONKERNEL"]

      self.g_frame = Tkinter.Frame(self.info_top, relief=RIDGE, bd=2)
      self.g_frame.pack(side=TOP, fill=X, padx=3, pady=3)

      self.logo = Tkinter.Label(self.g_frame, bitmap="@"+logopath+"/etlogo.xbm")
      self.logo.pack(side=LEFT, expand=ON)

      self.label = Tkinter.Label(self.g_frame, justify=LEFT, wraplength="8c",
                                 text="ET stellt dem Benutzer eine grafische "
                                      "Bedienoberfläche fiür das PSST-Tool zur "
                                      "Verfügung. Dieses macht das Plazieren "
                                      "von SDL-Prozessen kom- fortabler und "
                                      "übersichtlicher.")
      self.label.pack(side=LEFT, expand=ON)

      self.s_frame = Tkinter.Frame(self.info_top, relief=RIDGE, bd=2)
      self.s_frame.pack(side=TOP, expand=ON, fill=BOTH, padx=3, pady=3)

      self.help = Tkinter.Label(self.s_frame, justify=LEFT, wraplength="13c",
                                text=helptext)
      self.help.pack(side=TOP, pady=5)

      self.OK_button = Tkinter.Button(self.s_frame, text="OK",
                                      command=self.hide_info)
      self.OK_button.pack(side=BOTTOM, pady=5)


   def show_info(self):
      self.info_top.grab_set()
      self.OK_button.focus_set()
      self.info_top.deiconify()


   def hide_info(self):
      self.info_top.grab_release()
      self.info_top.withdraw()


class SystemSetup(Info):

   """
   ############################################################################
    Klasse         : SystemSetup
    Basisklasse    : Info
    Beschreibung   : Hier können die Voreinstellungen, wie z. B. die Anzahl der
                     statisches Frames, eingegeben werden. Anschließend wird
                     das Programm 'psst' gestartet und die Parameter übergeben.
                     Sind die Parameter in Ordnung, wird die Signal- und die
                     Prozeßliste eingelesen.
    Init-Parameter : Übergeordnetes Widget (master)
   ############################################################################
   """

   def __init__(self, master):
      self.master = master

      self.signallist  = []
      self.processlist = []
      self.sys_name = Tkinter.StringVar()
      self.exe_name = Tkinter.StringVar()

      self.top = Tkinter.Toplevel(master)
      self.top.title("ET")
      self.top.iconname("ET")
      self.top.protocol("WM_DELETE_WINDOW", self.cancel_command)
      self._createWidgets()

      Info.__init__(self, self.top, "Geben Sie den Namen des SDL-Systems "
                                    "(ohne Endung!) und den Namen, den das "
                                    "ausführbare Programm erhalten soll, ein. "
                                    "Die Anzahl der statischen Frames kann "
                                    "von 1 bis max. 20 eingestellt werden.\n"
                                    "Zeigt sich nach dem Drücken auf  "
                                    "keine Reaktion, haben Sie wahrscheinlich "
                                    "vergessen einen der Namen einzugeben.")


   def _createWidgets(self):
      self.topframe = Tkinter.Frame(self.top, relief=RAISED, borderwidth=2)
      self.topframe.pack(side=TOP, fill=X)

      self.sdl_label = Tkinter.Label(self.topframe, text="SDL system name")
      self.sdl_label.pack(side=TOP, anchor=CENTER)

      self.sdl_entry = Tkinter.Entry(self.topframe, textvariable=self.sys_name)
      self.sdl_entry.pack(side=TOP, fill=X)

      self.midframe = Tkinter.Frame(self.top, relief=RAISED, borderwidth=2)
      self.midframe.pack(side=TOP, fill=X)

      self.exe_label = Tkinter.Label(self.midframe, text="executeable name")
      self.exe_label.pack(side=TOP, anchor=CENTER)

      self.exe_entry = Tkinter.Entry(self.midframe, textvariable=self.exe_name)
      self.exe_entry.pack(side=TOP, fill=X)
      self.exe_entry.insert(0, "test")

      self.botframe = Tkinter.Frame(self.top)
      self.botframe.pack(side=TOP, fill=X, padx=5, pady=5)

      self.frm_label = Tkinter.Label(self.botframe,
                                     text="Number of static frames")
      self.frm_label.pack(side=TOP)
      self.frm_scale = Tkinter.Scale(self.botframe, from_=1, to=20,
                                     tickinterval=19, orient=HORIZONTAL)
      self.frm_scale.pack(side=TOP, fill=X)
      self.frm_scale.set(1)

      self.ok_button = Tkinter.Button(self.top, text="OK",
                                      command=self.ok_command)
      self.ok_button.pack(side=LEFT, expand=ON, padx=5, pady=5)

      self.cancel_button = Tkinter.Button(self.top, text="Cancel",
                                          command=self.cancel_command)
      self.cancel_button.pack(side=LEFT, expand=ON, padx=5, pady=5)

      self.info_button = Tkinter.Button(self.top, text="Info",
                                        command=self.info_command)
      self.info_button.pack(side=LEFT, expand=ON, padx=5, pady=5)


   def start(self):
      self.sdl_entry.focus_set()
      self.master.mainloop()
      self.staticframes = self.frm_scale.get()
      self.top.destroy()
      return self.staticframes, self.signallist, self.processlist


   def ok_command(self):
      if (self.sys_name.get() != "") and (self.exe_name.get() != ""):
         self.message = self.start_psst()
         if self.message == "signallist":
            self.signallist = self.readlist("end signallist")
            readline(self.r); readline(self.r)
            self.processlist = self.readlist("end processlist")
            self.ok = YES
            self.master.quit()
         else:
            error = readline(self.r)
            nr = readline(self.r)
            Dialog(self.master, title="Error: "+nr, bitmap="error",
                   text=self.message, default=0, strings=("OK",))


   def cancel_command(self):
      self.ok = NO
      self.master.quit()


   def info_command(self):
      self.show_info()


   def start_psst(self):
      self.r, self.w, self.pid = pipeopen("psst")
      for i in range(12):
         readline(self.r)
      os.write(self.w, self.sys_name.get()+"\n")
      readline(self.r)
      os.write(self.w, self.exe_name.get()+"\n")
      readline(self.r)
      os.write(self.w, `self.frm_scale.get()`+"\n")
      message = readline(self.r)
      return message


   def readlist(self, endsignal):
      self.list = []
      line = readline(self.r)
      while line != endsignal:
         self.list.append(line)
         line = readline(self.r)
      return self.list


class Placing(Info):

   """
   ############################################################################
    Klasse         : Placing
    Basisklasse    : Info
    Beschreibung   : Hier können die einzelnen Prozesse plaziert und den Frames
                     ein TCL-Environment zugeordnet werden.
                     Danach kann das Programm noch mit 'aimk' übersetzt werden.
    Init-Parameter : Übergeordnetes Widget (master), Anzahl der statischen
                     Frames, Signal- und Prozeßliste
   ############################################################################
   """

   def __init__(self, master, staticframes, signallist, processlist):
      self.master = master

      self.button_dict = {}
      self.buttons     = []
      self.enable      = []
      self.enable_env  = []
      self.names       = []
      self.env_names   = []

      self.top = Tkinter.Toplevel(master)
      self.top.title("process instance placer")
      self.top.iconname("process instance placer")
      self.top.protocol("WM_DELETE_WINDOW", self.cancel_command)
      self.env_top = Tkinter.Toplevel(self.top)
      self.env_top.title("select environment")
      self.env_top.iconname("select environment")
      self.signal_top = Tkinter.Toplevel(self.top)
      self.signal_top.title("signallist")
      self.signal_top.iconname("signallist")
      self._createWidgets()

      Info.__init__(self, self.top,"In dem Fenster 'select environment' kann "
                                   "jedem Frame ein TCL-Environment zugeordnet "
                                   "werden. Dazu ist der Dateiname mit Endung "
                                   "einzugeben und der entsprechende Schalter "
                                   "zu aktivieren. Ohne die Angabe eines "
                                   "Namens hat der Schalter keinen Einflu˜!\n"
                                   "Das Plazieren der Prozeßinstanzen "
                                   "geschieht im Fenster 'process instance "
                                   "placer'. Für jeden Prozeß muß mindestens "
                                   "ein Schalter aktiviert werden.")


   def _createWidgets(self):

   # 'process instance placer' window
      self.frame1 = Tkinter.Frame(self.top)
      self.frame1.pack(side=TOP, expand=ON, fill=BOTH, padx=5, pady=5)
      self.frame2 = Tkinter.Frame(self.top)
      self.frame2.pack(side=TOP, expand=ON, fill=BOTH, padx=5, pady=2)
      self.frame3 = Tkinter.Frame(self.top)
      self.frame3.pack(side=TOP, expand=ON, fill=BOTH, padx=5, pady=5)

      self.ok_button = Tkinter.Button(self.frame3, text="OK",
                                      command=self.ok_command)
      self.ok_button.pack(side=LEFT, expand=ON)
      self.cancel_button = Tkinter.Button(self.frame3, text="Cancel",
                                          command=self.cancel_command)
      self.cancel_button.pack(side=LEFT, expand=ON)
      self.info_button = Tkinter.Button(self.frame3, text="Info",
                                        command=self.info_command)
      self.info_button.pack(side=LEFT, expand=ON)

      self.title1 = Tkinter.Label(self.frame1, text="\nprocess name")
      self.title1.pack(side=LEFT, anchor=W)
      self.title2 = Tkinter.Label(self.frame1, text="frame\n Nr.")
      self.title2.pack(side=LEFT, anchor=CENTER, expand=ON)
      self.title3 = Tkinter.Label(self.frame1, text="l = local\nd = dynamic")
      self.title3.pack(side=RIGHT, anchor=E)

      for i in range(len(processlist)):
         self.processname = Tkinter.Label(self.frame2, text=processlist[i]+"  ")
         self.processname.grid(row=i, column=0)
         for j in range(staticframes+2):
            self.buttons.append(Tkinter.BooleanVar())
            index = j+i*(staticframes+2)
            attr = `j`
            if j == 0:
               self.buttons[index].set(1)
            elif j == staticframes:
               attr = "d  "
            elif j == staticframes+1:
               attr = "l "
            framebutton = Tkinter.Checkbutton(self.frame2, text=attr,
                                              variable=self.buttons[index])
            framebutton.grid(row=i, column=1+j)
            framebutton.bind("<1>", self.buttons_set)
            self.button_dict[framebutton] = index

   # 'select environment' window
      self.heading1 = Tkinter.Label(self.env_top, text="enable")
      self.heading1.grid(row=0, column=1)
      self.heading2 = Tkinter.Label(self.env_top, text="env. name")
      self.heading2.grid(row=0, column=2)

      for i in range(staticframes+1):
         if i == staticframes:
            text = "dynamic frames"
         else:
            text = "frame "+`i`
         self.framenames = Tkinter.Label(self.env_top, text=text)
         self.framenames.grid(row=1+i, column=0)
         self.enable.append(Tkinter.BooleanVar())
         self.enable_env.append(Tkinter.Checkbutton(self.env_top,
                                variable=self.enable[i]))
         self.enable_env[i].grid(row=1+i, column=1)
         self.names.append(Tkinter.StringVar())
         self.env_names.append(Tkinter.Entry(self.env_top,
                               textvariable=self.names[i]))
         self.env_names[i].grid(row=1+i, column=2)

   # 'signallist' window
      for i in signallist:
         self.signals = Tkinter.Label(self.signal_top, text=i)
         self.signals.pack(anchor=W, expand=ON)
      self.icon_button = Tkinter.Button(self.signal_top, text="Icon",
                                        command=self.signal_top.iconify)
      self.icon_button.pack(expand=ON, pady=5)


   def start(self):
      self.master.mainloop()
      self.top.destroy()


   def ok_command(self):
      self.send_env_to_psst()
      self.place_instances()
      d = Dialog(self.master, title="compile ?", bitmap="question",
                 text="Soll das Programm auch gleich mit "
                      "'aimk' übersetzt werden.",
                 default=0, strings=("Yes", "No"))
      if d.num == 0:
         os.execv("/bin/sh", ["/bin/sh", "-c", "aimk"])
      self.master.quit()


   def cancel_command(self):
     self.master.quit()


   def info_command(self):
      self.show_info()


   def buttons_set(self, ev):
      nr = self.button_dict[ev.widget]
      sf = staticframes
      if (nr+1)%(sf+2) == 0:
         # Knopf 'l' gedrückt
         for i in range(nr-(sf+1),nr):
            self.buttons[i].set(0)
         if self.buttons[nr].get():
            self.buttons[nr-(sf+1)].set(1)
      else:
         # anderer Knopf gedrückt
         self.buttons[int((nr+1)/(sf+2)+1)*(sf+2)-1].set(0)


   def send_env_to_psst(self):
      readline(s.r)
      for i in range(staticframes+1):
         if i == staticframes:
            lines = 2
         else:
            lines = 3
         for j in range(lines):
            readline(s.r)
         if self.enable[i].get() and self.names[i].get() != "":
            os.write(s.w, self.names[i].get()+"\n")
         else:
            os.write(s.w, "n\n")


   def place_instances(self):
      for i in range(len(processlist)):
         for j in range(2):
            readline(s.r)
         send = ""
         for j in range(staticframes+2):
            index = j+i*(staticframes+2)
            if self.buttons[index].get():
               if send != "":
                  send = send+","
               if j == staticframes:
                  send = send+"d"
               elif j == staticframes+1:
                  send = "l"
               else:
                  send = send+`j`
         os.write(s.w, send+"\n")


def pipeopen(cmd):

   """
   ############################################################################
    Funktion      : pipeopen
    Beschreibung  : Es wird eine bidirektionale Pipe zwischen diesem Prozess
                    (Vaterprozess) und einem neuen (Kindprozess) erzeugt. Der
                    Kindprozess startet dann das gewünschte Programm.
    Parameter     : Name des zu startenden Programms
    Rückgabewerte : der jeweilige file descriptor zum Lesen und Schreiben
                    und die PID des Kindprozesses
   ############################################################################
   """

   cmd = ["/bin/sh", "-c", cmd]
   p2cread, p2cwrite = os.pipe()
   c2pread, c2pwrite = os.pipe()
   pid = os.fork()
   if pid == 0:
      os.close(0)
      os.close(1)
      if os.dup(p2cread) != 0:
         sys.stderr.write("pipeopen: bad read dup\n")
      if os.dup(c2pwrite) != 1:
         sys.stderr.write("pipeopen: bad write dup\n")
      for i in range(3, 100):
         try:
            os.close(i)
         except:
            pass
      try:
         os.execv(cmd[0], cmd)
      finally:
         os._exit(1)
      os._exit(1)
   os.close(p2cread)
   os.close(c2pwrite)
   return c2pread, p2cwrite, pid


def readline(fd):

   """
   ############################################################################
    Funktion      : readline
    Beschreibung  : Die Funktion liest alle Zeichen bis zum Newlinezeichen aus
                    einer Pipe und speichert sie in einem String.
    Parameter     : file descriptor zum Lesen
    Rückgabewerte : String der gelesenen Zeile
   ############################################################################
   """

   line = ""
   char = os.read(fd, 1)
   while char != "\n":
      line = line + char
      char = os.read(fd, 1)
   return line



# Hauptprogramm

if __name__ == "__main__":
   tk = Tkinter.Tk()
   tk.withdraw()

   s = SystemSetup(tk)
   staticframes, signallist, processlist = s.start()
   if s.ok:
      p = Placing(tk, staticframes, signallist, processlist)
      p.start()


# -- EOF --

Die Fenster des Beispielskripts

Die folgenden Abbildungen zeigen die Fenster von et.py. Als Beispiel wurden generation als SDL-Systemname, test als Name des ausführbaren Programms und 3 statische Frames gewählt.

Die Bilder wurden aus technischen Gründen alle unter Windows 95 erzeugt. Das hier abgedruckte Programm in seiner Originalfassung läuft jedoch nur unter Linux!

Abbildung 7: Dieses Fenster erzeugt die Klasse SystemSetup.

Abbildung 8: Info-Fenster von SystemSetup.

Abbildung 9: Diese Fenster erzeugt die Klasse Placing.

Abbildung 10: Info-Fenster von Placing.


Die Syntax von Python

Die vollständige Grammatik von Python in erweiterter Backus-Naur-Form (EBNF). Im einzelnen bedeuten

  • großgeschriebene Namen und Zeichenfolgen in Anführungszeichen Terminalsymbole,
  • der senkrechte Strich  |  die Alternative,
  • die runden Klammern  ( )  Gruppierung,
  • die geschweifte Klammer  { }  null- oder mehrmalige Wiederholung,
  • die eckige Klammern  [ ]  null- oder einmalige Wiederholung und
  • der Doppelpunkt  :  die Definition eines Nichtterminals.

  single_input  : NEWLINE | simple_stmt
                | compound_stmt NEWLINE.
  file_input    : {NEWLINE | stmt} ENDMARKER.
  eval_input    : testlist {NEWLINE} ENDMARKER.
  stmt          : simple_stmt | compound_stmt.
  simple_stmt   : small_stmt {';' small_stmt} [';'] NEWLINE.
  small_stmt    : expr_stmt | print_stmt
                | del_stmt | pass_stmt | flow_stmt
                | import_stmt | global_stmt | exec_stmt.
  expr_stmt     : testlist {'=' testlist}.
  print_stmt    : 'print' {test ','} [test].
  del_stmt      : 'del' exprlist.
  pass_stmt     : 'pass'.
  flow_stmt     : break_stmt
                | continue_stmt
                | return_stmt
                | raise_stmt.
  break_stmt    : 'break'.
  continue_stmt : 'continue'.
  return_stmt   : 'return' [testlist].
  raise_stmt    : 'raise' test [',' test [',' test]].
  import_stmt   : 'import' dotted_name {',' dotted_name}
                | 'from' dotted_name 'import' ('*' | NAME {',' NAME}).
  dotted_name   : NAME {'.' NAME}.
  global_stmt   : 'global' NAME {',' NAME}.
  exec_stmt     : 'exec' expr ['in' test [',' test]].
  compound_stmt : if_stmt | while_stmt | for_stmt
                | try_stmt | funcdef | classdef.
  if_stmt       : 'if' test ':' suite
                       {'elif' test ':' suite} ['else' ':' suite].
  while_stmt    : 'while' test ':' suite ['else' ':' suite].
  for_stmt      : 'for' exprlist 'in' testlist ':'
                       suite ['else' ':' suite].
  try_stmt      : 'try' ':' suite except_clause ':' suite
                       {except_clause ':' suite} ['else' ':' suite]
                | 'try' ':' suite 'finally' ':' suite.
  except_clause : 'except' [test [',' test]].
  suite         : simple_stmt
                | NEWLINE INDENT stmt {stmt} DEDENT.
  test          : and_test {'or' and_test} | lambdef.
  and_test      : not_test {'and' not_test}.
  not_test      : 'not' not_test | comparison.
  comparison    : expr {comp_or expr}.
  comp_op       : '<' | '>' | '==' | '>='
                | '<=' | '<>' | '!=' | 'in'
                | 'not' 'in' | 'is' | 'is' 'not'.
  expr          : xor_expr {'|' xor_expr}.
  xor_expr      : and_expr {'^' and_expr}.
  and_expr      : shift_expr {'&' shift_expr}.
  shift_expr    : arith_expr {('<<'|'>>') arith_expr}.
  arith_expr    : term {('+'|'-') term}.
  term          : factor {('*'|'/'|'%') factor}.
  factor        : ('+'|'-'|'~') factor | power.
  power         : atom {trailer} {'**' factor}.
  atom          : '(' [testlist] ')' | '[' [testlist] ']'
                | '{' [dictmaker] '}' | '`' testlist '`'
                | NAME | NUMBER | STRING {STRING}.
  lamdef        : 'lambda' [varargslist] ':' test.
  trailer       : '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME.
  subscriptlist : subscript {',' subscript} [','].
  subscript     : '.' '.' '.' | test | [test] ':' [test] [sliceop].
  sliceop       : ':' [test].
  exprlist      : expr {',' expr} [','].
  testlist      : test {',' test} [','].
  dictmaker     : test ':' test {',' test ':' test} [','].
  classdef      : 'class' NAME ['(' testlist ')'] ':' suite.
  funcdef       : 'def' NAME parameters ':' suite.
  parameters    : '(' [varargslist] ')'.
  varargslist   : {fpdef ['=' test] ','}
                       ('*' NAME [',' ('**'|'*' '*') NAME]
                | ('**'|'*' '*') NAME)
                | fpdef ['=' test] {',' fpdef ['=' test]} [','].
  fpdef         : NAME | '(' fplist ')'.
  fplist        : fpdef {',' fpdef} [','].
  arglist       : argument {',' argument} [','].
  argument      : [test '='] test.


Python Manual-Seiten

NAME

python - an interpreted, interactive, object-oriented programming language

SYNOPSIS python [ -d ] [ -i ] [ -s ] [ -u ] [ -v ] [ -c command | script | - ] [ arguments ]

DESCRIPTION Python is an interpreted, interactive, object-oriented programming language that combines remarkable power with very clear syntax. For an introduction to programming in Python you are referred to the Python Tutorial. The Python Library Reference documents built-in and standard types, constants, functions and modules. Finally, the Python Reference Manual escribes the syntax and semantics of the core language in (perhaps too) much detail.

Python's basic power can be extended with your own module written in C or C++. On most systems such modules may be dynamically loaded. Python is also adaptable as an extension language for existing applications. See the internal documentation for hints.

COMMAND LINE OPTIONS -d Turn on parser debugging output (for wizards only, depending on compilation options).

-i When a script is passed as first argument or the -c option is used, enter interactive mode after executing the script or the command. It does not read the $PYTHONSTARTUP file. This can be useful to inspect global variables or a stack trace when a script raises an exception.

-s Suppresses the automatic printing of expressions entered in interactive mode (useful when input is actually generated e.g. by Emacs).

-u Force stdout and stderr to be totally unbuffered.

-v Print a message each time a module is initialized, showing the place (filename or built-in module) from which it is loaded.

-c command

Specify the command to execute (see next section). This terminates the option list (following options are passed as arguments to the command).

INTERPRETER INTERFACE The interpreter interface resembles that of the UNIX shell: when called with standard input connected to a tty device, it prompts for commands and executes them until an EOF is read; when called with a file name argument or with a file as standard input, it reads and executes a script from that file; when called with -c command, it executes the Python statement(s) given as command. Here command may contain multiple statements separated by newlines. Leading whitespace is significant in Python statements! In non-interactive mode, the entire input is parsed befored it is executed.

If available, the script name and additional arguments thereafter are passed to the script in the Python variable sys.argv, which is a list of strings (you must first import sys to be able to access it). If no script name is given, sys.argv is empty; if -c is used, sys.argv[0] contains the string æ-cÆ. Note that options interpreted by the Python interpreter itself are not placed in sys.argv.

In interactive mode, the primary prompt is æ>>>Æ; the second prompt (which appears when a command is not complete) is æ...Æ. The prompts can be changed by assignment to sys.ps1 or sys.ps2. The interpreter quits when it reads an EOF at a prompt. When an unhandled exception occurs, a stack trace is printed and control returns to the primary prompt; in non-interactive mode, the interpreter exits after printing the stack trace. The interrupt signal raises the KeyboardInterrupt exception; other UNIX signals are not caught (except that SIGPIPE is sometimes ignored, in favor of the IOError exception). Error messages are written to stderr.

FILES AND DIRECTORIES These are subject to difference depending on local installation conventions:

/usr/local/bin/python

Recommended location of the interpreter.

/usr/local/lib/python1.4 Recommended location of the directory containing the standard modules.

ENVIRONMENT VARIABLES PYTHONPATH Augments the default search path for module files. The format is the same as the shellÆs $PATH: one or more directory pathnames separated by colons. Non-existant directories are silently ignored. The default search path is installation dependent, but always begins with æ.Æ, (for example, .:/usr/local/lib/python ). The default search path is appended to $PYTHONPATH. If a script argument is given, the directory containing the script is inserted in the path in front of $PYTHONPATH. The search path can be manipulated from within a Python program as the variable sys.path.

PYTHONSTARTUP If this is the name of a readable file, the Python commands in that file are executed before the first prompt is displayed in interactive mode. The file is executed in the same name space where interactive commands are executed so that objects defined or imported in it can be used without qualification in the interactive session. You can also change the prompts sys.ps1 and sys.ps2 in this file.

PYTHONDEBUG If this is set to a non-empty string it is equivalent to specifying the -d option.

PYTHONINSPECT If this is set to a non-empty string it is equivalent to specifying the -i option.

PYTHONSUPPRESS If this is set to a non-empty string it is equivalent to specifying the -s option.

PYTHONUNBUFFERED If this is set to a non-empty string it is equivalent to specifying the -u option.

PYTHONVERBOSE If this is set to a non-empty string it is equivalent to specifying the -v option.

SEE ALSO Python Tutorial
Python Library Reference
Python Reference Manual

AUTHOR Guido van Rossum
CNRI
1895 Preston White Drive
Reston, VA 20191
USA
E-mail: guido@cnri.reston.va.us, guido@python.org

INTERNET RESOURCES Web site: http://www.python.org
FTP site: ftp://ftp.python.org
Newsgroup: comp.lang.python

COPYRIGHT Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam, The Netherlands.

All Rights Reserved

Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the names of Stichting Mathematisch Centrum or CWI or Corporation for National Research Initiatives or CNRI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.

While CWI is the initial source for this software, a modified version is made available by the Corporation for National Research Initiatives (CNRI) at the Internet address ftp://ftp.python.org.

STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.


Literaturhinweise

Für die Ausarbeitung dieser Studienarbeit verwendete Literatur:

  • Martin von Löwis, Nils Fischbeck, Das Python-Buch: Referenz der objektorientierten Skriptsprache für GUIs und Netzwerke, 1.Auflage 1997, Addison-Wesley
  • Holger Mecklenburg, Bedienungsanleitung für EPSYLON Version 1.0, FH Bielefeld, Fachbereich Elektrotechnik
  • Bodo Bauer, Alexander Bisler, ... , S.u.S.E. Linux 4.2: Installation, Konfiguration und erste Schritte, 5. Auflage 1996, S.u.S.E. GmbH
  • Michael Wielsch, LINUX, 1. Auflage 1996, DATA BECKER

Weitere Literatur in englischer Sprache:

  • Aaron Watters, Guido van Rossum, James Ahlstrom, Internet Programming with Python, MIS Press/Henry Holt publishers 1996
  • Mark Lutz, Programming Python, O`Reilly & Associates 1996
 

Python Internetadressen:

  • Homepage : http://www.python.org
  • FTP-Server : ftp://ftp.python.org
  • Newsgroup : comp.lang.python
Unser Server befindet sich immer im Aufbau.

© 1997 Labor für Parallelverarbeitung, Fachhochschule Bielefeld
Letzte Änderung am 2008-11-17 durch Markus Merten
Wünschenswertes und Anregungen bitte an: webmaster@parallel.fh-bielefeld.de