Im Katalog suchen

Linux - Wegweiser zur Installation & Konfiguration, 3. Auflage

Online-Version

Copyright © 2000 by O'Reilly Verlag GmbH & Co.KG

Bitte denken Sie daran: Sie dürfen zwar die Online-Version ausdrucken, aber diesen Druck nicht fotokopieren oder verkaufen. Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

Wünschen Sie mehr Informationen zu der gedruckten Version des Buches Linux - Wegweiser zur Installation & Konfiguration oder wollen Sie es bestellen, dann klicken Sie bitte hier.


vorheriges Kapitel Inhaltsverzeichnis Stichwortverzeichnis nächstes Kapitel

Programmieren mit Tcl und Tk

Die Tool Command Language oder Tcl (»tickel« gesprochen) ist eine einfache, interpretierte Sprache, die in einigen Aspekten Ähnlichkeiten mit der Bourne-Shell und Perl aufweist. Der größte Vorteil von Tcl liegt darin, daß es sowohl erweiterbar ist als auch in andere Anwendungen eingebunden werden kann. Tcl ist besonders beliebt in Verbindung mit der Erweiterung Tk (Toolkit), die das wohl einfachste Interface zur Programmierung mit Fenstern bietet. Außerdem hat Tcl den zusätzlichen Vorteil, daß darin geschriebene Programme ohne große Probleme sowohl unter Unix als auch unter Windows und auf dem Macintosh laufen.

Mit Erweiterung ist gemeint, daß Sie der Sprache Tcl eigene Befehle hinzufügen können, indem Sie einfach ein paar C-Routinen schreiben. Mit einbinden ist gemeint, daß Sie ein C-Programm mit den Tcl-Libraries binden können, so daß dieses Programm den vollen Zugriff auf die Sprache Tcl erhält. Obwohl die meisten Tcl-Programme in Form von Skripten entstehen und von einem vorkompilierten Tcl-Interpreter ausgeführt werden, haben Sie auch die Möglichkeit, die Interpreter-Routinen in Ihre eigene Anwendung einzubinden.

Nehmen wir beispielsweise an, daß Sie einen Debugger schreiben wollen, der ähnlich wie gdb von der Befehlszeile aus gesteuert wird. Der Debugger würde sich mit einem Prompt melden, und der Benutzer hätte die Möglichkeit, Befehle wie step und breakpoint einzugeben.

Solange die Syntax der Befehle für Ihren Debugger noch einfach ist, könnten Sie ohne Schwierigkeiten Ihre eigenen Routinen in C schreiben, die einen Befehl einlesen und verarbeiten. Dies wird allerdings schon wesentlich schwieriger, wenn der Benutzer auch in der Lage sein soll, Variablen, Makros, neue Funktionen usw. zu definieren.

Statt von Grund auf neue Routinen zu schreiben, könnten Sie ganz einfach einen Tcl-Interpreter in Ihren Debugger einbinden. Die Befehle, die der Benutzer eingibt, würden dann von den Interpreter-Routinen ausgewertet. Diese Routinen stehen in Form von C-Library-Funktionen zur Verfügung.

Die Sprache Tcl selbst enthält bereits viele, viele Befehle. Sie kennt Kontrollstrukturen wie while- und for-Schleifen, hat die Fähigkeit, Funktionen zu definieren, kennt Routinen zur Bearbeitung von Strings und Listen sowie arithmetischen Funktionen usw.

Zusätzlich zu diesen Tcl-eigenen Routinen müßte Ihr Debugger Befehle wie die bereits erwähnten step und breakpoint zur Verfügung stellen. Sie würden solche Befehle innerhalb Ihrer Anwendung in C schreiben und dem Tcl-Interpreter mitteilen, wie sie zu benutzen sind.

Damit kann Ihr Debugger auf den vollen Funktionsumfang von Tcl zugreifen. So könnte zum Beispiel die Konfigurationsdatei des Debuggers aus einem einfachen Tcl-Skript bestehen. Innerhalb dieses Skripts hätte der Benutzer dann die Möglichkeit, neue Funktionen und Variablen zu definieren, und könnte dabei die in Tcl enthaltenen Fähigkeiten nutzen.

Icon

Kapitel 10

Zu den vielen Erweiterungen von Tcl gehört Tk, das viele Befehle enthält, die Ihre Anwendung unter dem X Window System laufen lassen. (Wir beschreiben X in Kapitel 10, Das X Window System installieren.) Es ist erstaunlich einfach, X-basierte Anwendungen als Tk-Skript zu schreiben. Die folgende Tcl/Tk-Anwendung zum Beispiel zeigt ein Textfenster an, in das ein Dateiname eingegeben werden kann. Anschließend startet ein xterm mit einem vi-Prozeß darin, um die Datei zu editieren.

