Zurück | Übersicht | Weiter

Kapitel 5 : Rechner mit einer geordneten Oberfläche

Wx::Panel

Da ein Fenster aber meist mehr als einen Knopf beinhaltet, brauchen wir eine Grundfläche und etwas, daß den Widgets ihren Platz darauf zuweist, selbst wenn man die Größe des Fensters ändert. Als Grundfläche kann man Wx::Panel verwenden oder statt Wx::Frame Wx::Dialog nehmen, das bereits eine eingebautes Panel besitzt. Da ihr hier etwas lernen sollt, nehmen wir ersteres. Vorhersehbar erzeugt Wx::Panel->new( $frame, -1); das Panel das automatisch die ganze Fläche des Fensters (Elternwidget im ersten Parameter, zweiter Parameter ist autogenerierte WidgetID) einnimmt, wie es der Knopf im vorigen Kapitel tat.

Panel können aber auch Teilflächen eines Fensters sein, für optische Einheiten die man zusammen ein und ausblenden will, wie Schaltflächen eines Konfigurationsdialoges oder Flächen die eine Reiterleiste aufklappt.

Wx::BoxSizer

Jetzt haben wir eine Fläche die uns eine Hintergrundfarbe setzt und auf die wir die Widgets draufstecken. Der Gummi der die Widgets auf der Fläche festhält nennt sich Sizer (Size - en. Grösse), weil er auch die Grösse der Widgets ändern kann, wenn ihr die Fenstergrösse ändert. Es gibt verschiedene Arten von Sizern, für den Anfang nehmen wir die einfachste, den Boxsizer, der wirklich wie ein Gummi funktioniert. Oder besser gesagt wie eine gummiartige Perlenschnüre auf die wir die Widgets in der Reihenfolge auffädeln in der sie auch angezeigt werden. Das er mit Wx::BoxSizer->new(wxHORIZONTAL); erstellt wird sollte jetzt wirklich niemand mehr überraschen, auch das wxHORIZONTAL erklärt sich fast von selbst, da so ein gummi ja bloss waagerecht (xwHORIZONTAL) oder senkrecht (xwVERTICAL) gespannt werden kann. Das Auffädeln macht man dann mit der Methode Add, wobei man natürlich auch mit Insert oder Remove jederzeit noch was einfügen oder löschen kann. Vergesst nicht danach $sizer->Layout aufzurufen damit euere Änderung aus sichtbar wird.

Widgets einfügen

Auch wenn man Add auf mehrere Arten rufen kann mach ich es meistens indem ich ihm 4 Parameter übergebe. 1. eine Referenz auf das Widget, 2. ein Proportion-Flag (0/1) je nach dem ob das Widget seine Grösse behalten soll oder sich prozentual mit dem Fenster mitvergrössern soll. 3. wxFlags die angeben nach welcher Richtung ihr das Element ausrichten wollte (wxTOP, wxBOTTOM, wxLEFT, wxRIGHT) 4. Rand in Pixel. Statt zwischen die Widgets aufwendig WxSpacer einzufügen die für Abstand zwischen den Elementen sorgen, kann man auch gleich im Add angeben wieviel Abstand das Widget zum "Vordermann" lassen soll. Dabei auch Richtung beachten, weil diese Angabe bei wxLEFT den linken und bei wxRIGHT den rechten Rand betrifft.

BoxSizer schachteln

