Eine Einführung in Tk

ArticleCategory:

Software Development

AuthorImage:

[Photo des Autors]

TranslationInfo:

Original in fr Charles Vidal

fr to en Charles Vidal

en to de Michael Weinrich

AboutTheAuthor:

Charles Vidal ist Präsident einer gastronomischen LUG in Paris. Er mag die GNU- und Open-Source-Philosophie, weil sie es beide den Leuten ermöglichen, ihr Wissen miteinander zu teilen. Charles würde gerne Zeit finden, um Saxophon zu spielen.

Abstract:

Dieser Artikel macht dich mit den Merkmalen der grafischen Werkzeuge für Tcl (Tool command language) bekannt: Tk (Toolkit). Wir werden sehen, wie leicht es ist, mit nur ein paar Zeilen Programmcode ein GUI (Graphical User Interface - grafische Benutzeroberfläche) zu erstellen.

ArticleIllustration:

Tcl Tk logo

ArticleBody:

Tk - der grafische Baukasten für Tcl: Die WISH

Tk wurde geschrieben, um der Sprache Tcl eine grafische Oberfläche zu geben. Wir nennen beides zusammen Tcl/Tk (sprich: Tickel/Tieh-Key).

Tcl/Tk ist ein plattformübergreifendes grafisches Toolkit mit der Optik des darunterliegenden Betriebssystems. Es arbeitet perfekt mit der Tcl-Programmiersprache, die ebenfalls plattformübergreifend ist, zusammen. Der große Vorteil von Tcl/Tk ist seine Einfachheit. Beides zusammen ermöglicht es dir, leicht portierbare Anwendungen zu entwickeln. So wie es die tclsh für tcl gibt, gibt es die wish (windowing shell) für Tk.

Hello world !

Als Einführung werde ich dir nun zeigen, wie du ein klassisches Beispiel programmierst. Das Programm zeigt die Möglichkeiten und die Einfachheit einer Tcl/Tk-Anwendung.
pack [ label .l -text "Bonjour monde" ]

Nun vergleich' dieses mal mit einem in C geschriebenen gtk-Programm:
#include <gtk/gtk.h>
Int main( int   argc,
          char *argv[] )
{
   /* GtkWidget ist der Speichertyp für Widgets */
    GtkWidget *window;
    GtkWidget *button;

    gtk_init(&&argc, &&argv);
   /* erzeuge ein neues Fenster */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    button = gtk_button_new_with_label ("Bonjour Monde");
    gtk_container_add (GTK_CONTAINER (window), button);
    gtk_widget_show (button);

    /* und das Fenster */
    gtk_widget_show (window);
    gtk_main ();

    return(0);
}
... oder vergleiche es mit Motif:
/* COMPILE  cc xmhello.c -L/usr/X11R6/lib  -lXm -lXt -lX11 -lXmu -o xmhello */

#include <Xm/PushB.h>
#include <Xm/Form.h>

/* Widget */

        Widget      main_widget, pushb;

main(int argc, char **argv)
{
        Widget      form;
        Arg         args[1];
        int         ac = 0;
        XmString    label_str;

        main_widget = XtInitialize(argv[0], "test", NULL, 0, &&argc, argv);
        label_str = XmStringCreate("Bonjour Monde", XmSTRING_DEFAULT_CHARSET);
        XtSetArg(args[ac], XmNlabelString, label_str);
        ac++;
        pushb = XmCreatePushButton(main_widget, "hello", args, ac);
        XtManageChild(pushb);
        XtRealizeWidget(main_widget);
        XtMainLoop();
}
Aber man darf nicht den Quellcode einer Skriptspache mit dem einer kompilierten Sprache vergleichen. Man muss ebenso an andere Aspekte, zum Beispiel den Speicherbedarf der Anwendung, denken. Also lassen wir es dabei zu sagen, dass ein klassisches "Hallo Welt" (oder "Hello world" oder "Bonjour Monde") Progamm recht leicht in Tk zu erstellen ist. Wir sollten weniger die Erscheinungsweise des Toolkits als das Konzept und die Ideen dahinter beachten. Es gibt einige Erweiterungen, um das Aussehen eines Tk Widgets zu ändern, zum Beispiel Qtk und TkStep.
Es ist beachtenswert, dass das Tk Toolkit mit einer Menge von Programmiersprachen benutzt werden kann. Und genau wie viele anderen Scriptics-Sprachen auch, ist Tcl/Tk plattformübergreifend.