#!/usr/local/bin/wish -f # Label-Widget namens .l erzeugen label .l -text "Filename:" # Eingabe-Widget namens .e erzeugen entry .e -relief sunken -width 30 -textvariable fname # Beide Widgets im Fenster der Anwendung plazieren pack .l -side left pack .e -side left -padx 1m -pady 1m # Nach RETURN im Eingabefenster, xterm starten bind .e <Return> { exec xterm -e vi $fname }

Wir werden gleich auf die Syntax des Skripts eingehen - Sie können aber jetzt schon erkennen, daß wir in weniger als 20 Zeilen Code eine nichttriviale X-Anwendung geschrieben haben. Wenn dieses Skript ausgeführt wird, sieht das Ergebnis aus wie Abbildung 13-1.

Screenshot

Abbildung 13-1: Von Tk erzeugtes, einfaches Fenster

Die Nützlichkeit von Tk kann man schon daran ablesen, daß sowohl die Skriptsprache Python als auch die Skriptsprache Perl entsprechende Schnittstellen zu Tk unterhalten.

Für solche Tcl-Anwendungen, die nur interne Tcl-Routinen benutzen, steht der vorkompilierte Interpreter tclsh zur Verfügung. Dieser Interpreter liest einfach einen Tcl-Befehl nach dem anderen ein und führt ihn aus. Für unseren Debugger würden wir ein neues Programm erstellen, das dann mit den Library-Routinen des Tcl-Interpreters gebunden wird.

Icon

[51]

In ähnlicher Weise gibt es für Tk-Anwendungen, die nur die Standard-Tcl-Befehle und Tk-Widgets benutzen, den Interpreter wish (Window-Shell). Wie Sie sehen, wird das oben angeführte Skript von wish ausgeführt. Wenn Sie neue Tcl-Befehle und Tk-Widgets einführen wollten, könnten Sie ein C-Programm schreiben und mit den Tcl- und Tk-Bibliotheken binden. In diesem Abschnitt befassen wir uns mit der Erstellung von einfachen Tcl- und Tk-Skripten, die unter tclsh oder wish ausgeführt werden. Eine Schnellreferenz zu Tcl/Tk finden Sie in Tcl/Tk in a Nutshell von Paul Raines und Jeff Tranter.

Ein Intensivkurs in Tcl

Die Sprache Tcl ist sehr einfach zu lernen. Wenn Sie überhaupt schon mit anderen Skriptsprachen wie der Bourne- oder C-Shell vertraut sind, brauchen Sie sich vor Tcl/Tk nicht zu fürchten.

Icon

[50] Tcl/Tk

Deshalb werden wir nicht allzuviel Zeit auf die Sprache Tcl selbst verwenden. Sie ist überschaubar und kann mit Hilfe der verschiedenen Tcl-Manpages oder John Ousterhouts hervorragendem Buch Tcl and the Tk Toolkit erlernt werden. Dieses Buch beschreibt nicht nur, wie Tcl- und Tk-Skripten geschrieben werden, sondern auch, wie Sie die Tcl/Tk-Libraries in Ihren eigenen Anwendungen einsetzen.

Lassen Sie uns mit einem einfachen Beispiel beginnen. Das folgende Tcl-Skript zählt die Anzahl der Zeilen in einer bestimmten Datei.

1 #!/usr/local/bin/tclsh -f 2 3 if {$argc != 1} { 4 error "lc < filename >" 5 } 6 7 set thefile [open [lindex $argv 0] r] 8 set count 0 9 10 while {[gets $thefile line] >= 0} { 11 set count [expr $count + 1] 12 } 13 14 puts "Read $count lines."

Die Zeilen 3 bis 5 sollen mit einer einfachen if-Anweisung sicherstellen, daß das Skript mit einem Argument aufgerufen wird - nämlich dem Namen der Datei, in der die Zeilen gezählt werden sollen. Der Befehl if hat zwei Argumente - einen Ausdruck und einen Codeblock, der ausgeführt wird, wenn der Ausdruck wahr ist. (Wie in C gilt: null = falsch; andere Werte erfüllen die Bedingung.)