Nun enthält ein Fenster meisst mehr Widgets, die eine komplexere Anordnung brauchen als den Gänsemarsch des Wx::BoxSizer. Dazu kann man die Gridsizer nehmen, die ein Raster über das Fenster legen, in deren Zellen man die Widgets ablegen kann oder man kann auch die BoxSizer schachteln was weit mehr Möglichkeiten offen lässt. Dazu übergibt man beim $sizer->Add() einfach einen anderen Sizer anstatt eines Widget, der Rest läuft wie oben beschrieben und kann hier auch im Beispiel nachgelesen werden. Einzig neu ist das wxGROW, mit dem ich die Untersizer der ersten beiden Zeilen an den Hauptsizer knüpfe. Dieses Flag befiehlt den Sizern (oder einem Widget) den gesamten restlichen verfügbaren Platz einzunehmen. Manche mögen jetzt meinen, dazu könnte man den vorigen Parameter auf 1 setzen aber dies ist ein Irrtum, denn ein BoxSizer denkt nur eindimensional. Der Haupsizer ist ein senkrechter, wenn der durch die 1 im zweiten Parameter die Info bekommt: "ja gib dem Widget den restlichen Platz", dann betrifft das nur die Y-Ausdehnung. Sagt aber der Haupsizer dem waagerecht denkenden Untersizer mit dem =wxGROW=-Flag "du kriegst all den restlichen Platz", dann nimmt er sich die gesammt Breite des Fensters die noch frei ist. Spätestens wenn man das Beispielprogramm startet und das Fenster in Höhe und Breite grösser zieht, sieht man sofort wie Wx hier die Situation versteht.

Wx::TextCtrl

Als zusätzliches Eingabefeld und für die Ausgabe der Ergebnisse hab ich hier das Wx::TextCtrl genommen. Die meissten Werte die ich dem Textfeld bei der Erzeugung (new) gebe, halten sich an das bekannte Schema, erwähnenswert sind nur das ich als Style-Konstante wxTE_RIGHT angebe, was den Text rechtsbündig macht und das ich ihm nicht die Konstante wxTE_MULTILINE gebe, wodurch das Feld einzeilig bleibt. Und damit die aktuelle Gleichung auch ausgewertet wird wenn an innerhalb des Textfeldes Enter drückt, habe ich die Subroutine die das macht auch mit dem Event EVT_TEXT_ENTER des Feldes verknüpft.

handrechner.pl
use strict;
use warnings;

Handrechner->new->MainLoop;

