BWidgets ergänzen die normalen Tk-Widgets um häufig benötigte GUI-Elemente wie Tree und Notebook. Anders als die meisten Alternativen sind sie in reinem Tcl/Tk geschrieben und eignen sich daher bestens für plattformübergreifende Anwendungen.
Das zu Tcl gehörende GUI-Toolkit Tk enthält alle Standardwidgets wie Rahmen, Buttons oder Listboxen. Bei vielen Anwendungen kommt aber der Wunsch nach mehr auf. Zusätzliche Widgets wie Tree oder Combobox sind in diversen Erweiterung wie BLT[2] zu finden, die aber als kompilierte Bibliotheken plattformabhängig sind. BWidgets haben diesen Nachteil nicht, sie sind in Tcl/Tk geschrieben und können daher über Plattformgrenzen hinweg zusammen mit der Anwendung verteilt werden.
Die BWidgets basieren auf einer Entwicklung der Firma Unifix und sind inzwischen unter einer BSD-Lizenz bei Sourceforge verfügbar[1]. Nach dem Auspacken des Archivs muss die Erweiterung dem Interpreter bekannt gemacht werden. Das geschieht durch den Bibliothekspfad »auto_path«:
lappend auto_path [file join //usr local lib BWidget-1.4.1]
Das Archiv enthält neben der eigentlichen Erweiterung auch eine umfangreiche Dokumentation im Unterverzeichnis »BWMan«. Die Schnittstellen ähneln denen der normalen Tk-Widgets, allerdings sind die Widget-Kommandos großgeschrieben.
Widgets gruppieren und anordnen
Einige der BWidgets dienen zur Gliederung von grafischen Oberflächen:
- Notebook (auch Karteireiter genannt)
- Rahmen mit Beschriftung
- Vertikal oder horizontal geteilter Bereich mit veränderbarer Aufteilung
- Bereich zum Scrollen
Das Notebook-Widget (siehe Abbildungen 1a und 1b, häufig auch Tabbed Widget genannt) gibt dem Benutzer die Möglichkeit, zwischen den beschrifteten Karteireitern (Tabs) zu blättern. Das soll die Bedienung intuitiver gestalten. Folgendes Kommando fügt einen neuen Reiter ein:
set frame [$notebook insert end name -text "Beispiel"]
Der neue Frame kann weitere Widgets aufnehmen. Die Reiter lassen sich nicht nur mit der Maus auswählen, auch das Programm kann festlegen, welche Karteikarte oben liegen soll:
$notebook raise name
Reiter lassen sich auch abfragen, sortieren oder löschen. An ihre Grenzen stoßen Notebooks, wenn sie viele Einträge aufnehmen sollen. Je nach Titel der Karten genügt schon bei vier oder fünf Stück der Platz nicht mehr, um alle Reiter gleichzeitig darzustellen. Das Widget blendet dann Knöpfe ein, um in den Reitern zu blättern (Abbildung 2). Darunter leidet die Bedienung erheblich. Als Ausweg bietet sich ein Pages Manager mit einem Menü oder einer Toolbar zum Umschalten an.
Nicht immer muss eine Oberfläche so stark gegliedert sein wie mit einem Notebook. Dezenter ist ein Title Frame, ein Rahmen mit Überschrift wie in Abbildung 1a zu sehen. Tk 8.4 wird jedoch nativ bereits den mächtigeren Label Frame enthalten, er lässt sich einfacher benutzen und bietet mehr Kontrolle über das Aussehen. Man kann sogar beliebige Widgets als Beschriftung einsetzen.

Abbildung 1a: Im Notebook- Widget schaltet der Benutzer zwischen den Karteikarten um.

Abbildung 1b: Die einzelnen Karten nehmen beliebige Widgets auf, auch ein Paned Window.

Abbildung 2: Bei zu vielen Karten stellt das Notebook zwei Pfeile zum Durchscrollen bereit.
Teile und herrsche
Ein gern benutztes GUI-Element sind horizontal oder vertikal geteilte Flächen. Der Benutzer kann dann mit einem Knopf (Sash) bestimmen, welchem Bereich er mehr der knappen Bildschirmoberfläche einräumen will. Hierfür dient das Paned Window. Die Option »-side« bestimmt die Teilungsrichtung und Position des Knopfes, das Teilungsverhältnis wird wie beim »grid«-Kommando mit »-weight« festgelegt. Einem Paned Window lässt sich mit »set frame [$panedWindow add]« ein Bereich hinzufügen, in den neuen Frame kann man die eigenen Widgets packen.
Automatische Scrollbars
Beim Verschieben des Paned Window wird meist ein Bereich zu klein, um alle enthaltenen Widgets aufzunehmen. Eine elegante Lösung ist das Scrolled Window, das eingebettete Fenster (»$scrolledWindow setwidget $child«) automatisch mit Scrollbars verknüpft und diese nur bei Bedarf angezeigt. Von Haus aus arbeiten allerdings nur wenige Widgets wie »text« oder »canvas« mit den Scrollbars zusammen. Mit dem Scrollable Frame enthalten die BWidgets einen scrollfähigen Container, der beliebige Widgets aufnehmen kann.
Aus historischen Gründen enthält das BWidgets-Paket einige Dubletten zum normale Tk. Sie unterscheiden sich in ihrer Funktion aber kaum noch von den nativen Tk-Widgets und sind deshalb nicht mehr so interessant.
Listing 1: Basis-Widgets
01 #!/usr/local/bin/wish8.3
02 # Basis-Widgets aus den BWidgets
03
04 lappend auto_path [file join [pwd] BWidget-1.4.1]
05 package require BWidget 1.4.1
06
07 set notebook [NoteBook .nb]
08 pack $notebook -expand true -fill both
09
10 # Erstes Notebook mit TitleFrame
11 set frame [$notebook insert end tf -text "TitleFrame"]
12 TitleFrame $frame.titel -text "Rahmen mit Titel"
13 pack $frame.titel -expand true -fill both
14
15 set f [$frame.titel getframe]
16 label $f.label -text "ein Eintrag"
17 pack $f.label
18
19 # Zweites Notebook mit PanedWindow,
20 # ScrolledWindow und ScrolledFrame
21 set frame [$notebook insert end sp -text "PanedWindow" ]
22 set panedWindow [PanedWindow $frame.pw -side top ]
23 pack $frame.pw -expand true -fill both
24
25 set pane [$panedWindow add -weight 1]
26 set sw [ScrolledWindow $pane.sw]
27 set text [text $sw.text -wrap none -width 50 -heigh 50 -bg white]
28 $text insert 0.0 "linker Text in ScrolledWindow"
29 $sw setwidget $text
30 pack $sw -fill both -expand yes
31
32 set pane [$panedWindow add -weight 9]
33 set sw [ScrolledWindow $pane.sw]
34
35 pack $sw -fill both -expand yes
36 set sf [ScrollableFrame $sw.sf]
37 $sw setwidget $sf
38
39 set f [$sf getframe]
40 label $f.label -text "Ein Label im ScrollableFrame"
41 pack $f.label
42
43 # foreach t {a b c d e f g} {
44 # $notebook insert end $t -text "Tab $t"
45 # }
46
47 $notebook raise [$notebook page 0]
48
49 wm title . "Anordnung"
50 wm geometry . 200x200
Nützliche Kleinigkeiten
Wichtig sind die zusätzlichen Eingabemöglichkeiten, die im normalen Tk fehlen: Combobox und Spinbox. Tk 8.4 wird eine Spinbox enthalten sein, so dass vor allem die Combobox als wichtiges Eingabe-Widget interessant bleibt (siehe Listing 2). Sie bietet dem Benutzer die Auswahl aus einer Liste, die das Programm mit der Option »-values« vorgibt. Diese Auswahlliste kann einmalig festgelegt oder – wie im Beispiel in Zeile 8 – vor der Anzeige mit einem Callback aktualisiert werden.
Wählt der Benutzer einen neuen Wert, ruft das Widget eine weitere Callback-Funktion auf. Die Funktion ».combo getvalue« (Zeile 12) gibt dann die gewählte Position zurück, das Element lässt sich mit »lindex« ermitteln. Wie das Beispielprogramm in Zeile 25 zeigt, ist das ausgewählte Element auch von Tcl aus steuerbar. Neben der numerischen Position ».combo setvalue @ Position« sind auch »last«, »first«, »next« oder »previous« möglich. Ist die Option »-editable true« gesetzt, dann kann der Benutzer den Wert auch direkt in das Eingabefeld tippen. Allerdings ist hier kein Callback vorgesehen.
Ein Sonderfall ist der Drag & Drop-Mechanismus. Das aktuelle Tk enthält noch keine eigene Implementierung, allerdings bietet sich eher die Erweiterung »tkdnd«[3] von George Petasis an. Sie unterstützt das native Drag & Drop von X11 sowie Windows und kann damit auch mit Gnome- oder KDE-Anwendungen kommunizieren.
Listing 2: Combobox
01 #!exec /usr/local/bin/wish8.3
02 # Beispiele für BWidgets-Combobox
03
04 lappend auto_path [file join [pwd] BWidget-1.4.1]
05 package require BWidget 1.4.1
06
07 proc auswahlAktualisieren {} {
08 .combo configure -values [glob -nocomplain *]
09 }
10
11 proc wertÄndern {} {
12 set index [.combo getvalue]
13 set auswahl [.combo cget -values]
14 .wert configure -text [lindex $auswahl $index ]
15 }
16
17 ComboBox .combo -postcommand auswahlAktualisieren
18 -modifycmd wertÄndern -editable false
19 -entrybg white
20
21 label .wert
22 grid .wert .combo -sticky ew -padx 10
23
24 auswahlAktualisieren
25 .combo setvalue @0
26 wertÄndern
27
28 wm title . "ComboBox"
In der Krone
Neben den Gliederungsmöglichkeiten ist die Baumdarstellung ein wichtiges Feature der BWidgets. Ein einfacher Filebrowser in Listing 3 dient als Beispiel (siehe auch Abbildung 3). Zeile 10 erzeugt das Widget und legt neben den Farben auch eine Callback-Funktion fest, die beim Öffnen und Schließen von Knoten zum Zuge kommt. Bäume können typischerweise sehr groß werden, deshalb kommt in Zeile 7 wieder das Scrolled Window zum Einsatz.
Als Nächstes wird der erste Knoten in den Baum eingefügt. Das geschieht mit folgendem Kommando:
$tree insert Index Eltern Name
Der Index nimmt die gleichen Werte auf wie das »lindex«-Kommando, meist ist er »end«. Das Eltern-Feld bezeichnet den Knoten, unter dem der neue eingehängt werden soll, für den ersten Knoten ist es »root«. Der Knotenname ist ein beliebiger String, der jedoch innerhalb des Tree-Widgets nur einmal vorkommen darf. Meist ergibt sich der Knotenname aus den dargestellten Daten, es kann aber auch einfach ein durchlaufender Zähler sein. Knoten haben noch ein paar zusätzliche Optionen, neben dem angezeigten Text und Bild ist dies vor allem »-data Wert«, um zusätzliche Daten im Knoten zu speichern.
Listing 3: Baum-Widget
#01 #!/usr/local/bin/wish8.3
02 # Das Tree-Widget aus den BWidgets
03
04 lappend auto_path [file join [pwd] BWidget-1.4.1]
05 package require BWidget 1.4.1
06
07 set sw [ScrolledWindow .sw -relief sunken -borderwidth 2]
08 grid $sw -sticky nesw
09
10 set tree [Tree $sw.tree -background white
11 -selectbackground LightSkyBlue
12 -opencmd knotenÖffnet
13 -closecmd knotenSchließt
14 ]
15 $sw setwidget $tree
16 $tree bindText <Button-1> selektiert
17 $tree bindImage <Button-1> selektiert
18
19 label .label -textvariable fileInfo -anchor w
20 grid .label -sticky ew
21
22 grid columnconfigure . 0 -weight 10
23 grid rowconfigure . 0 -weight 10
24 grid rowconfigure . 1 -weight 1
25
26 # Callbacks
27 proc knotenÖffnet {node} {
28 # Icon austauschen
29 $::tree itemconfigure $node -image [Bitmap::get openfold]
30 # alte Kinder-Knoten Löschen
31 $::tree delete [$::tree nodes $node]
32
33 # Verzeichnis aus Knoten
34 set path [$::tree itemcget $node -data]
35
36 # Knoten für alle Kinder anlegen
37 foreach child [glob -nocomplain [file join $path *]] {
38 if {[file isfile $child]} {
39 set icon [Bitmap::get file]
40 set dc never
41 } else {
42 set icon [Bitmap::get folder]
43 set dc allways
44 }
45 $::tree insert end $node $child -data $child
46 -text [file tail $child]
47 -image $icon -drawcross $dc
48 }
49 }
50
51 proc knotenSchließt {node} {
52 $::tree itemconfigure $node
53 -image [Bitmap::get folder]
54 }
55
56 proc selektiert {node} {
57 $::tree selection set $node
58 set path [$::tree itemcget $node -data]
59 set ::fileInfo "[file tail $path], [file size $path] bytes"
60 }
61
62 # den erste Knoten einfügen
63 $tree insert end root pwd -data [pwd] -text [pwd]
64 -image [Bitmap::get folder]
65
66 # ... und öffnen
67 $tree opentree pwd false
68
69 wm title . "FileBrowser"

Abbildung 3: Dieser File-Browser ist mit dem Tree von BWidgets implementiert. Beim Öffnen eines Verzeichnisses stellt er dessen aktuellen Inhalt dar.
Immer der aktuelle Inhalt
Die Callback-Funktion »knotenÖffnet« (Zeile 27) löscht beim Öffnen eines Knotens erst alle Kinder und erzeugt dann neue – damit ist sichergestellt, dass der Filebrowser die aktuellen Verzeichnisinhalte darstellt. Um einen Unterbaum per Maus öffnen zu können, muss der Anwender auf den kleinen Kasten neben dem Knoten klicken. Dieser Kasten wird entweder automatisch gezeichnet (wie beim ersten Knoten) oder explizit mit der Option »-drawcross allways|never |auto« angefordert.
Bei den meisten Anwendungen muss der Benutzer Knoten auswählen (selektieren). Mit »$tree bindText Event Callback« oder mit »bindImage« kann das Programm eine Callback-Funktion an Ereignisse binden, die auf dem Text oder dem Icon des Knotens stattfinden.
Die Callback-Funktion ist dafür zuständig, das gewählte Element hervorzuheben. Dazu dient das Kommando »$tree selection Unterkommando« mit den Unterkommandos »set«, »get«, »clear«, »add« und »remove«. Die Prozedur »selektiert« (Zeile 56) setzt die Selektion auf einen bestimmten Knoten und zeigt Name und Größe der Datei mit Hilfe der Variablen »::fileInfo« an (die Statuszeile stellt diese Variable dar, siehe Zeile 19).
Abbildung 4: Das Icon-Paket von Adrian Davis enthält nicht nur eine Vielzahl von Icons, sondern auch einen Browser, um den Vorrat bequem zu durchsuchen.
Gute Gründe
Neben den genannten Features enthalten die BWidgets auch eine Fortschrittsanzeige sowie einen Passwort-, einen Schrift- und einen Farbdialog. Außerdem lassen sich mit »DynamicHelp« Hilfetext für Menüs erzeugen, »Dialog« dient als Basis für eigene Dialogfenster.
Obwohl einige Erweiterungen inzwischen überflüssig sind, da sich auch Tk weiterentwickelt hat, bleiben noch genug gute Gründe für den Einsatz von BWidgets: Es lassen sich damit leicht moderne Oberflächen programmieren, die mit Tk-Bordmitteln nur mit Klimmzügen zu erzielen wären. (fjl)
Infos |
|
[1] BWidgets: [http://tcllib.sourceforge.net] [2] BLT: [http://incrtcl.sourceforge.net/blt/] [3] Tkdnd: : [http://www.iit.demokritos.gr/~petasis/] [5] Tcl: [http://www.tcl.tk/software/tcltk/8.4.html] [6] Active Tcl: [http://aspn.activestate.com/ASPN/Downloads/ActiveTcl/] [7] Vorträge: [http://www.t-ide.com/tcl2002e.html] [8] TK3D: [http://www.gm.com/automotive/innovations/rnd/TK3/TK3D_Software_Description.html] [9] Icon: [http://www.satisoft.com/tcltk/icons/] |
Der Autor |
|
Carsten Zerbst arbeitet bei Atlantec an einem PDM-System für den Schiffbau. Daneben beschäftigt es sich mit dem Einsatz von Tcl/Tk. |