Die beiden Argumente zum if-Befehl stehen in Klammern. Klammern werden einfach benutzt, um eine Gruppe von Wörtern (oder Zeilen) zu einem einzelnen Argument zusammenzufassen. Obwohl diese Syntax vielleicht an C oder Perl erinnert, ist die Art und Weise, in der Tcl die Befehle auswertet, doch recht einfach. So darf sich ein Befehlsargument (in diesem Fall der Code in den Zeilen 3 bis 5, der auch den Befehl error enthält) nur dann über mehr als eine Zeile erstrecken, wenn die öffnende Klammer am Ende einer Zeile steht. Wenn wir diese if-Anweisung als

if {$argc != 1} { error "lc < filename >" }

geschrieben hätten, hätte Tcl folgende Fehlermeldung ausgegeben:

Error: wrong # args: no script following "$argc != 1" argument wrong # args: no script following "$argc != 1" argument while executing "if {$argc != 1} " (file "./lc.tcl" line 3)

Mit anderen Worten: Tcl weiß nicht, daß das zweite Argument zu if in der nächsten Zeile steht.

Der eigentliche if-Befehl in Zeile 4 ruft den Befehl error auf, um eine Fehlermeldung anzuzeigen und das Tcl-Skript zu beenden.

In Zeile 7 öffnen wir die Datei, deren Name als erstes Argument auf der Befehlszeile übergeben wurde, und weisen der Variablen thefile den resultierenden Dateizeiger zu. Der Befehl set wird benutzt, um einer Variablen einen Wert zuzuweisen. Das liegt daran, daß alle Tcl-Befehle mit dem Namen eines Befehls beginnen müssen; wir können die Variable a nicht auf 1 setzen, indem wir so etwas wie

a = 1

eingeben, weil a der Name einer Variablen ist und nicht der Name eines Befehls. Statt dessen schreiben wir:

set a 1

Wenn wir später auf den Wert der Variablen a zugreifen, werden wir $a schreiben.

Das erste Argument zu set ist der Name der Variablen, das zweite Argument ist der Wert. In diesem Fall haben wir:

set thefile [open [lindex $argv 0] r]

Eckige Klammern bezeichnen ein Subskript (untergeordnetes Skript), also Befehle, die in einen anderen Befehl eingebettet werden. Das Subskript wird ausgeführt und durch seinen Rückgabewert ersetzt.

Lassen Sie uns das Subskript

open [lindex $argv 0] r

betrachten. Dieses Skript ruft den Befehl open auf, um die Datei zu öffnen, deren Name als erstes Argument genannt wird. Das zweite Argument, r, zeigt an, daß die Datei zum Lesen geöffnet wird.

Das erste Argument zu open ist das Subskript:

lindex $argv 0

Der Befehl lindex indiziert Listen und Arrays. In diesem Fall suchen wir das nullte Element des Arrays $argv, das die Befehlszeilenargumente des Programms enthält, aber nicht den Befehl selbst. (Hier ist der Gebrauch von argv anders als in C-Programmen.) Das nullte Element von $argv ist also das erste Argument auf der Befehlszeile.

Lassen Sie uns annehmen, daß wir unser Skript lc.tcl genannt haben und es mit

eggplant$ lc.tcl /etc/passwd

aufrufen.

Innerhalb des Befehls

set thefile [open [lindex $argv 0] r]

wird deshalb das eingebettete Subskript

open [lindex $argv 0] r

durch

open "/etc/passwd" r

ersetzt, das wiederum durch den Wert des Dateizeigers ersetzt wird, der auf /etc /passwd verweist. Als Ergebnis nimmt die Variable thefile den Wert des Dateizeigers an.

In Zeile 8 setzen wir den Wert der Variablen count auf 0 - das wird unser Zeilenzähler.

Die Zeilen 10 bis 12 enthalten eine einfache while-Schleife, die so lange Zeilen aus der Datei einliest, bis ein EOF (End Of File; Dateiende) auftaucht.

while {[gets $thefile line] >= 0} { set count [expr $count + 1] }

Wie Sie sehen, hat der Befehl while zwei Argumente: eine Bedingung und einen Anweisungsblock, der ausgeführt wird, solange die Bedingung wahr ist. Die Schleifenbedingung lautet in diesem Fall:

[gets $thefile line] >= 0

Das darin enthaltene Subskript

gets $thefile line

führt den Befehl gets aus. Dieser liest über den Dateizeiger $thefile eine einzelne Zeile und weist sie der Variablen line zu. gets gibt entweder die Anzahl der gelesenen Zeichen zurück oder -1, wenn EOF erreicht wird. Die while-Schleife wird also so lange Zeilen aus der Datei lesen, bis gets einen anderen Wert als null liefert.

Der Hauptteil der while-Schleife besteht aus:

set count [expr $count + 1]