Erzeugung grafischer Tk Objekte

Dieses Toolkit versorgt dich mit einer ganzen Anzahl an grafischen Objekten (genannt Widgets), um ein GUI zu bauen.

Um dir eine Demonstration aller verfügbaren Widgets anzusehen, gehe in das Verzeichnis /usr/local/lib/tk8.0/demos (oder wo immer Tcl/Tk bei dir installiert ist) und starte widget.

Die Anzahl dieser grafischen Objekte ist begrenzt (es gibt kein Baum-Widget, Comboboxen ...). Aber es gibt Leute und Firmen, die viele wertvolle Ergänzungen gemacht haben, die berühmtesten sind: Tix , die widgets Incr Tcl und seit kurzem die großartige BWidget. Alle diese Erweiterungen gibt's auf der Scriptics-Webseite.

Die Baumstruktur eines grafischen Objekts in der GUI

GUIs werden als Baum von Fenstern (Rechtecken), mit ihren eigenen Eigenschaften gebaut.
Die Knoten dieser Baumstruktur sind Container für grafische Objekte wie zum Beispiel eine Schaltfläche. Eine Baumstruktur wird in der Tcl/Tk-Sprache wie folgt beschrieben: Wenn das wish-Programm gestartet wird, erscheint ein Fenster. Dieses ist das Wurzelfenster (root window) und heißt . (Punkt).

Das Erstellen grafischer Objekte: "Hallo Welt"

Für die Erstellung eines grafischen Objekts sieht der tcl-Befehl so aus:
name_des_objects .(name_des_containers.)*name_des_grafischen_objects [eigenschaft wert]
Beispiel: label .meinlabel -text "Hallo Welt"

Wie du siehst, wird die Aktion ausgeführt, indem zunächst der Name des zu erzeugenden grafischen Objekts angegeben wird, in diesem Fall label, dann folgt der Name des Containers, wobei . das Wurzel- oder Hauptfenster ist; zum Schluss bestimmst du die Eigenschaften (-text " Hallo Welt "). Merke, dass diese Argumente auch nach der Erzeugung des grafischen Objekts ausgelesen und geändert werden können.


Nun enthält das Label den Text "Bonjour Monde in french :)"
Du kannst ihn dir mit dem Befehl puts [label .meinlabel -text "hello world"] anzeigen lassen.
Das Label kann auch das Ergebnis eines Befehls anzeigen:
.meinlabel configure -text "Dies ist das aktuelle Datum [exec date ]"
Um die Optionen, die du an das Widget übergeben kannst, anzusehen, tippe im interaktiven Modus der Wish ".mylabel configure" ein.
Diese Eingabe erzeugt eine Fehlermeldung und zeigt dir die Optionen, die du benutzen kannst, an.

Layouts: Die Manager der Widget-Positionen.

Du magst sagen: Es passiert nichts, es wird nichts angezeigt!

Tatsächlich hast du gerade ein Objekt vom Typ Label erzeugt, aber du hast das Programm noch nicht darum gebeten, dieses Objekt auch anzuzeigen. Die Anzeige dieses Objekts erfordert nämlich Informationen, die du noch nicht spezifiziert hast; du hast noch nicht gesagt in welchem Layout (das ist ein Manager, der dir hilft, Fenster zu positionieren) du dieses Objekt anzeigen lassen willst.

Es gibt verschiedene Layouts:
  1. packer (packer = Der Packer)
  2. placer (placer = Der Plazierer)
  3. grid (grid = Das Gitternetz)
Lass uns den einfachsten wählen: den Packer. Er fügt innerhalb von sich selbst ein Objekt hinzu und versieht es mit den Spezifikationen des Benutzers. Mehr dazu später.

Ein voll funktionsfähiges Hallo Welt
label .meinlabel -text "Hallo Welt"
pack .meinlabel

Oder in einer Zeile
pack [label .meinlabel -text "Hallo Welt"]


Der Button (die Schaltfläche)
Lasst uns mal den Fall einer Schaltfläche ansehen:

