IV. Beispiel: Menüleisten, Bilder, Radio-Buttons und Dropdown-Menüs

  • Ziel des Beispiels:
  • Ein einfacher Bildbetracher für JPEG, PNG und GIF

    Waren die bisherigen Programme eher Spielerei, so wird jetzt ein Programm mit richtigem Nährwert erklärt.

    1. Die Schritte 1 bis 3 vorhergehender Beispiele wiederholen sich an dieser Stelle:
    2. Der Ästhetik wegen habe ich meinem Hauptfenster unter dem Eintrag Label (Registrierkarte GUI) noch den Namen "Image Viewer" gegeben. Was die Maße des Fensters angeht, so will ich diesmal Vorgaben zur Orientierung geben. Mein Fenster ist 700 Pixel hoch und 900 breit.
    3. Um Zugriff auf die Vorschau im Dialog "Datei öffnen" zu erhalten, muss zu Beginn der main()-Methode über ein Codefragment (New -> Code) der folgende Befehl eingebunden werden:

      			fl_register_images();
      		
    4. Im nächsten Schritt wird eine Box über New -> other -> Box eingefügt, diese soll in unser Hauptfenster, also vor dem Anlegen noch in der Hierachie das Hauptfenster markieren.
      Auch hier möchte ich die Maße der Box vorschlagen.
      • X-Position: 50
      • Y-Position: 50
      • Width: 800
      • Height: 600

      Diese Box wird später das gewählte Bild anzeigen und darüber hinaus soll der Pfad der Grafik angezeigt werden.
      Um dies realisieren zu können, muss man der Box einen Namen (Registrierkarte C++ / Name) geben, ich nenne sie einfach "picture". Damit das fertige Programm auch nach etwas aussieht, habe ich die Label-Bezeichnung "kein Bild geöffnet" gewählt. Das mag auf den ersten Blick verwirrend erscheinen, aber ich werde das Label der Box nutzen, um den Pfad auszugeben.
      Damit das Label außerhalb der Box, unten zentriert steht, muss keine weitere Ausrichtung des Labels vorgenommen werden.
      An dieser Stelle lohnt es sich aber mit den Parametern für die Label-Ausrichtung herumzuexperimentieren.
    5. Nun soll dieses Programm auch eine Menü-Leiste enthalten, auch wenn diese noch sehr einfach gehalten ist.
      Erneut muss das Hauptfenster unserer Applikation markiert werden und über New -> Menus -> Menu_Bar kann eine entsprechende Leiste eingefügt werden. Die Menü-Leiste kann im Vorschaufenster entsprechend der Größe des Fenster angepasst werden oder alternativ über die Zahlenwerte im Eigenschaftmenü der Leiste, ich habe die Werte:
      • X-Position: 0
      • Y-Position: 0
      • Width: 900
      • Height: 20
      verwendet.
    6. Nun gilt es die noch leere Menü-Leiste mit Leben zu erfüllen. Zunächst wollen wir uns mit einem einzigen Menüpunkt begnügen, dem Eintrag "Datei". Um einen Menüeintrag anzulegen, markiert man in der Hierachie die Menüleiste und fügt über New -> Menus -> Submenu ein Eintrag ein, als Label verpasse ich ihm den gewollten Namen "Datei".
      Damit nun Datei nicht allein dasteht, braucht man noch entsprechende Menüpunkte, dazu markiert man das gerade angelegte Submenu "Datei" und fügt über New -> Menus -> Menuitem einen Eintrag ein.
      Hier sind nun mehr Einstellungen nötig, als bei den bisherigen Elementen. Zum einen muss ein Label vergeben werden, hier "Öffnen" und zum anderen muss in der Registrierkarte "C++" unter Callback eine entsprechende Routine angegeben werden, bei mir "Menu_CB", außerdem muss im Feld "User Data" noch ein Wert stehen, den ich mit "open" (einschließlich Anführungszeichen) versehe.
      Später werden in der Callback-Methode "Menu_CB" die Menüeinträge anhand dieses User-Data-Eintrages unterschieden und entsprechend weiterverarbeitet.

    7. Da unser Datei-Menü aus mehreren Einträgen bestehen soll, füge ich noch den Menüeintrag "Beenden". Es wäre jetzt recht umständlich, nochmals einen Eintrag wie im Schritt zuvor anzulegen. Deswegen kopiert man den eben angelegten "Öffnen"-Eintrag und fügt ihn noch einmal im Menüeintrag "Datei" ein. Jetzt ändert man lediglich das Label des zweiten Eintrages in "Beenden" um und den User-Data-Eintrag in "quit".
    8. Damit ist unser Programm optisch erstmal fertig, jetzt folgt das Auswerten und Verarbeiten der Menüeinträge.
      Dazu legt man über New -> Code -> function/method eine neue Methode mit dem Namen "Menu_CB" an. Es handelt sich dabei um die Callback-Methode unserer Menüleiste und die Funktion hat folgenden Kopf:
      			Name(args): Menu_CB(Fl_Widget*w,void*userdata)
      			Return Type: static void
      		

      In einem Codefragment folgt jetzt die Abarbeitung des Öffnen-Dialogs. dazu legt man ein neues Codefragment innerhalb der gerade entstandenen Methode an.

      Das erste Fragment beginnt mit der Überprüfung des User-Data-Eintrages.

      			if ((const char*)userdata == "open")
      		

      Wenn diese Bedingung erfüllt ist, kann die Abhandlung in einem eigenen Block geschrieben werden.

    9. FLTK kommt von Haus aus mit einem recht schönen Dialog zum Öffnen von Dateien, leider findet ist dieser zur Zeit noch nicht von FLUID als festes Widget unterstützt, weshalb ich ihn als Codefragment einfüge.
      Dieser sog. "File_Chooser" gibt mir den absoluten Pfad einer Datei zurück. Um diesen verarbeiten zu können lege ich erstmal mit
      			char* file;
      		

      ein dynamisches char-Feld an. Danach kommt die eigentliche Funktion des Dateidialoges
      			file = fl_file_chooser("Öffnen", "Bild (*.{jpg,png,gif,bmp})\t", "", 0);
      		

      Was macht dieser Befehl nun?
      Zum einen ruft er natürlich einen Dialog zum Öffnen einer Datei auf, wie man ihn aus vielen Anwendungen kennt. Der Eintrag "Öffnen" gibt diesem Dialog diesen Namen.
      Da dieses Programm nur Bilddateien von den Formaten JPG, PNG, GIF oder BMP anzeigen sollen, werden alle Dateien (* Stern steht für Wildcard) mit diesen Endungen angezeigt. "\t" trennt mehrere Vorgaben voneinander, so kannte man z.B. zwischen JPG und BMP unterscheiden, der erste Eintrag ist dabei der Standardwert.
      In der Dokumentation unter www.fltk.org findet man noch zahlreiche Möglichkeiten, wie man mit diesem Dialog umgehen kann.
    10. Nun steht der komplette absolute Pfad in der Variable file. Dieser Pfad zeigt auf eine Grafik und die muss jetzt in den Speicher geladen werden. Dies geschieht wieder innerhalb des gleichen Codefragmentes in der gleichen if-Anweisung.
      FLTK hält auch dafür eine einen Befehl bereit, allerdings kommt es jetzt zu einer Fallunterscheidung.

      FLTK kann zwar mit den vier Formaten JPG, GIF, PNG und BMP umgehen, aber es muss wissen, welcher Typ vorliegt. Deswegen wird nun zuerst die Länge der Variable file ermittelt und dann das letzte, vorletzte und vorvorletzte Zeichen aus dem Feld herauskopiert, diese stellen die Dateiendung dar.

      Zu Beginn ermittelt man die Länge des Strings über den Befehl:

      			std::size_t size = std::strlen(file);
      		

      Da ich meist den STD-Namespace verwende, können die STD-Anweisungen auch entfallen.
      In der Variable size, die sich wie eine ganzzahlige, vorzeichenlose Zahl verhält, steht nun die Gesamtlänge meines Feldes, ohne das abschließende '\0'. Wenn diese Zahl z.B. 50 ist, muss ich mir nun die Zeichen 47, 48 und 49 herauskopieren, die drei Zeichen bilden die Endung.

      Sicherlich gibt es irgendwo eine Funktion, die mir den Teilstring elegant herauskopiert, aber da es sich hierbei um vier Anweisungen handelt, kopiere ich diese jetzt manuell:

      			char endung[3];
      			char endung[0] = file(size-3);
      			char endung[1] = file(size-2);
      			char endung[2] = file(size-1);
      		

      Auch eine for-Schleife wäre denkbar, aber auch die wird gleich viel Tipparbeit bedeuten.
      Jetzt steht in diesem Feld "endung" die nötige Information und jetzt kann man sich an die Fallunterscheidung machen.
    11. Ich bin noch immer im ersten Codefragment und brauche jetzt 4 if-Anweisungen, die alle dem selben Aufbau unterliegen - perfekte Voraussetzungen zum Nutzen der Zwischenablage.

      Die erste Anweisung steht für JPG-Dateien.

      				if (strcmp("jpg",endung) == 0)
      				{
      					Fl_JPEG_Image * image = new Fl_JPEG_Image(file);
      				}
      		

      Für PNG-Dateien:

      				if (strcmp("png",endung) == 0)
      				{
      					Fl_PNG_Image * image = new Fl_PNG_Image(file);
      				}
      		

      Für GIF-Dateien:

      				if (strcmp("gif",endung) == 0)
      				{
      					Fl_GIF_Image * image = new Fl_GIF_Image(file);
      				}
      		

      Für BMP-Dateien:

      				if (strcmp("bmp",endung) == 0)
      				{
      					Fl_BMP_Image * image = new Fl_BMP_Image(file);
      				}
      		

      Es folgen noch drei Anweisungen, dann ist der Menüeintrag "Öffnen" (endlich) abgearbeitet.

    12. Was jetzt noch fehlt, ist zum einen das Übergeben des Bildes image an unsere Box, das Ändern des Labes der Box und schließlich muss die Box neu gezeichnet werden.
      All das erfolgt nun relativ einfach über die Anweisungen:
      			picture->image(image);       //das FLTK-Bild "image" wird an die Box übergeben
      			picture->label(file);        //das Label der Box erhält den Pfad der Datei
      			picture->redraw();           //veranlasst das erneute Zeichnen der Box picture in unserem Hauptfenster
      		

      Damit kann der Anweisungsblock geschlossen werden und müsste nun so aussehen:

    13. Kümmern wir uns um die nächste Menüfunktion, "Beenden"
      Dazu benötigen wir wieder ein neues Codefragment, diesmal beginnt es mit der Anweisung:
      			if ((cost char*)userdata == "quit")
      		

      Es ist kein Gehemins, dass der Befehl:

      			exit(0);
      		

      sämtliche Fenster der aktuellen Anwendung schließt.
    14. Es müssen noch eine ganze Reihe an Bibliotheken deklariert werden:
      			#include 
      			#include 
      			#include 
      			#include 
      			#include 
      			#include 
      			#include 
      			#include 
      		

    15. Damit ist der Bildbetrachter fertig und kann kompiliert werden.
      Nachtrag: Wer ein Bild lädt, das die Dimensionen der Box (800x600 Pixel) sprengt, der wird nur einen Teil des Bildes sehen. Um dies zu umgehen muss in den Eigenschaften der Box der Punkt "Resizeable" aktiviert sein.
    16. Tutorial-4-1.fl Tutorial-4-1.h Tutorial-4-1.cpp

    Ein Formular, welches die persönlichen Daten einliest

    1. Nach diesem kurzen Ausflug in die Darstellung von Bildern mit FLTK/FLUID geht es zurück zu Formularen.
      Es soll an dieser Stelle eine Maske für
      • Anrede
      • Vorname
      • Name
      • Geburtsdatum

      geschrieben werden. Dabei sollen die verschiedenen Möglichkeiten der Formularobjekte genutzt werden.

      Hinweis: Die Benutzung von Checkboxen erfolgt analog zu Radio-Buttons, nur dass anstatt "Radio" in der Registrierkarte C++ eines Buttons "Toggle" gewählt werden muss, natürlich ist dann auch Mehrfachauswahl möglich.

    2. Wie immer geht es mit einem neuen Fenster los. In dieses kommen nun mehrere Formularobjekte.
      Los geht es mit der Auswahl zwischen der Anrede, "Frau" oder "Mann".
      Dazu fügee ich über New -> Group -> Group eine neue Gruppe ein. Dieser gebe ich das Label "Anrede".
      Über New -> Buttons -> Round_Button kann nun ein sog. Radio-Button eingefügt werden. Damit dieser als solcher erkannt wird, muss in der Registerkarte "C++" des Eigenschaftenmenü im Drop-Down-Menü "Radio" ausgewählt werden.
      Die Radio-Buttons, die innerhalb einer Gruppe zusammengefasst sind, haben die Eigenschaft, dass sich nur einer von Ihnen auswählen lässt. Dem ersten Button habe ich die Labelbezeichnung "Frau" gegeben. In den Callback des Widgets habe ich folgenden Code direkt eingetragen:
      			vfrau = true;
      			vherr = false;
      		

      Zur Erklärung: Ich werde später noch globale Variablen festlegen, unter anderem zwei vom Typ Boolean mit den Bezeichnungen "vfrau" und "vherr". An dieser Stelle lege ich die beiden Werte manuell fest.
      Ich könnte alternativ auch den Widgets Namen geben (globale Variablen anlegen) und später mittels des Befehls "widget->value()" den entsprechenden Wert prüfen.
    3. Dieser Schritt wiederholt sich noch eimal für die Herren. Man muss darauf achten, dass die einzelnen Optionen in einer Gruppe stehen, ansonsten verlieren die Radio-Buttons ihren Sinn. Der Callback für die Herren:
      			vfrau = false;
      			vherr = true;
      		

    4. Als nächstes brauchen wir ein simples Eingabefeld, was über New -> Text -> Input eingefügt wird. Das Label nenne ich "Vorname" und in der Registrierkarte "C++" vergebe ich den Namen "vorname".
    5. Analog dazu fügt man ein weiteres Eingabefeld ein und versieht das Label mit "Nachname" und den C++-Namen mit "nachname".

    6. Jetzt kommen wir zur Eingabe des Geburtsdatums. Dazu sollen zwei Drop-Down-Menüs und ein Texteingabefeld genutzt werden. Das erste Drop-Down-Menü soll die Tage 1 bis 31 erhalten und das zweite die Monatsnamen.
      Um ein solches Drop-Down-Menü einzufügen, wählt man New -> Menus -> Choice, gleich danach sieht man schon das Eigenschaftenmenü. Das Label heißt "Tag:" und - ganz wichtig - der Name in der Registrierkarte C++ muss vergeben werden, bei mir "day".
      Jetzt hat man ein leeres Drop-Down-Menü, welches man schon ordentlich in der GUI positionieren kann. Um jetzt die Einträge zu erstellen, markiert man in der Hierachie das Choice-Menü und fügt über New -> Menus -> Menuitem einen neuen ein. Dessen Label bezeichnet man nun mit "1".
      Danach erfreut sich die Zwischenablage über Arbeit, denn es müssen noch 30 weitere Einträge angelegt werden mit den Labes "2", "3",...,"30" und "31". Es empfiehlt sich hier mehr als anderswo mit der Zwischenablage zu arbeiten. und einfach nur das Label anzupassen.
      Die einzelnen Einträge brauchen keine Namen und keine Callback-Funktion für dieses Beispiel.
    7. Jetzt wird das zweite Drop-Down-Menü angelegt, alles erfolgt analog zum ersten, nur dass diesmal das Label des Menüs "Monat" heißt und die Labels der Menüeinträge "Januar", "Februar",...,"Dezember".
      Auch hier muss dem eigentlichen Menü (Label: Monat) wieder ein C++-Pointer vergeben werden, diesmal "month".
    8. Damit die Eingabe des Geburtsdatums vollständig ist, fügt man noch ein weiteres Texteingabefeld wie schon für Vor- und Nachname ein. Jetzt heißt das Label aber "Jahr" und der C++-Name "year". Außerdem wählt man Drop-Down-Menü oben, rechts (Registrierkarte C++) den Eintrag "int". Somit ist es nur gestattet Zahlen einzugeben.
    9. Damit unsere GUI fertig ist, benötigen wir noch ein Button und zwar mit der Aufschrift "OK". Die Callback-Methode soll schlicht "submit" heißen.
      Dieser wird später beim Betätigen Überprüfen, ob alle Werte gesetzt worden und ob das Datum korrekt ist.
    10. Um die Übersicht zu behalten, führen wir als nächste erstmal globale Variablen ein. Die geschieht über New -> Code -> Declaration. Wir benötigen folgende Variablen:
      			bool vfrau = false;
      			bool vherr = false;
      			char * vvorname;
      			char * vnachname;
      			int  vday;
      			int  vmonth;
      			int  vyear;
      		

      Ich habe allen Namen ein "v" vorangestellt, um zu sehen, dass es sich um die globalen Variablen handelt (v für value). An dieser Stelle darf ich natürlich keinen Namen verwenden, den ich schon mal als C++-Name für ein Widget benutzt habe. Diese Namen sind fast immer globale Variablen und würden daher doppelt vereinbart werden.
      Wenn man diese übrigens nicht global definieren möchte, kann man im Eigenschatenmenü eines Widgets neben dem C++-Namen mit dem Button "public" das ganze vermeiden. Aber wir brauchen globale Variablen für dieses Beispiel.
    11. Kommen wir zur Funktion "submit". Diese wird wie jede andere auch über New -> Code -> function/method eingefügt. Auch der Kopf ist wie bei allen anderen Button-Callback-Funktionen:
      • Zeile 1: submit(Fl_Widget*w,void*userdata)
      • Zeile 2: static void
    12. Im nächsten Schritt folgt wieder ein größeres Codefragment benötigt, eingefügt wird dieses über New -> Code -> Code.
      Wenn das Programm fertig ist, soll ein Fenster geöffnet werden, welches entweder eine Fehlermeldung zeigt oder die Daten zusammenfasst. Deshalb lege ich zu Beginn eine Boolean-Variable fest:
      			bool mistake = false;
      		

      Und nun kann ich alles durchprüfen und setzte bei einem Fehler Variable auf "true".
    13. Es folgt die erste if-Anweisung. Sie überprüft, ob die Anrede zumindest technisch stimmt:
      			if (vfrau == vherr)
      				mistake = true;
      		
    14. Es folgt die zweite if-Anweisung. Sie überprüft, ob Vorname und Name eingetragen wurden:
      			if (strcmp("",vorname->value()) == 0 || strcmp("",nachname->value()) == 0)
      				mistake = true;
      			else
      			{
      				vvorname = (const char*)vorname->value();
      				vnachname = (const char*)nachname->value();
      			}
      		
      	
    15. Zuletzt überprüft man das Datum:
      Hier wird es nun interessanter, denn nun muss, man den Wert ermitteln, der eingegeben wurde. Das Drop-Down-Menü vergibt dazu Indexe, die man sich für die Monate so vorstellen muss:
      EintragIndex
      Januar0
      Februar1
      März2
      April3
      Mai4
      Juni5
      Juli6
      August7
      September8
      Otkober9
      November10
      Dezember11

      Diesen Index kann man nun im Falle der Monate mit "month->value()" abrufen, dabei verhalten sich die zurückgegebenen Werte wie normale, ganzzahlige, nichtnegative Werte. Wenn man also z.B. Mai auswählt, erhält man 4 und um 1 erhöht, hat man den korrekten Monat.
      Analog erfolgt es bei den Tagen.

      Und nun müssen mehrere Überprüfungen gemacht werden (auch hier sind alle negiert! Stichwort: De Morganische Gesetze):

      			vday = day->value()+1;
      			vmonth = month->value()+1;
      			sscanf (year->value(); "%i"; &vyear);
      
      
      			//Validierung von Tag und Monat
      			if (vday <= 0 || vday >= 32 || vmonth <= 0 || vmonth >= 13)
      				mistake = true;
      
      			//zu alte und noch nicht geborene Menschen, sollen außen vorgelassen werden
      			if (vyear > 2005 || vyear < 1900)
      				mistake = true;
      
      			//Überprüfung, ob ein Monat auch 31 Tage hat, falls der 31. Tag ausgewählt wurde (Februar separat)
      			if ( (vday == 31) && (vmonth == 4 || vmonth == 6 || vmonth == 9 || vmonth == 11) )
      				mistake = true;
      
      			//Überprüfung Februar inkl. Schaltjahr
      			if ( vmonth == 2 && vday > 28 )
      			{
      				if ( (vyear % 4 != 0 || vyear % 400 == 0) || vday > 29)
      					mistake == true;
      			}
      		
    16. Damit wären alle Werte eingelesen und jetzt muss entschieden werden, ob eine Zusammenfassung oder eine Fehlermeldung ausgegeben wird. Dies geschieht wieder über ein Codefragment:
      			if (mistake == false)
      				result()->show();
      			else
      				failure()->show();
      		

      Diese beiden Fenster müssen noch geschrieben werden. Für die Fehlermeldung kann man alternativ auch die Dialogmöglichkeiten von FLTK nutzen. Diese wurde aber leider in FLUID nicht integriert (außer man fügt sie manuell über Befehlszeilen ein) und deswegen will ich diesmal beide Fenster selbst implementieren.
    17. Dazu legt man wieder eine neue Funktion an mit den folgenden Parametern:
      • Zeile 1: result()
      • Zeile 2: Fl_Window*
    18. Was muss nun in dem Fenster gemacht werden?
      Unser Datum muss noch in eine ordentlich Form gebracht werden, d.h. Tag, Monat und Jahr müssen in einen ausgabefähigen String verpacken. Und natürlich muss eine Anzeige erfolgen.
    19. Zu Beginn die Verpackung des Datums: Dazu wird wieder ein Codefragment eingefügt und wie schon so oft ein Wert in einen Stream gepackt.
      			std::stringstream outstream;
      			std::string datum;
      
      			if (vday < 10)
      				outstream<<"0";
      			outstream<
      Die if-Anweisungen dienen dazu, dass einstellige Zahlen eine führende Null bekommen. So erhalten wir ein Datum der Form: dd.mm.yyyy.
    20. Es folgt die Ausgabe. Dazu fügt man über New -> Text -> Output ein neues Widget ein. Das Widget erhält das Label "Anrede:". In der Registrierkarte "Style" habe ich die "Label Font" auf "Helvetica Bold" gestellt. Außerdem habe ich "NO_BOX" im entpsrechenden Menü gewählt.
      Jetzt geht es in die Registrierkarte "C++": Bisher haben wir unter "Name" immer fröhlich globale Variablen definiert. Das machen wir jetzt nicht anders, nur dass die Variablennamen nun lokal angelegt werden. Dazu trage ich einen Namen ein, bei mir "showanrede" (Achtung: globale Variablen nicht verwenden). Durch Klicken auf den Button "public" neben dem Namen werden die Variablen lokal vereinbart (kein roter Punkt leuchtet).
      Die nächste Besonderheit kommt in die Zeilen "Extra Code:". Da es sich hier ausschließlich um eine Zusammenfassung handelt, deren (technische) Richtigkeit bereits überprüft wurde, kann man von vorherein Festlegen, was angezeigt werden soll. Diese Initialisierung eines Widgets mit einem solchen Wert kann über die Zeilen "Extra Code" erfolgen.
      Für die Anzeige der Anrede benötigen wir die ersten beiden Zeilen. Die ertse Zeile:
      			if (vfrau == true) showanrede->value("Frau");
      		

      Und die zweite Zeile:
      			if (vherr == true) showanrede->value("Herr");
      		
    21. Jetzt fügt man das nächste Feld ein (Zwischenablage nutzen, um die Stylevorgaben nicht neu eingeben zu müssen). Der "Name" in der C++-Registrierkarte lautet "showvorname" und der Extra-Code:
      			showvorname->value(vvorname);
      		

      Analog erfolgt die Ausgabe des Nachnamen. Hier lautet der C++-Name "shownachname" und der Extra Code:

      			shownachname->value(vnachname);
      		
    22. Kommen wir zum letzten Ausgabefeld, dem Datum. Dazu wird ein weiteres Ausgabefeld angelegt, der C++-Name lautet "showdatum". Zur Erinnerung: In der Variable "datum" ist das formatierte Datum. Diese Variable ist ein C++-konformer String. Über die Funktion "datum.c_str()" erhält man einen ausgabefähigen String.
      Damit ist klar, wie die Zeile im "Extra Code" heißt:
      			showdatum->value(datum.c_str());
      		

      Leider sieht man bei Eingabe in die Maske den Unterstrich nicht.
    23. Damit ist auch diese Oberfläche fertig. Damit die Funktion so arbeitet, wie sie soll, muss zum Schluss noch ein Code-Fragment eingefügt werden. Dieses wird bentigt, um die (manuelle) Rückgabe zu ermöglichen. Der Befehl, der eingetragen werden muss, lautet:
      			return w;
      		
    24. Nun noch die Fehlermeldung für falsche Eingaben: Dazu wieder eine Funktion mit dem diesen Werten:
      • Zeile 1: failure()
      • Zeile 2: Fl_Window*
    25. In dieser Funktion soll nicht weiter darauf eingegangen werden, was falsch war. Es genügt die Tatsache, dass ein Fehler gemacht worde.
      Um noch etwas neues bieten zu können, soll einfach nur diese Grafik angezeigt werden (Grafik kann frei genutzt werden):
    26. Dazu legt man wieder ein neues Fenster an. In diesen Fenster fügt man über New -> other -> Box eine Box ein. In der Registrierkarte "GUI" der Box klickt man auf "Browse" auf Höhe der Zeile "Image". Danach wählt man die zuvor lokal gespeicherte Grafik aus.
      Das Label lässt man leer. Die Höhe und Weite habe ich mit jeweils 100 Pixeln angegeben (entspricht den Ausmaßen der Grafik).
    27. Zum Abschluss der Funktion nooch die manuelle Rückgabe, d.h. Code-Fragment mit der Zeile:
      			return w;
      		
    28. Zum Abschluss noch die Klasseneinbindung, diesmal sind es diese:
      • #include
      • #include
      • using namespace std;

      Das letzte ist zwar keine Klasse, aber nützlich.
    29. Code schreiben, Kompilieren und Ausführen.

    Tutorial-4-2.fl Tutorial-4-2.h Tutorial-4-2.cpp