womit der Wert von count erhöht wird. Erinnern Sie sich, daß Tcl-Anweisungen mit dem Namen eines Befehls eingeleitet werden müssen. Arithmetische Ausdrücke werden deshalb mit dem Befehl expr verarbeitet. Das Subskript

expr $count + 1

liefert den Wert der Variablen count plus 1 zurück. Dies ist in Tcl die übliche Methode, Variablen hochzuzählen.

In Zeile 14 schließlich finden wir:

puts "Read $count lines."

Hier wird puts benutzt, um einen String auf der Standardausgabe anzuzeigen.

Rufen wir das Skript einmal auf:

eggplant$ lc.tcl /etc/passwd Read 144 lines.

Tk-Anwendungen schreiben

Schon mit den Tcl-Kenntnissen aus dem vorhergehenden Abschnitt sind Sie in der Lage, auch mit Tk Anwendungen zu schreiben - der Tcl-Erweiterung für das X Window System. Tk ist im wesentlichen eine Sammlung von Tcl-Befehlen, mit denen X-Widgets erzeugt und bearbeitet werden - etwa Schaltflächen (Buttons), Bildlaufleisten (Scrollbars), Menüs usw. Wir werden noch sehen, daß Tk ausgesprochen vielseitig ist und das Erzeugen von graphischen Bedienoberflächen unter X wesentlich vereinfacht.

Ein Widget ist ein Objekt, das Sie manipulieren möchten, zum Beispiel ein Rechteck. Jedes Widget benötigt Speicher und hat eine Reihe von Eigenschaften, aber Tk nimmt Ihnen all das ab. Sie legen einfach nur Ihr Widget an und sagen Tk, wie es aussehen soll.

In diesem Abschnitt werden wir dem Benutzer dabei behilflich sein, ein Oval und ein Rechteck zu zeichnen; beide sind Widgets. Aber wir brauchen auch einen Platz, auf dem wir sie zeichnen können, einen festen Hintergrund, auf dem sich das Oval und das Rechteck frei bewegen können. Deswegen legen wir vor dem Zeichnen einen solchen Raum an. Man nennt das ein Leinwand-Widget (canvas widget). Dieses Leinwand-Widget ist eine Art von Grafik-Widget, das viele Objekttypen wie zum Beispiel Ovale, Linien, Text usw. unterstützt. In dieser Anwendung wollen wir das Leinwand-Widget interaktiv benutzen, um aus Ovalen und Rechtecken Grafiken zu erzeugen. Nach dem Start sieht diese Anwendung etwa so aus, wie es Abbildung 13-2 zeigt.

Screenshot

Abbildung 13-2: Fenster eines Tk-Programms

Lassen Sie uns einen Blick auf den Quellcode unserer Anwendung draw.tcl werfen:

#!/usr/local/bin/wish -f # Globale Variablen; für Objekte und Positionen set oval_count 0 set rect_count 0 set orig_x 0 set orig_y 0

Völlig problemlos - wir initialisieren nur ein paar Variablen, die wir benutzen, um uns die erzeugten ovalen und rechteckigen Objekte mit ihrer Position zu merken.

Der nächste Abschnitt des Quellcodes sieht vielleicht eher abschreckend aus:

# Diese Prozedur aktiviert Ovale proc set_oval {} { # Wir bekommen Zugriff auf diese globalen Variablen global oval_count orig_x orig_y # Wenn Taste-1 gedrückt wird, erzeuge ein Oval bind .c <ButtonPress-1> { set orig_x %x set orig_y %y set oval_count [expr $oval_count + 1] .c create oval %x %y %x %y -tags "oval$oval_count" -fill red } # Wenn wir Taste-1 ziehen, lösche und ersetze das aktuelle Oval bind .c <B1-Motion> { .c delete "oval$oval_count" .c create oval $orig_x $orig_y %x %y -tags "oval$oval_count" -fill red } }

Wir definieren hier die Prozedur set_oval und benutzen dazu den Tcl-Befehl proc. Das erste Argument zu proc ist die Liste der Argumente, die die Prozedur mitbekommt - in diesem Fall gibt es keine Argumente. Das zweite Argument ist die eigentliche Prozedur. Diese Prozedur wird aufgerufen, wenn wir im Menü Object den Punkt Ovals wählen; wir werden dieses Menü später in diesem Abschnitt erzeugen.

set_oval definiert zunächst die Variablen oval_count, orig_x und orig_y als globale Variablen - Tcl würde sonst annehmen, daß wir diese Variablen nur innerhalb dieser Prozedur benutzen.