button .meinbutton -text "Hallo Welt" -command "exit"
pack .meinbutton

Wir stellen fest, dass der Button eine "command"-Eigenschaft als Argument hat (und zwar nur eine). Diese ist der tcl-Befehl, der ausgeführt wird, wenn der Anwender auf den Button klickt.In diesem Fall ist es der Befehl exit, der das Programm veranlasst, sich selbst zu beenden.

Grafische Objekte mit Variablen verbinden

Die Stärke von Tcl/Tk ist die Leichtigkeit, mit der grafische Objekte mit einer Variablen und ihrem dazugehörigen Status verbunden werden können. Wir brauchen nur eine Funktion aufzurufen, um den Status eines grafischen Objekts zu erfahren. Genauso wird das grafische Objekt direkt beeinflusst, wenn die Variable geändert wird. Wie du siehst, macht dies das Programmieren direkt und ohne Umwege möglich - du brauchst viel weniger Programmcode zu schreiben.

Beispiel

Die Checkbuttons und Radiobuttons.

  1. Ein Checkbutton ist eine Schaltfläche, die einen Status widerspiegelt.
    Diese Schaltfläche zeigt einen Bool'schen Wert (gesetzt oder nicht gesetzt). Wir können eine Variable damit vebinden, die den Wert 0 (als Standardwert) oder 1 annimmt, je nach dem, ob der Checkbutton aktiviert ist oder nicht. Aber wir können ebenso den Wert, den die Variable übernimmt über den Status des Checkbuttons definieren.
    Beispiel:

    checkbutton $w.b1 -text "Wischer in Ordnung" -variable wischer -relief flat -onvalue "Ok" -offvalue "Wischer nicht in Ordnung"

  2. Ein Radiobutton ist eine Schaltfläche, die den Status einer Anzahl an Schaltflächen widerspiegelt.
    Die Radiobuttons sind oft gruppiert, so dass die Auswahl eines Buttons aus einer Menge möglich ist. Um sie zu gruppieren, musst du ihnen denselben Variablennamen geben und den Wert, den diese Variable annimmt, wenn der Radiobutton aktiviert ist.
    Beispiel:



    radiobutton .b1 -text "Erster" -variable size -value 1
    radiobutton .b2 -text "Zweiter" -variable size -value 2

Eingabefeld: Entry
Dieses Widget ist ein Textfeld, das vom Anwender ausgefüllt wird. Du kannst dem Eingabefeld mit -textvariable eine Variable zuordnen.
Beispiel:


entry .e -textvariable toto -width 40

Layout-Manager

Für gewöhnlich werden grafische Objekte innerhalb eines Rahmens angeordnet. Aber achte darauf, innerhalb eines Rahmens nur einen Layout-Manager zu verwenden! Es gibt drei Layout-Manager:
  1. Den packer: Er positioniert ein grafisches Objekt anhand der Ausrichtung:
    1. top (oben)
    2. bottom (unten)
    3. right (rechts)
    4. left (links)

    Beispiel:
    pack [ button .b1 -text top ] -side top
    pack [ button .b2 -text bottom ] -side bottom
    pack [ button .b3 -text right ] -side right
    pack [ button .b4 -text right ] -side left
    

    Wir können auch den Umfang im inneren des Widgets konfigurieren: option -expand (yes|no) -fill ( x|y| both)

  2. Der placer: Wir können ein grafisches Objekt anhand der x y Position plazieren.

    Beispiel: place [ label .l -text "With Place"] -x 100 -y 100 .l configure -bg red

  3. Der grid plaziert Widgets auf einem virtuellen Gitternetz, das aus Zeilen und Spalten besteht. Grid ist der ideale Layout Manager für Listboxen und Eingabeboxen, die sich über mehrere Zeilen erstrecken. Die Syntax ist: Erst das Widget erzeugen, zum Beispiel .e_name, dann das Widget anzeigen mit:

    label .mainlbl2 -text "Label 2" -bd 2 -relief sunken
    grid .mainlbl2 -row 0 -column 1 -sticky news
    label .mainlbl1 -text "Label 1" -bd 2 -relief raised
    grid .mainlbl1 -row 0 -column 0 -sticky news
    label .mainlbl3 -text "Label 3" -bd 2 -relief solid
    grid .mainlbl3 -row 1 -column 0
    label .mainlbl4 -text "Label 4" -bd 2 -relief groove
    grid .mainlbl4 -row 1 -column 1

