Damit dieser Kurs möglichst kompakt bleibt, werden jetzt gleich 3 Steuerelemente auf einmal vorgestellt. Sie sehen ja auch an den vergangenen Beispielen, dass sich diese Elemente stets miteinander verzahnen.
Hier geht es um Checkbutton, Radiobutton und das Element Listbox.
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Tk;
my $main = MainWindow->new();
my $check1 = $main->Checkbutton(-text => "Kontrollkästchen 1")->pack();
my $check2 = $main->Checkbutton(-text => "Kontrollkästchen 2")->pack();
my $radio1 = $main->Radiobutton(-text => "Optionsfeld 1")->pack();
my $radio2 = $main->Radiobutton(-text => "Optionsfeld 2")->pack();
my $radio3 = $main->Radiobutton(-text => "Optionsfeld 3")->pack();
my $liste = $main->Listbox(-relief => 'sunken',
-width => -1, # Shrink to fit
-height => 5,
-setgrid => 1,
)
->pack();
$liste->insert('end', $_) for qw(Teil1 Teil2 Teil3 Teil4 Teil5);
$main->MainLoop();
So sieht das Ergebnis aus:
Man sieht auf dem erzeugten Fenster schon die angelegten Elemente, aber
zumindest mit den Radiobuttons gibt es ein Problem, denn alle drei sind
angewählt und lassen sich auch nicht deaktivieren.
Das liegt daran, dass von einer Gruppe Radiobuttons genau einer aktiv ist und
wir hier drei Gruppen haben.
Die Gruppen werden über die mit -variable
zugewiesene Variable gebildet,
Radiobuttons, die zu einer Gruppe gehören, weist man die gleiche Variable zu.
Hier ein funktionstüchtiges, dafür etwas komplexeres Beispiel. Hier werden Frames verwendet, die eigentlich erst im nächsten Kapitel vorgestellt werden, also werfen Sie ggf. einen Blick in Kapitel 8, wenn Sie es genau wissen wollen, anderenfalls nehmen Sie erstmal hin, dass Frames Behälter für andere Steuerelemente sind.
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Tk;
#
# Hauptfenster:
#
my $mw = MainWindow->new();
$mw->title('Eiswähler');
my $fo = $mw->Frame()->pack(
-side => 'top',
-expand => 1,
-fill => 'both',
);
#
# Checkbuttons:
#
my $f1 = $fo->Frame(
-borderwidth => 3,
-relief => 'groove',
)->pack(
-side => 'right',
-expand => 1,
-fill => 'both',
);
my $sahne;
my $waffel;
$f1->Checkbutton(
-text => "Sahne",
-variable => \$sahne,
)->pack(-anchor => 'w');
$f1->Checkbutton(
-text => "Extrawaffel",
-variable => \$waffel,
)->pack(-anchor => 'w');
#
# Radiobuttons1:
#
my $f2 = $fo->Frame(
-borderwidth => 3,
-relief => 'groove',
)->pack(
-side => 'right',
-expand => 1,
-fill => 'both',
);
my @streuselsorten = (
'keine Streusel',
'Schokoladenstreusel',
'bunte Streusel',
'Krokantstreusel',
'Zartbitterabrieb',
);
my $streusel = 0;
for my $i (0..$#streuselsorten) {
$f2->Radiobutton(
-text => $streuselsorten[$i],
-variable => \$streusel,
-value => $i,
)->pack(-anchor => 'w');
}
#
# Radiobuttons2:
#
my $f3 = $fo->Frame(
-borderwidth => 3,
-relief => 'groove',
)->pack(
-side => 'right',
-expand => 1,
-fill => 'both',
);
my @saucensorten = (
'keine Sauce',
'Erdbeersauce',
'Schokoladensauce',
'Tropico',
);
my $sauce = 0;
for my $i (0..$#saucensorten) {
$f3->Radiobutton(
-text => $saucensorten[$i],
-variable => \$sauce,
-value => $i,
)->pack(-anchor => 'w');
}
#
# Listbox:
#
my $listbox = $fo->Listbox(
-relief => 'sunken',
-width => -1, # Shrink to fit
-setgrid => 1,
-selectmode => 'single',
)->pack(
-side => 'right',
-expand => 1,
-fill => 'both',
);
my @kugeln = (
'Eine Kugel',
'Zwei Kugeln',
'Drei Kugeln',
'Vier Kugeln',
'Fünf Kugeln',
);
for my $kugel ( @kugeln ) {
$listbox->insert('end', $kugel);
}
$listbox->selectionSet(2, 2); # Default : drei Kugeln
#
# OK-Button:
#
$mw->Button(
-text => 'OK',
-command => sub {
print "Sahne : ", $sahne?'ja':'nein', "\n",
"Extrawaffel: ", $waffel?'ja':'nein', "\n",
"Streusel : ", $streuselsorten[$streusel], "\n",
"Sauce : ", $saucensorten[$sauce], "\n";
my @l = $listbox->get(0, 'end');
my @s = $listbox->curselection();
print $l[$_], "\n" for (@s);
$mw->destroy();
},
)->pack(
-side => 'bottom',
-expand => 0,
-fill => 'none',
-ipadx => 20,
-pady => 2,
);
$mw->MainLoop();
So sieht das Ergebnis aus:
Ausgabe des Programms:
Sahne : ja
Extrawaffel: nein
Streusel : Krokantstreusel
Sauce : Erdbeersauce
Zwei Kugeln
Wie man sieht, kommt man nun an alle vom Benutzer eingestellten Werte heran.
Je nach Vorkenntnissen in Perl bedarf dieses Beispiel nun mehr oder weniger langer Kommentare, deshalb erkläre ich das Programm Abschnitt für Abschnitt:
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Tk;
Der übliche Programmkopf, mit use utf8;
, damit die Umlaute richtig dargestellt werden.
my $mw = MainWindow->new();
$mw->title('Eiswähler');
my $fo = $mw->Frame()->pack(
-side => 'top',
-expand => 1,
-fill => 'both',
);
Hier wird das Hauptfenster $mw
definiert, der Fenstertitel vergeben
und ein Rahmen $fo
angelegt, der oben gepackt wird und sich in alle
Richtungen ausdehnen darf.
In diesen Rahmen kommen alle weiteren Elemente mit Ausnahme des
OK-Buttons.
Auswahl besonderer Extras:
my $f1 = $fo->Frame(
-borderwidth => 3,
-relief => 'groove',
)->pack(
-side => 'right',
-expand => 1,
-fill => 'both',
);
my $sahne;
my $waffel;
$f1->Checkbutton(
-text => "Sahne",
-variable => \$sahne,
)->pack(-anchor => 'w');
$f1->Checkbutton(
-text => "Extrawaffel",
-variable => \$waffel,
)->pack(-anchor => 'w');
Hier wird ein Rahmen $f1
angelegt, in dem die Checkbuttons dargestellt
werden.
Der Frame wird in der Reliefart "groove" und mit einem 3 Pixel breiten
Rand dargestellt, er ist also nicht nur als Trennung fürs Packen da,
sondern auch als optische Abtrennung der Checkbuttons von den anderen
Fensterelementen.
Die Angabe -anchor => 'w'
beim Packen der Checkbuttons bewirkt, dass
diese im Frame linksbündig dargestellt werden.
Die Variablen $sahne
und $waffel
werden an die beiden Checkbuttons
gebunden.
Hier wird die Streuselsorte gewählt:
my $f2 = $fo->Frame(
-borderwidth => 3,
-relief => 'groove',
)->pack(
-side => 'right',
-expand => 1,
-fill => 'both',
);
my @streuselsorten = (
'keine Streusel',
'Schokoladenstreusel',
'bunte Streusel',
'Krokantstreusel',
'Zartbitterabrieb',
);
my $streusel = 0;
for my $i (0..$#streuselsorten) {
$f2->Radiobutton(
-text => $streuselsorten[$i],
-variable => \$streusel,
-value => $i,
)->pack(-anchor => 'w');
}
Hier wird ein Rahmen $f2
angelegt, in dem die Radiobuttons analog zum
Rahmen $f1
dargestellt werden.
Die Angabe -anchor => 'w'
beim Packen der Checkbuttons bewirkt auch hier,
dass diese im Frame linksbündig dargestellt werden.
Die einzelnen Streuselsorten werden im Array @streuselsorten
vorgehalten,
die Variable $streusel
enthält den Index der ausgewählten Bestreuselung
(oder -1, falls noch kein Radiobutton dieser Gruppe ausgewählt wurde).
Mit -variable => \$streusel
wird die Variable $streusel
an alle
Radiobuttons dieser Gruppe gebunden, mit -value => $i
wird für jeden
Radiobutton festgelegt, welchen Wert die Variable bei der Anwahl des
Buttons bekommen soll.
Andersherum werden aber auch alle diejenigen Radiobuttons, an die diese Variable gebunden wurde, aktiviert, wenn der Wert der Variablen auf irgend eine andere Weise auf diesen Wert gesetzt wird. Alle Radiobuttons dieser Gruppe mit anderen Werten werden dann deaktiviert.
Übrigens: Theoretisch können mehrere Radiobuttons den gleichen Wert zugewiesen bekommen, dann können alle diese Buttons aber nur gemeinsam an oder aus sein, was i.A. dem Sinn von Radionbuttons widerspricht.
Hier wird die Saucensorte gewählt:
my $f3 = $fo->Frame(
-borderwidth => 3,
-relief => 'groove',
)->pack(
-side => 'right',
-expand => 1,
-fill => 'both',
);
my @saucensorten = (
'keine Sauce',
'Erdbeersauce',
'Schokoladensauce',
'Tropico',
);
my $sauce = 0;
for my $i (0..$#saucensorten) {
$f3->Radiobutton(
-text => $saucensorten[$i],
-variable => \$sauce,
-value => $i,
)->pack(-anchor => 'w');
}
Diese Radiobuttongruppe dient der Saucenauswahl und ist völlig analog zur
Radiobuttongruppe 1 (Streuselauswahl).
Hier heißt das Array mit den Saucen @saucensorten
und die Index- bzw.
Wert-Variable $sauce
.
Wichtig ist, dass bei allen Radiobuttongruppen ein Defaultwert gesetzt wird, der einem der zugeordneten Werte entspricht, so dass beim Programmstart in jeder Gruppe ein Radiobutton angewählt ist. (In diesem Beispiel sind das die Werte 'keine Streuseln' und 'keine Sauce'.)
Hier wird die Anzahl an Eiskugeln ausgewählt:
my $listbox = $fo->Listbox(
-relief => 'sunken',
-width => -1, # Shrink to fit
-setgrid => 1,
-selectmode => 'single',
)->pack(
-side => 'right',
-expand => 1,
-fill => 'both',
);
my @kugeln = (
'Eine Kugel',
'Zwei Kugeln',
'Drei Kugeln',
'Vier Kugeln',
'Fünf Kugeln',
);
for my $kugel ( @kugeln ) {
$listbox->insert('end', $kugel);
}
$listbox->selectionSet(2, 2); # Default : drei Kugeln
Hier wird zunächst eine Listbox erzeugt, die ganz links angezeigt wird (da sie
letztes Element einer Reihe von rechts angefügten Elementen ist).
Als Refiefart wird mit -relief => 'sunken'
'sunken' ausgewählt, die Option
-width => -1
veranlasst die Listbox so breit zu werden wie ihr breitester
Eintrag.
Die Option -setgrid => 1
gibt an, dass die Listbox Texteinträge anzeigt.
Dies ist etwa bei Größenänderungen des Fensters interessant, dort werden dann
immer nur ganze Zeilen mehr oder weniger in der Listbox angezeigt.
Die Option "-selectmode => 'single'" schließlich bewirkt, dass immer nur ein
Eintrag ausgewählt werden kann.
Schließlich macht es nicht Sinn, in einer Bestellung (für ein Eis) etwa
sowohl 2 als auch 4 Kugeln bestellen zu wollen.
Näheres zu dieser und anderen Optionen (wie immer) unter perldoc Tk::Listbox
und perldoc Tk::Options
.
Mit $listbox->selectionSet(2, 2)
wird der zweite Eintrag ("drei Kugeln")
aktiviert.
Übrigens: Nähme man keine Defaultbelegung vor, so wäre kein Eintrag ausgewählt und die
Methode curselection
würde eine leere Liste zurückliefern.
Ende des Programms und Ausgabe der durch den Benutzer vorgenommenen Einstellungen:
$mw->Button(
-text => 'OK',
-command => sub {
print "Sahne : ", $sahne?'ja':'nein', "\n",
"Extrawaffel: ", $waffel?'ja':'nein', "\n",
"Streusel : ", $streuselsorten[$streusel], "\n",
"Sauce : ", $saucensorten[$sauce], "\n";
my @l = $listbox->get(0, 'end');
my @s = $listbox->curselection();
print $l[$_], "\n" for (@s);
$mw->destroy();
},
)->pack(
-side => 'bottom',
-expand => 0,
-fill => 'none',
-ipadx => 20,
-pady => 2,
);
Das umfangreichste hier ist die Funktion, die dem Button per -command
zugewiesen wird, der Rest ist schon aus Kapitel 3
(Tk::Button) und Kapitel 6 (Geometriemanager pack)
bekannt.
Zur Funktion: Hier werden nun die vom Benutzer vorgenommenen (oder in der Defaulteinstellung belassenen) Werte der Bedienelemente abgelesen und auf der Konsole ausgegeben.
Im Einzelnen erhält man diese Werte auf folgende Weise: Die Checkbuttonwerte
(wahr oder falsch) liegen in den Variablen $sahne
und $waffel
vor.
Die Informationen über Streusel und Sauce liegen als Indices der zugehörigen
Arrays @streuselsorten
und @saucensorten
in den Variablen $streusel
und
$sauce
vor.
Die ausgewählten Daten der Listbox werden so geholt, als ob mehrere Einträge
gleichzeitig ausgewählt werden könnten.
Dies ist eigentlich nicht nötig, da durch die Option -selectmode => 'single'
nur die Auswahl eines Eintrages möglich ist, aber es schadet nicht, auch
die anderen Fälle mit zu bearbeiten.
Die Methode get
holt alle Einträge der Listbox im angegebenen Bereich (hier
alle), die Methode curselection
holt eine Liste aller ausgewählter
Einträge der Listbox.
Ist kein Eintrag ausgewählt, so ist die Liste leer.
Übrigens: Da mit $listbox->selectionSet(2, 2)
ein Defaulteintrag für die Listbox
erzeugt und mit -selectmode => 'single'
die Auswahl von mehr als einem
Eintrag verhindert wurde, ist eigentlich sichergestellt, dass @l
genau ein
Element hat.
Somit könnte man dieses auch mit my ($l) = $listbox->curselection()
ermitteln
und ausgeben.
Aufruf der MainLoop
:
$mw->MainLoop();
Startet die Tk-Verarbeitungsschleife.
Wenn man den Listboxabschnitt folgendermaßen ändert:
my $listbox = $fo->Listbox(
-relief => 'sunken',
-width => -1, # Shrink to fit
-setgrid => 1,
-selectmode => 'extended',
)->pack(
-side => 'right',
-expand => 1,
-fill => 'both',
);
my @kugeln = (
'Vanille',
'Schokolade',
'Erdbeer',
'Heidelbeer',
'Joghurt',
'Amarena',
'Malaga',
'Wallnuss',
'Haselnuss',
'weiße Schokolade',
'After Eight',
);
for my $kugel ( @kugeln ) {
$listbox->insert('end', $kugel);
}
$listbox->selectionSet(0, 2); # Default : Fürst Pückler Art
erhält man folgendes Ergebnis:
Ausgabe im oben gezeigten Fall:
Sahne : ja
Extrawaffel: ja
Streusel : Zartbitterabrieb
Sauce : Schokoladensauce
Vanille
Schokolade
Malaga
Wallnuss
weiße Schokolade
Der Selectmode 'extended' ermöglicht die Windows-typische Auswahl mit Shift+Mausklick und Ctrl+Mausklick. Mit dem Selectmode 'multiple' kann man jeden Eintrag der Liste mit einem einzelnen Klick an- oder abwählen.
Wie immer findet man nähere Informationen in der mitgelieferten Perldokumentation, oder den Widget-Seiten zu Checkbutton, Radiobutton und Listbox.