Der nächste Schritt besteht darin, mit dem Ereignis ButtonPress (Tastendruck) im Leinwand-Widget, in das wir zeichnen werden, eine Aktion zu verbinden (binding). Wir nennen dieses Widget .c. Die Tk-Widgets werden hierarchisch benannt. Das Widget . (ein Punkt) bezeichnet das Hauptfenster der Anwendung. Alle Widgets, die innerhalb dieses Fensters erzeugt werden, erhalten einen Namen, der mit einem Punkt beginnt, etwa .c (für canvas widget; Leinwand-Widget), .mbar (für menu bar; Menüleiste) usw. Natürlich steht es dem Programmierer frei, die Widget-Namen selbst zu wählen, aber sie müssen mit einem Punkt beginnen. Wir werden noch sehen, daß Widgets in anderen Widgets enthalten sein können - zum Beispiel ist ein Menü Bestandteil einer Menüleiste. Ein Widget namens

.mbar.file.menu

könnte das Menü menu bezeichnen, das im Menüpunkt file enthalten ist, der wiederum in der Menüleiste .mbar enthalten ist. Wir werden das weiter unten noch zeigen.

Mit dem Befehl bind wird ein Ereignis mit einem bestimmten Widget verknüpft. Fußnoten 1 Das erste Argument zu bind bezeichnet das Widget, in dem die Verknüpfung erzeugt werden soll; das zweite Argument ist das Ereignis, und das dritte Argument enthält den Code, der ausgeführt werden soll, wenn dieses Ereignis auftritt.

In unserem Beispiel möchten wir immer dann ein Oval zeichnen, wenn der Benutzer innerhalb des Leinwand-Widgets die linke Maustaste drückt. Der Code dieser Verknüpfung setzt die Variablen orig_x und orig_y auf %x bzw. %y. Innerhalb einer Verknüpfung bezeichnen %x und %y die x- und y-Koordinaten des betreffenden Ereignisses. In unserem Beispiel wäre das die Position, an der die Maustaste gedrückt wird. Wir wollen uns für den Fall, daß das Oval in der Größe verändert wird, diese Position merken. Außerdem wird der Wert der Variable oval_count erhöht.

Die Verknüpfung mit ButtonPress-1 führt auch den Befehl

.c create oval %x %y %x %y -tags "oval$oval_count" -fill red

aus. Damit erzeugen wir ein Objekt oval innerhalb des Leinwand-Widgets .c. Die Koordinaten »oben links« und »unten rechts« des Ovals werden in %x und %y übergeben, der Position des Ereignisses ButtonPress. Wir malen das Oval rot aus (fill red).

Die Option -tags zum Befehl create gibt dem gerade erzeugten Objekt oval einen »Namen« (tag). Auf diese Weise können wir dieses spezielle Oval innerhalb des Leinwand-Widgets unter seinem Namen ansprechen. Wir sorgen dafür, daß jedes Oval einen eindeutigen Namen bekommt, indem wir die Variable oval_count benutzen, die mit jedem neu erzeugten Oval hochgezählt wird.

Wenn wir die Maus mit gedrückter linker Taste ziehen, soll die Größe des Ovals entsprechend angepaßt werden. Dazu definieren wir im Leinwand-Widget eine Verknüpfung für das Ereignis B1-Motion. Diese Verknüpfung führt zwei Befehle aus:

.c delete "oval$oval_count" .c create oval $orig_x $orig_y %x %y -tags "oval$oval_count" -fill red

Der Leinwand-Befehl delete löscht das Objekt, das den entsprechenden Namen trägt. Wir zeichnen das Oval dann mit den ursprünglichen Oben-links-Koordinaten neu, aber mit den Unten-rechts-Koordinaten, die durch die Position des Ereignisses B1-Motion bestimmt werden. Mit anderen Worten: Wir ersetzen das ursprüngliche Oval-Objekt durch ein neues Oval mit anderen Koordinaten, die der Position der Maus entsprechen. Der Effekt ist, daß wir die Größe des Ovals verändern, indem wir die Maus mit gedrückter linker Taste über die Leinwand ziehen.

Analog dazu definieren wir die Funktion set_rect, die mit der gerade besprochenen fast identisch ist; allerdings werden auf der Leinwand rectangle-Objekte (Rechtecke) erzeugt.

# Identisch mit set_oval, aber für Rechtecke proc set_rect {} { global rect_count orig_x orig_y bind .c <ButtonPress-1> { set orig_x %x set orig_y %y set rect_count [expr $rect_count + 1] .c create rectangle %x %y %x %y -tags "rect$rect_count" -fill blue } bind .c <B1-Motion> { .c delete "rect$rect_count" .c create rectangle $orig_x $orig_y %x %y -tags "rect$rect_count" \ -fill blue } }