Die Liste der erzeugten Widgets

Mit dem Befehl winfo bekommen wir eine Liste der erzeugten grafischen Objekte.

 winfo exists name_object
Aber wir können auch eine Liste aller erzeugten Widgets bekommen, und zwar mit dem Befehl:
winfo children .
Diese Funktionalität von Tcl/Tk gibt es in den anderen kompilierten grafischen Toolkits (wie zum Beispiel gtk, awt, motif) nicht.

Ereignisse

Tk bearbeitet ein Ereignis und führt einen Befehl aus, wenn ihm ein -command übergeben wird. Aber es gibt Fälle, in denen man sich wünscht, die Ereignisse präziser zu bearbeiten oder mehrere Ereignisse für ein Widget vorzusehen. Das canvas ist so ein Widget.



bind name_of_widget name_of_event tcl_code.

Ein kleines Beispiel

Wir werden ein GUI schreiben, das den Inhalt einer tar(gz)-Datei anzeigt.
Die Benutzerschnittstelle besteht aus einer Liste und zwei Rollbalken zum Scrollen.
Hier ist die Beschreibung der tcl-Skript-Struktur:
  1. Wir bauen das GUI (mit proc makegui).
  2. Wir fügen alle Pfade des Widgets in eine Hash-Tabelle tabgui.
  3. Wir lesen die Anzahl der Argumente ein; wenn sie null ist, beendet sich die Anwendung.
  4. Wir öffnen das Handle einer Umleitung mit dem Programm tar -tzvf name_file_tar.
  5. Wir fügen jede gelesene Zeile in eine Liste ein.
Wir fangen das Tastatur-Ereignis "Strg C" ab, um die Applikation zu beenden:
bind all <Control-c> {destroy .}

Dies ist nur ein kleines Beispiel, wir können es noch verbessern. Wir können testen, ob die Datei komprimiert ist oder nicht, indem wir den Befehl string first verwenden. Wenn der Benutzer kein Argument übergibt, dann öffnet sich ein Auswahlfenster, in dem die tar-Datei selektiert werden kann. Wir können es noch ausgeklügelter machen, indem wir ein Popmenü erzeugen, das es erlaubt mit den Dateien innerhalb des Archivs zu arbeiten. Und schließlich können wir eine Befehlszeile am oberen Rand erzeugen, die es dem Benutzer ermöglicht, eine neue Datei zu öffnen, ein neues Archiv zu erzeugen undsoweiter.
#!/bin/sh
# die nächste Zeile startet wish \
exec wish8.0 "$0" "$@"

global tabgui

proc makegui { } {
global tabgui
#
# Erzeugen der Rollbalken für die Liste
# dann wird der horizontale Rollbalken rechtsplaziert und füllt
# das Fenster in horizontaler Richtung
# und der vertikale Rollbalken wird unten plaziert
# und füllt das Fenster in vertikaler Richtung
#
set tabgui(scrollv) [scrollbar .scrollv -command ".list yview"]
pack $tabgui(scrollv) -side right -fill y
set tabgui(scrollh) [scrollbar .scrollh -command ".list xview" -orient horizontal ]
pack $tabgui(scrollh) -side bottom -fill x
#
# Erzeugen der Liste und Verknüpfung von dieser mit
# dem Rollbalken
#
set tabgui(list) [listbox .list  \
                -yscroll "$tabgui(scrollv) set" \
                -xscroll "$tabgui(scrollh) set" \
                -relief sunken -width 20 -height 20 \
        -setgrid yes ]
pack $tabgui(list) -side left -fill both -expand yes
wm minsize . 1 1
}

#
# Erzeugen der GUI
#
makegui

if $argc>0 {set tarfile [lindex $argv 0]} else {puts stderr "tar Datei fehlt" ; exit}
set command "tar -tzvf $tarfile"
set tube [ open |$command r]
 while {![eof $tube]} {
    set tarresult  [ gets $tube ]
    $tabgui(list)  insert end $tarresult
}

bind all <Control-c> {destroy .}

Quellen