Einführung in die Erstellung | |
grafischer Oberflächen | |
mit Perl / Tk | |
von Christian Dühl |
Woraus bestehen grafische Oberflächen?
Grafische Oberflächen bestehen aus verschiedenen Fenster-Elementen, sogenannten Widgets, die zur Information des Anwenders oder zur Interaktion mit dem Anwender dienen. | |
Diese Widgets reichen von der Anzeige eines einfachen Wortes bis zu Textfeldern, die schon viele Eigenschaften eines Editors aufweisen und zu mächtigen vektororientierten Zeichenflächen. |
Verschiedene Widgets an einem Beispiel
Rechts sieht man das Optionenmenü von Powerpoint, folgende Widgets lassen sich erkennen: | |
Texteingabefelder (Entrys) | |
Überschriften (Label) | |
Auswahlfelder (Checkboxen) | |
Rahmen (Frames) | |
Schalter (Buttons) | |
Reiter (NoteBooks) |
Grundsätzliches zur Programmierung grafischer Oberflächen
Bei grafischen Oberflächen hat das Programm keine lineare Handlungssteuerung, wie man es von der „normalen“ Programmierung her gewohnt ist. Das liegt daran, dass man dem Benutzer nicht die Reihenfolge seiner Eingaben vorgeben möchte. Der Benutzer muss etwa im Dialog auf der vorigen Folie nicht von oben nach unten seine Wünsche eingeben, sondern kann auf ein beliebiges Widget klicken bzw. Text in beliebige Eingabefelder eintragen. | |
Diese Aktionen des Benuztzers nennt man Ereignisse („Events“) und der Programmablauf ist nun ereignisgesteuert, das heißt, dass das Programm auf bestimmte Ereignisse reagiert. Um dies tun zu können, gibt es eine Schleife, die immer wieder durchlaufen wird, und in der auftretende Ereignisse (vom Benutzer oder anders ausgelöst) behandelt werden. | |
Diese Ereignisschleife wird von Perl/Tk bereitgestellt und kann mit dem Befehl MainLoop()aufgerufen werden. |
"Während solche Funktionen ablaufen"
Während solche Funktionen ablaufen, reagiert die Oberfläche nicht auf Benutzereingaben. Normalerweise ist dies nicht schlimm, wenn nach dem Druck auf etwa den Ok-Button kurz etwas gemacht wird und der Dialog dann beispielsweise verschwindet. | |
Sollte man längere Berechnungen durchführen, so kann man dies der Oberfläche vorher mitteilen (dafür gibt es die Methoden Busy und Unbusy). Dann wird unter anderem ein Wartecursor angezeigt. | |
Sehr lange Wartezeiten sollte man dann mit einem Fortschrittsbalken oder ähnlichem „unterhaltsam“ gestalten. | |
Nun kommen wir zum ersten Perl/Tk-Programm. |
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
use Tk; | |
my $mw = MainWindow->new(); | |
$mw->Label(-text => 'Hallo Welt') | |
->pack(); | |
MainLoop(); |
Wenn man mal die „Vorrede“ (Shebang, Pragmas und use Tk;) und den Aufruf der MainLoop weglässt, bleiben nur zwei Anweisungen übrig. | |
my $mw = MainWindow->new(); | |
Damit wird ein neues Objekt vom Typ MainWindow erzeugt. Mindestens ein solches Objekt wird in jedem Perl/Tk-Programm gebraucht. Mit | |
$mw->Label(-text => 'Hallo
Welt') ->pack(); |
|
wird eine Überschrift (Label) erzeugt, die den Text „Hallo Welt“ anzeigt. Genauer gesagt wird ein Label-Objekt erzeugt. Hinterher wird noch die Methode pack des erzeugten Objektes aufgerufen. Dann wird das Objekt wieder vergessen. Man könnte es mit „my $label =“ vorweg „einfangen“, aber es wird hier nicht weiter benötigt. | |
Die Methode pack() packt ein Widget ins erzeugende Fenster (oder Rahmen), hier ins Hauptfenster. Dabei kann man nähere Angaben machen, wie das Objekt eingefügt werden soll. Die Darstellung hängt sehr davon ab, in welcher Reihenfolge die Objekte gepackt werden. |
pack: Der Standardgeometriemanager, man umgibt Gruppen von Widgets mit (meist unsichtbaren) Rahmen, um diese gemeinsam anzuordnen. | |
grid: Ein Geometriemanager für tabellenartige Fenster. | |
place: Ein Geometriemanager zur freien Platzierung von Widgets. Mit diesem Manager können Widgets auch überlappt dargestellt werden. |
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
use Tk; | |
my $mw = MainWindow->new(); | |
$mw->Button(-text => 'Klick mich', | |
-command => sub { $mw->destroy() }, | |
) | |
->pack(); | |
MainLoop(); |
Besprechung zum Schalter Widget
Mit den Zeilen | |
$mw->Button(-text => 'Klick mich', -command => sub { $mw->destroy() }, ) ->pack(); |
|
wird ein Schalter-Objekt (Button) erzeugt und gepackt. Der Schalter bekommt die Aufschrift „Klick mich“ und außerdem wird über -command eine sogenannte Callback-Funktion hinterlegt, die aufgerufen wird, wenn der Schalter ausgelöst wird. | |
Im Beispiel ist die Callback-Funktion eine Closure, man könnte statt sub {...} auch eine Referenz zu einer benannten Funktion angeben, etwa so: | |
-command => \&beenden | |
Wieder könnte man das Schalter-Objekt mit „my $schalter =“ vor der obigen Zeile für die spätere Verwendung abspeichern, aber das ist hier noch nicht nötig. |
-option => Anweisung ist typischer Tk-Stil um Parameter festzulegen. Hier ein paar Widget-Optionen, die man häufiger anwendet: | |
Widget-Option mögliche Werte Bedeutung | |
-anchor "n", "ne", "e", "se", "s", "sw", "w", Der Text wird an dieser Position verankert. "nw" oder "center " | |
-background Farbe (etwa 'SeaGreen3') Legt die Hintergrundfarbe des Widgets fest. | |
-borderwidth Betrag Ändert die Breite des Rahmens. | |
-command Callback
(Funktionsreferenz, anonyme Subroutine Aktiviert die Callback-Funktion, wenn
das oder anonyme Liste, deren erstes Element Widget angeklickt wird. eine Funktionsreferenz und deren weitere Elemente Parameter für diese Funktion sind): \&myfunc sub { ... } [ \&myfunc, $arg1, $arg2, \@arg3 ] |
|
-height Betrag (in Pixeln oder Zeilen) Ändert die Höhe des Widgets. | |
-width Betrag (in Pixeln oder Zeichen) Ändert die Breite des Widgets. | |
-justify "left", "right" oder "center“ Ausrichtung von mehrzeiligen Text. | |
-relief "flat",
"groove", "raised", "ridge", Ändert den
Kantentyp. "sunken" oder "solid " |
|
-state "normal", "disabled" oder "active " Der Status des Widgets. | |
-text "Text " Der Textstring, der im Widget angezeigt wird. | |
-textvariable Variablenreferenz:
\$text Wie -text , nur das sich der Text ändert, wenn die Variable $text sich ändert. |
|
-title "Titel" Gibt dem Widget einen Titel. |
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
use Tk; | |
my $mw = MainWindow->new(); | |
$mw->Entry()->pack(); | |
MainLoop(); |
Besprechung zum Eingabefeld Widget
Mit der Zeile | |
$mw->Entry()->pack(); | |
wird ein Eingabefeld-Objekt (Entry) erzeugt und gepackt. In diesem Eingabefeld können einzeilige Eingaben des Benutzers entgegengenommen werden. | |
Man kann es auch beim Start des Programms schon mit Text vorbelegen, den der Benutzer dann ggf. ändern kann. | |
Möchte man später abfragen, was
der Benutzer in das Eingabefeld eingetragen hat, so kann man dazu die Methode
get verwenden. Dazu muss man das Eingabefeld bei der Erzeugung aber in einer
Variablen abspeichern: my $entry = $mw->Entry()->pack(); Dann kann man den Inhalt mit my $input = $entry->get(); ermitteln. |
|
Alternativ kann man beim
Erzeugen des Eingabefeldes mit -textvariable => \$text eine (vorher definierte) Variable an das Eingabeelement binden. Ändert man den Inhalt der Variablen, so ändert sich der angezeigte Text und umgekehrt. |
Rahmenelement und Skalen Widget
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
use Tk; | |
my $mw = MainWindow->new(); | |
my $f1 = $mw->Frame(-relief => 'sunken', | |
-width => '50', | |
-height => '50', | |
-borderwidth => '1', | |
) | |
->pack(); | |
$f1->Scale(-from => 0, | |
-to => 100, | |
-orient => "vertical", | |
-label => "Schieb mich", | |
) | |
->pack(); | |
MainLoop(); |
Mit den Zeilen | |
my $f1 = $mw->Frame(-relief => 'sunken', | |
-width => '50', | |
-height => '50', | |
-borderwidth => '1', | |
) | |
->pack(); | |
wird ein Rahmenelement (Frame) $f1 mit den Maßen 50x50 Pixeln und der Randbreite 1 Pixel erzeugt und in das Hauptfenster $wm gepackt. | |
Es hat die Reliefart „sunken“. Außerdem gibt es noch die Reliefarten raised, flat, ridge, solid und groove (vergleiche perldoc Tk::options). Diese Reliefarten kann man auch bei anderen Widgets setzen, die Defaultwerte der Reliefart sind von Widget zu Widget unterschiedlich. |
Mit den Zeilen | |
$f1->Scale(-from => 0, -to => 100, -orient => "vertical", -label => "Schieb mich", ) ->pack(); |
|
wird ein Skalenelement erzeugt und in den Rahmen $f1 gepackt. Das Skalenelement hat den Wertebereich von 0 bis 100, ist vertikal ausgerichtet und trägt das Label „Schieb mich“. | |
Nun kommen wir zu einem etwas komplexeren Programm, an dem einige weitere Widgets und das generelle Umgehen mit und das Auslesen von Widgets erläutert wird. |
Auswahlmöglichkeiten: Radiobutton, Checkbox und Listbox
Besprechung zu Radiobutton, Checkbox und Listbox
Und so sieht das Programm dann aus. Der Benutzer kann mit Checkbuttons (jeder einzelne wählbar oder nicht wählbar), Radiobuttons (nur eine Auswahl pro Gruppe) und der Listbox (hier: mehrere Einträge gleichzeitig auswählbar) seine „Bestellung“ zusammenklicken und dann per OK-Button absenden. |
Sahne : ja Extrawaffel: ja Streusel : Zartbitterabrieb Sauce : Schokoladensauce Vanille Schokolade Malaga Wallnuss weiße Schokolade |
|
Aber natürlich könnte hier auch etwas anderes passieren, etwa die Bestellung per E-Mail an den nächsten Eisladen zu verschicken oder dergleichen. |
Besprechung zum Geometriemanager pack
Hier tauchten nun zum ersten Mal Optionen der Methode pack auf. Etwa beim Schalter: | |
pack(-side => 'bottom', | |
-expand => 0, | |
-fill => 'none', | |
-ipadx => 20, | |
-pady => 2, | |
); | |
pack-Option mögliche Werte Bedeutung | |
-side "left",
"right", "top" oder "bottom" Platziert das
Widgetrechteck an die angegebene Seite des Fensters oder Frames. |
|
-fill "none",
"x", "y" oder "both" Das Widgetrechteck
breitet sich in die angegebene Richtung aus. |
|
-expand 1 oder 0 Das Widget
füllt den Platz im Widgetrechteck aus (oder nicht). |
|
-ipadx Betrag in Pixeln Definiert einen horizontalen Abstand um das Widget | |
(dabei wird es um 2xBetrag in
horizontaler Richtung vergrößert). |
|
-ipady Betrag in Pixeln
Definiert einen vertikalen Abstand um das Widget (dabei wird es um 2xBetrag in vertikaler Richtung vergrößert). |
|
-padx Betrag Setzt links und rechts je Betrag Pixel Polster ein. | |
-pady Betrag Setzt oben und unten je Betrag Pixel Polster ein. | |
-anchor "n",
"ne", "e", "se", "s", "sw",
"w", "nw“ Verankert das Widget in seinem Rechteck an der oder "center“ angegebenen Stelle. |
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
use Tk; | |
my $mw = MainWindow->new(); | |
my $box = $mw->Scrolled('Listbox', | |
-scrollbars => 'oe', | |
-height => 5, | |
) | |
->pack(-side => 'left', | |
-fill => 'both', | |
-expand => 1, | |
); | |
$box->insert('end', $_) | |
for qw(Eins Zwei Drei Vier Fünf Sechs Sieben Acht Neun Zehn); | |
MainLoop(); |
Besprechung zum Rollbalken Widget
Mit den Zeilen | |
my $box = $mw->Scrolled('Listbox', -scrollbars => 'oe', -height => 5, ) |
|
wird eine Listbox erzeugt, die über einen optionalen „östlichen“ Rollbalken (Scrollbar) verfügt und fünf Zeilen hoch ist. Mit | |
$box->insert('end', $_) for qw(Eins Zwei Drei Vier Fünf Sechs Sieben Acht Neun Zehn); |
|
werden nacheinander ans Ende der Listbox zehn Werte eingefügt. Da die Listbox nur fünf Zeilen hoch ist, wird der Rollbalken benötigt (und deshalb angezeigt). |
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
use Tk; | |
my $mw = MainWindow->new(); | |
my $c1 = $mw->Canvas(-width => '350', | |
-height => '350', | |
) | |
->pack(); | |
$c1->createLine( 25, 175, 325, 175, | |
-arrow => 'last', ); | |
$c1->createText( 335, 175, | |
-fill => 'blue', | |
-text => 'X', ); | |
$c1->createLine(175, 325, 175, 25, | |
-arrow => 'last', ); | |
$c1->createText(175, 15, | |
-fill => 'darkgreen', | |
-text => 'Y', | |
); | |
MainLoop(); |
Besprechung zum Zeichenflächenelement
Mit den Zeilen | |
my $c1 = $mw->Canvas(-width => '350', -height => '350', ) |
|
wird eine Canvas erzeugt, welche 350x350 Pixel groß ist. Dann werden mit createLine und createText x-, y-Achse und Achsenbeschriftungen erzeugt: | |
$c1->createLine( 25, 175, 325,
175, -arrow => 'last', ); $c1->createText( 335, 175, -fill => 'blue', -text => 'X', ); |
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
use Tk; | |
my $mw = MainWindow->new(); | |
$mw->Button(-text => 'Exit', | |
-command => [$mw => 'destroy'], | |
) | |
->pack(-side => 'bottom'); | |
$mw->Scrolled('Text', | |
-scrollbars => 'osoe', | |
-wrap => 'none', | |
) | |
->pack(-expand => 1, | |
-fill => 'both', | |
); | |
MainLoop(); |
Mit den Zeilen | |
$mw->Scrolled('Text', -scrollbars => 'osoe', -wrap => 'none', ) |
|
wird ein Textelement erzeugt, welches rechts und unten über optionale Scrollbalken verfügt. Der Text wird nicht umgebrochen. | |
Der Exit-Button wird zuerst
eingesetzt, damit er auch noch zu sehen ist, wenn das Fenster stark
verkleinert wird. Damit er trotzdem unter dem Text-Widget landet, wird er
mit -side => 'bottom' gepackt. |
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
use Tk; | |
use Tk::NoteBook; | |
my $mw = new MainWindow; | |
my $nb = $mw->NoteBook()->pack(); | |
my $page1 = $nb->add('PageID-1', | |
-label => 'Reiter Eins', ); | |
my $page2 = $nb->add('PageID-2', | |
-label => 'Reiter Zwei', ); | |
$page1->Label(-text => 'In Seite 1')->pack(); | |
$page2->Label(-text => 'In Seite 2')->pack(); | |
MainLoop(); |
Mit den Zeile | |
my $nb = $mw->NoteBook()->pack(); | |
wird ein Reiterelement (NoteBook) erzeugt und gepackt. In diesem werden nun mit | |
my $page1 = $nb->add('PageID-1', -label => 'Reiter Eins', ); my $page2 = $nb->add('PageID-2', -label => 'Reiter Zwei', ); |
|
zwei Seiten angelegt mit den Reitertiteln „Reiter Eins“ und „Reiter zwei“. In die Seiten wird nun noch je ein Label gesetzt: | |
$page1->Label(-text => 'In Seite
1')->pack(); $page2->Label(-text => 'In Seite 2')->pack(); |
Man kann seinem Perl/Tk-Programm ein Menü hinzuzufügen, das man wie man es unter Windows gewöhnt ist, mit Alt+Buchstabe aufrufen und mit Maus und Tastatur darin navigieren kann. Einzelnen Menüpunkten kann man dabei Tastenkürzel zuordnen, die auch im Menü angezeigt werden. | |
Das Beispielprogramm menu.pl ist ziemlich lang, deshalb gehe ich zunächst auf den wichtigen Punkt, das Menü ein. | |
Das gesamte Programm kopiere ich unkommentiert auf eine ausgeblendete Folie für alle, die sich diesen Vortrag selbst ansehen. |
1.) Menü (Menubar) erstellen: my $menu = $mw->Menu(-type => 'menubar'); |
|
2.) Dem Fensterwidget dieses
Menü zuweisen: $mw->configure(-menu => $menu); |
|
3.) Eigentliche Menüs in der
Menubar eintragen: $menu->cascade(-label => 'Datei', -underline => 0, ); $menu->cascade(-label => 'Bearbeiten', -underline => 0, ); $menu->separator(); $menu->cascade(-label => 'Hilfe', -underline => 0, ); |
|
5.) Untermenü in Menubar
eintragen: $menu->entryconfigure('Datei', -menu => $menu_datei); |
|
6.) Bindungen zum Aufruf des
Menüs erzeugen: $mw->bind('<Alt-d>', sub {$menu->postcascade('Datei');}); $mw->bind('<Alt-b>', sub {$menu->postcascade('Bearbeiten');}); $mw->bind('<Alt-h>', sub {$menu->postcascade('Hilfe');}); |
So sieht das Programm aus: |
Empfehlenswertes Buch: „Mastering Perl/Tk“ von Steve Lidie und Nancy Walsh. | |
Perldoc: Zu allen Teilproblemen hilft einem meist ein entsprechender perldoc aufruf weiter. | |
Perl/Tk im Netz: http://www.perltk.org/ |
|
Onlinetutorial: http://wiki.perl-community.de/bin/view/Wissensbasis/PerlTkTutorial |
|