Eine andere Methode, Rechtecke und Ovale zu zeichnen, wäre die Benutzung einer Funktionsklasse namens »draw object« (Objekt zeichnen), die eine Variable wie zum Beispiel $objtype benutzt, um sich den aktuellen Objekttyp zu merken. Man würde die Menüeinstellungen (weiter unten beschrieben) benutzen, um den Objekttyp auszuwählen, indem man den Wert dieser Variable setzt. In der Zeichenfunktion könnten wir einfach einen Leinwand-Befehl wie

.c create $objtype %x %y %x %y -tags "obj$obj_count" -fill blue

benutzen. Dies setzt allerdings voraus, daß alle Objekte in derselben Weise gezeichnet werden (indem an einer Stelle geklickt wird, um dann durch Ziehen der Maus die Größe zu bestimmen). Wenn wir für jedes Objekt eine eigene Funktion benutzen, haben wir die Möglichkeit, die Schnittstelle besser an die einzelnen Objekte anzupassen - falls wir darauf Wert legen.

Jetzt sind wir soweit, daß wir die verschiedenen Widgets definieren können, aus denen unsere Anwendung besteht. Als erstes brauchen wir ein Rahmen-Widget (frame), das wir als Menübalken benutzen werden. Ein Rahmen-Widget ist nichts anderes als ein Container für andere Widgets.

# Rahmen-Widget für Menübalken erzeugen frame .mbar -relief groove -bd 3 pack .mbar -side top -expand yes -fill x

Damit erzeugen wir das Rahmen-Widget .mbar. Die Option -relief bestimmt das Aussehen des Rahmens - wir haben uns für einen Menübalken mit einer »Nut« rund um die Außenkante entschieden. Die Option -bd legt die Breite der Umrahmung fest; in diesem Fall bestimmt sie die Breite der Nut.

Der Befehl pack ordnet die Widgets innerhalb des Hauptfensters oder innerhalb anderer Widgets an. pack ist einer von mehreren »Geometrie-Managern« für Tk. Damit ein Widget innerhalb der Anwendung angezeigt werden kann, muß ein Geometrie-Manager aufgerufen werden, der das Widget auf dem Bildschirm plaziert. pack ist Bestandteil von Tcl/Tk und ist für die meisten Anwendungen flexibel genug. Mit pack haben Sie die Möglichkeit, die Anordnung von Widgets relativ zueinander festzulegen, ohne daß Sie absolute Positionen angeben müssen.

In unserem Beispiel plazieren wir das Widget .mbar am oberen Rand des übergeordneten Widgets, nämlich . (das Hauptfenster der Anwendung). Die Option -fill x zeigt pack an, daß das Widget die ganze Breite des Fensters ausfüllen soll, in dem es enthalten ist; die Option -expand bewirkt, daß das Widget »wächst«, um die gesamte Breite auszufüllen. Falls Sie an den Feinheiten von pack interessiert sind, finden Sie in der Manpage eine sehr detaillierte Beschreibung.

Als nächstes erzeugen wir innerhalb dieses Menübalkens zwei menubutton-Widgets - die Menüs File und Object:

# Zwei Menüpunkte erzeugen menubutton .mbar.file -text "File" -menu .mbar.file.menu menubutton .mbar.obj -text "Object" -menu .mbar.obj.menu pack .mbar.file .mbar.obj -side left

Die beiden Widgets heißen .mbar.file und .mbar.obj. Sie stammen also direkt vom Widget .mbar ab, nicht vom Hauptfenster. Mit pack plazieren wir die beiden Widgets am linken Rand des übergeordneten Menübalkens.

Die Option -menu zum Befehl menubutton bestimmt, welches menu-Widget angezeigt werden soll, wenn dieses Pull-down-Menü ausgewählt wird. Wir werden die Widgets .mbar.file.menu und .mbar.obj.menu weiter unten erzeugen.

# File-Menü mit einzigem Punkt "Quit" erzeugen menu .mbar.file.menu .mbar.file.menu add command -label "Quit" -command { exit }

Wir erzeugen zuerst das Menü File selbst und fügen dann einen einzigen command-Menüpunkt hinzu. Ein command funktioniert wie eine Schaltfläche - wenn man darauf klickt, wird der Code ausgeführt, den die Option -command bestimmt. In unserem Beispiel beendet diese Option das Tk-Skript.

# Object-Menü mit zwei Radiobuttons erzeugen menu .mbar.obj.menu .mbar.obj.menu add radiobutton -label "Ovals" -variable objtype \ -command { set_oval } .mbar.obj.menu add radiobutton -label "Rectangles" -variable objtype \ -command { set_rect }