package Handrechner;
use base qw(Wx::App);
use Wx qw (wxVERTICAL wxHORIZONTAL wxTOP wxLEFT wxGROW wxTE_RIGHT);
use Wx::Event qw( EVT_BUTTON EVT_TEXT_ENTER );
sub OnInit {
    my $frame = Wx::Frame->new( undef, -1, 'Wx Rechner', [-1,-1], [160, 228]);
    my $panel = Wx::Panel->new( $frame, -1);
    my $term  = $panel->{'term'} = Wx::TextCtrl->new( $panel, -1, '', [-1,-1], [-1,-1], wxTE_RIGHT );
    for my $label (0..9,'=','+','-','*','/','.','+/-','<-|', 'C' ) {
        $panel->{button}{$label} = Wx::Button->new( $panel, -1, " $label ", [-1,-1], [25,-1]);         
        EVT_BUTTON($panel, $panel->{button}{$label}, sub {
            $term->AppendText($label);
            Wx::Window::SetFocus($term);         
        } );
    }     
    EVT_BUTTON($panel, $panel->{button}{'+/-'}, sub { } );
    EVT_BUTTON($panel, $panel->{button}{'<-|'}, sub {
        my $pos = $term->GetInsertionPoint;
        $term->Remove($pos-1, $pos);
        Wx::Window::SetFocus($term);     
    } );
    EVT_BUTTON($panel, $panel->{button}{'C'}, sub {
        $term->Clear;
        Wx::Window::SetFocus($term); 
    } );     
    EVT_BUTTON($panel, $panel->{button}{'='}, \&eval_term );
    EVT_TEXT_ENTER($panel, $term, \&eval_term);

    my $h_sizer = Wx::BoxSizer->new(wxVERTICAL);
    my $r0_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
    $r0_sizer->Add( $term,  1, wxLEFT, 5 );
    $r0_sizer->AddSpacer(5);

    my $r1_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
    $r1_sizer->Add( $panel->{button}{'C'},  0, wxLEFT,  5 );
    $r1_sizer->Add($panel->{button}{'<-|'}, 0, wxLEFT,  5 );
    $r1_sizer->Add( $panel->{button}{'='},  1, wxLEFT, 48 );
    $r1_sizer->AddSpacer(5);

    my $r2_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
    $r2_sizer->Add( $panel->{button}{1},  0, wxLEFT, 5 );     
    $r2_sizer->Add( $panel->{button}{2},  0, wxLEFT, 5 );
    $r2_sizer->Add( $panel->{button}{3},  0, wxLEFT, 5 );
    $r2_sizer->Add( $panel->{button}{'+'},0, wxLEFT,25 );

    my $r3_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
    $r3_sizer->Add( $panel->{button}{4},  0, wxLEFT, 5 );     
    $r3_sizer->Add( $panel->{button}{5},  0, wxLEFT, 5 );
    $r3_sizer->Add( $panel->{button}{6},  0, wxLEFT, 5 );
    $r3_sizer->Add( $panel->{button}{'-'},0, wxLEFT,25 );

    my $r4_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
    $r4_sizer->Add( $panel->{button}{7},  0, wxLEFT, 5 );
    $r4_sizer->Add( $panel->{button}{8},  0, wxLEFT, 5 );
    $r4_sizer->Add( $panel->{button}{9},  0, wxLEFT, 5 );
    $r4_sizer->Add( $panel->{button}{'*'},0, wxLEFT,25 );

    my $r5_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
    $r5_sizer->Add( $panel->{button}{'.'}, 0,wxLEFT, 5 );
    $r5_sizer->Add( $panel->{button}{'0'}, 0,wxLEFT, 5 );
    $r5_sizer->Add($panel->{button}{'+/-'},0,wxLEFT, 5 );
    $r5_sizer->Add( $panel->{button}{'/'}, 0,wxLEFT,25 );

    $h_sizer->Add( $r0_sizer,  0, wxTOP|wxGROW,  7 );
    $h_sizer->Add( $r1_sizer,  0, wxTOP|wxGROW, 15 );
    $h_sizer->Add( $r2_sizer,  0, wxTOP, 15 );
    $h_sizer->Add( $r3_sizer,  0, wxTOP,  5 );
    $h_sizer->Add( $r4_sizer,  0, wxTOP,  5 );
    $h_sizer->Add( $r5_sizer,  0, wxTOP, 5 );

    $panel->SetSizer($h_sizer);
    $panel->SetAutoLayout(1);     
    $frame->Show(1); 
}  

sub eval_term {
    my $term = shift->{'term'};
    $term->SetValue( eval($term->GetValue) ) if $term->GetValue;
    $term->SetInsertionPointEnd;
    Wx::Window::SetFocus($term);
}

Fehlt da nicht was?

Wer schon etwas Perl kann weiß, daß man hier einiges kürzer schreiben könnte, aber für den Anfang wollt ich die lange Fassung damit man leichter versteht. Der aufmerksame Leser merkte auch sicher, daß der Knopf, der die Vorzeichen der aktuellen Zahl ändern soll ("+/-"), nichts macht. Ich überlass es als Übungsaufgabe den Taschenrechner um diese und weitere Funktionen nach belieben zu erweitern. Anfangs hab ich mich sehr gesträubt einen Taschenrechner als Beispiel zu bringen, weil es oft gemacht wird und es mich selbst schauderte: "Nein, nicht schon wieder". Aber Taschenrechner haben 'ne Menge für sich. Es sind einfache, aber vollständige und sinnvolle Programme. Zudem kann man sie einfach um gewünschte Funktionen erweitern, die man vermisst und hat so schnell Software geschrieben, die man selber gebrauchen kann. Genau dafür wurde ja Perl erfunden.


Zurück | Übersicht | Weiter

-- HerbertBreunung - 29 Mar 2006
Topic revision: r16 - 2009-04-29 - 13:19:47 - HerbertBreunung
 
Bitte die NutzungsBedingungen beachten.
Bei Vorschlägen, Anfragen oder Problemen mit dem PerlCommunityWiki bitten wir um WebBottomBarExample">Rückmeldung.