Mit diesem Codeabschnitt definieren wir das Menü Objects und fügen zwei Objekte vom Typ radiobutton hinzu. Radiobuttons definieren eine Reihe von Optionen, von denen zu einem beliebigen Zeitpunkt nur eine aktiviert werden kann. Wenn beispielsweise Ovals ausgewählt wird, leuchtet es auf, und Rectangles wird dunkel dargestellt.

Um die beiden Radiobuttons miteinander zu »koppeln« (so daß immer nur einer gewählt werden kann), definieren wir mit der Option -variable eine »unechte« Variable (dummy), in der der aktuelle Zustand der Radiobuttons gespeichert wird. Man kann die Option -variable auch zusammen mit -value benutzen und auf diese Weise der Variablen einen Wert zuweisen, wenn dieser Menüpunkt gewählt wird. Wir haben statt dessen beschlossen, eine Prozedur aufzurufen (mit der Option -command), wenn dieser Menüpunkt gewählt wird; damit wird auch -value nicht gebraucht.

Im nächsten Schritt erzeugen wir unser Leinwand-Widget (canvas) und bringen es im Fenster der Anwendung unter:

# Leinwand-Widget (canvas) .c erzeugen canvas .c pack .c -side top

Schließlich aktivieren wir die Option Ovals, indem wir den entsprechenden Menüpunkt auf künstliche Weise auswählen. Das ist genau das, was auch passiert, wenn ein Benutzer diesen Punkt mit der Maus anklickt:

# Ovale aktivieren durch Auswahl des ersten Punktes im Object-Menü .mbar.obj.menu invoke 0

Wir haben soeben mit einigen Dutzend Codezeilen eine komplette und ziemlich komplexe X-Anwendung erstellt. Es ist leicht möglich, dieses Programm auf vielfältige Weise zu erweitern - Sie könnten neue Objekttypen hinzufügen, dem Benutzer die Möglichkeit geben, die erzeugten »Bilder« zu speichern und zu laden usw. Das Leinwand-Widget unterstützt sogar eine Option, um den Inhalt der Leinwand als PostScript-Datei zu formatieren, die Sie anschließend ausdrucken können.

Tcl und Tk in andere Anwendungen einbinden

Wir haben bereits erwähnt, daß Tcl und Tk zusammen mit anderen Sprachen wie C und Perl benutzt werden können. Es ist möglich, auch komplexe Programme als Tcl/Tk-Skript zu schreiben; das Skript wäre wahrscheinlich langsamer als ein kompiliertes Programm, weil Tcl eine interpretierte Sprache ist. Obwohl auch Perl eine Interpreter-Sprache ist, eignet es sich für einige Aufgaben, die in Tcl oder C schwieriger zu realisieren sind.

Die übliche Methode, Tcl und Tk zusammen mit einem C-Programm zu benutzen, ist das Binden der Tcl/Tk-Libraries mit dem C-Code. Tcl und Tk haben sowohl einfache statische Bibliotheken (.a) als auch, auf manchen Systemen, Shared Libraries (.so). Der Tcl-Interpreter besteht aus einer Reihe von Funktionen, die Ihr Programm aufruft.

Die Idee dahinter ist, daß Sie neue Tcl-Befehle in Form von C-Funktionen einführen und daß der Tcl-Interpreter diese Funktionen aufruft, wenn einer dieser Befehle benutzt wird. Damit das funktioniert, müssen Sie Ihr Programm so schreiben, daß der Tcl-Interpreter initialisiert wird und daß es innerhalb einer Tcl-»Hauptschleife« abläuft, die die Tcl-Befehle irgendwo liest (zum Beispiel in einer Datei), um sie dann auszuführen. Das ist in etwa dasselbe, als ob Sie Ihren eigenen tclsh- oder wish-Interpreter mit zusätzlichen, in C verfaßten Tcl/Tk-Befehlen schreiben.

Das ist vielleicht nicht für alle Anwendungen die beste Vorgehensweise. Zunächst einmal werden Sie den Aufbau mancher Programme ändern müssen; anschließend steht die Anwendung unter der Kontrolle des Tcl-Interpreters - und nicht umgekehrt. Außerdem gilt: Solange Sie nicht die Shared Libraries von Tcl und Tk benutzen, kann die ausführbare Datei durch den komplett eingebundenen Tcl/Tk-Interpreter ziemlich groß werden - weit mehr als ein Megabyte. Es kann auch sein, daß Ihre Anwendung von einem Tcl-Skript gesteuert wird; das bedeutet, daß die Anwendung alleine nicht lauffähig ist - Sie brauchen zusätzlich das Skript.

Eine andere Lösung wäre eine in C oder Perl geschriebene Anwendung, die den Interpreter wish als eigenen Prozeß ausführt und durch Pipes mit ihm kommuniziert. In diesem Fall würden Sie zwei Pipes brauchen; in der einen schickt das C-Programm Befehle an wish, in der anderen liest das C-Programm die Antworten von wish. Das läßt sich auch mit einer Pipe bewerkstelligen, aber die Synchronisation wird schwieriger. Ein Beispiel: Die Antworten von wish können asynchron eintreffen - durch solche Ereignisse wie eine gedrückte Maustaste hervorgerufen -, dadurch wird die Benutzung einer einzelnen Pipe ziemlich schwierig. Fußnoten 2

Die direkteste Methode ist in diesem Fall eine C-Funktion, die folgendes erledigt (in Pseudocode):

Erzeuge zwei Pipes durch zweifachen Aufruf von pipe(); Starte mit fork() einen Kindprozeß; Im Kindprozeß: Schließe Lese-Kanal der einen Pipe und Schreib-Kanal der anderen; Kopiere stdin und stdout mit dup2() in die entsprechenden Pipes; Starte wish mit execlp(); Im Elternprozeß: Schließe den Lese-Kanal der Write-Pipe und den Schreib-Kanal der Read-Pipe; Öffne mit fdopen() beide Pipes, um einen FILE-Deskriptor für fprintf() und fscanf() zu bekommen;

Natürlich müssen Sie einiges von der Systemprogrammierung unter Unix verstehen, um dieses Beispiel nutzen zu können, aber für die Wagemutigen haben wir es hier aufgeschrieben.

Der Elternprozeß (Ihr C-Programm) kann anschließend Tcl/Tk-Befehle in den Schreib-Kanal schreiben und die Antworten von wish aus dem Lese-Kanal lesen. Mit der Funktion select können Sie den Lese-Kanal auf eingehende Daten abfragen (pollen), falls Ihre Anwendung weiterarbeiten soll, während sie auf Daten vom wish-Interpreter wartet.

Auf diese Weise benutzen wir wish als »Server« für X-Window-System-Routinen. Ihr Programm schickt dann die Befehle zur Erzeugung von Widgets in die Write-Pipe. wish könnte eine Meldung an die Standardausgabe schicken, wenn von der Anwendung eine Reaktion erfolgen soll. So könnte man beispielsweise ein Button-Widget erzeugen, das den String OK-Button gedrückt ausgibt, wenn der Benutzer darauf klickt. Ihr Programm würde diese Meldung aus der Read-Pipe lesen und darauf reagieren. wish könnte andere Teile der Anwendung kontrollieren, ohne daß Ihr Programm davon weiß. Die rechenintensiven, zeitkritischen Teile der Anwendung würde man in C schreiben, und wish würde sich um die Benutzerschnittstelle kümmern.

Wir hoffen, daß dieser Überblick ausreicht, um Ihnen eine Vorstellung davon zu geben, wie ein C- oder Perl-Programm auf diese Weise wish benutzen kann. Sie sollten auch ein Buch über die Systemprogrammierung unter Unix lesen, in dem die Kommunikation zwischen Prozessen mittels Pipes besprochen wird; zum Beispiel Advanced Programming in The Unix Environment von Richard Stevens oder UNIX System Programming von David Curry.

 Fußnoten 1
Ein Ereignis ist einfach eine Nachricht, die der X-Server als Antwort auf eine Benutzeraktion erzeugt. Ein Beispiel: Wenn der Benutzer in einem bestimmten Fenster die Maustaste 1 (normalerweise die linke Maustaste) drückt, wird das Ereignis ButtonPress-1 an dieses Fenster geschickt.
 Fußnoten 2
Erinnern Sie sich, daß eine Pipe ein einfacher Datenstrom in eine Richtung ist, den ein Prozeß an einen anderen schickt. Die Shell gestattet die Benutzung einfacher Pipes zwischen Befehlen, wie etwa in: cat foo.txt.gz | gunzip -c | more.


vorheriges Kapitel Inhaltsverzeichnis Stichwortverzeichnis nächstes Kapitel


Weitere Informationen zum Linux - Wegweiser zur Installation & Konfiguration

Weitere Online-Bücher & Probekapitel finden Sie in unserem Online Book Center


O'Reilly Home | O'Reilly-Partnerbuchhandlungen | Bestellinformationen | Kontaktieren Sie uns
International | Über O'Reilly | Tochterfirmen

© 2000, O'Reilly Verlag