Kylix kennt Standarddialoge für viele gängige Aktionen, etwa Dateien öffnen und speichern, Textpassagen suchen und ersetzen oder Schriftarten und Farben auswählen. Diese Komponenten sparen Programmierarbeit und sorgen für einheitlich bedienbare Software. Außerdem: Richtig verpackt laufen Ihre Programme auch auf fremden Desktops.
Zu den großen Errungenschaften der Desktop-Umgebungen wie KDE oder Gnome gehört, dass die Programme einheitlich aussehen und sich einheitlich verhalten (Look and Feel). Dazu gehören auch Dialogfenster. Vielleicht haben Sie sich als Anwender auch schon darüber geärgert, wie unterschiedlich sich der Datei-Öffnen-Dialog in älteren Programmen verhält.
Moderne Entwicklungsumgebungen unterstützen Sie als Programmierer dabei, dieses Chaos zu vermeiden und Aktionen einheitlich zu implementieren. Als Anwender werden Sie sich über ein bekanntes Fenster freuen, das Sie flott bedienen können, und als Programmierer sparen Sie sich überflüssige Schreibarbeit. Für die standardisierten Vorgänge wie das Auswählen von Farben oder das Speichern von Dateien bringt die Programmierumgebung passende Komponenten mit, siehe Abbildung 1.

Abbildung 1: Das Kylix-Register “Dialoge” enthält die Komponenten für Datei öffnen und speichern, Schriftart, Farbe, Suchen und Ersetzen
Leider realisieren diese Komponenten nur die GUI-Funktionalität und liefern Werte, die das Programm dann selbst weiter verarbeiten muss. Beispielsweise liefert der Datei-Öffnen-Dialog nur die URL der vom Benutzer ausgewählten Datei. Die Datei wird aber nicht automatisch geöffnet. Hier ist Handarbeit notwendig.
Den Dialog starten
Dialogkomponenten sind allesamt nicht-visuell. Im Designfenster ist nur ein Symbol auf der Form zu sehen. Das Dialogfenster soll schließlich nicht immer sichtbar sein, sondern nur auf Anforderung Ihres Programms. Zur Laufzeit öffnet sich die Dialogkomponente, wenn Ihr Programm die Methode Execute() aufruft – etwa als Reaktion, wenn der Benutzer auf einen Button klickt. Diese Methode ist bei allen Komponenten vorhanden.
Die Execute-Methode bleibt so lange aktiv, bis der User den Dialog wieder schließt. Erst dann kehrt sie zurück und liefert als Rückgabewert True, wenn der Benutzer die Bestätigungsschaltfläche gewählt hat und False, wenn er im Dialogfeld Abbrechen selektiert hat. Sie können auch aus dem Programm selbst jedes Dialogfenster wieder schließen, dazu ist die Methode CloseDialog() vorhanden.
Dialogkomponenten lassen sich auf vielfältige Weise konfigurieren. Beispielsweise verfügen alle über die Eigenschaft Options. Mit ihr können Sie das Aussehen und die Funktionsweise der Komponente steuern. Um auf die Optionen zuzugreifen, klicken Sie im Objektinspektor doppelt auf Options (oder Einfachklick auf das Pluszeichen), siehe Abbildung 2. Zur Laufzeit können Sie mit den Eigenschaften Handle, Left, Top und Position die Position des Dialogfelds ändern.

Abbildung 2: Der Objektinspektor der “OpenDialog”-Komponente bietet im “Options”-Menü mehrere Eigenschaften, die das Aussehen und Verhalten der Komponente steuern
Datei, öffne dich
Die OpenDialog-Komponente implementiert den Standarddialog zum Öffnen von Dateien, der alle üblichen Zusatzfunktionalitäten mitbringt. Um dieses Dialogfenster in Ihr Programm einzubinden, ziehen Sie aus dem Dialogs-Register (Abbildung 1) die passende Komponente (zweiter Button von links) auf Ihre Form.
Wenn Ihr Programm zur Laufzeit die Execute()-Methode der Komponente aufruft, öffnet sich das Dialogfenster (Abbildung 3) und der Benutzer kann den Namen der zu öffnenden Datei eingeben oder sie per Maus auswählen. Wenn er dann den OK-Button drückt, kehrt die Methode mit dem Rückgabewert True zurück. Welchen Dateinamen der Anwender gewählt hat, erfahren Sie über die Eigenschaft Filename der Datei-Öffnen-Komponente.

Abbildung 3: Diesen bekannten Datei-Öffnen-Standarddialog können Sie bequem in eigenen Programmen verwenden
Der Dialog verfügt über die üblichen Funktionalitäten: Der Anwender kann Dateien und Verzeichnisse sortieren, zwischen verschiedenen Ansichten wählen und neue Verzeichnisse anlegen.
Die SaveDialog-Komponente ist das passende Gegenstück, sie stellt ein Standarddialogfeld zum Speichern von Dateien zur Verfügung. Beide Dialoge realisieren nur die Auswahl der Datei-URL. Alle weiteren Schritte, die zum Öffnen und Speichern nötig sind, müssen Sie manuell im Quelltext durchführen. Tabelle 1 listet wichtige Pascal-Operationen für das Öffnen und Speichern von Dateien.
Tabelle 1: Dateioperationen in Pascal
| Operation | Erklärung |
|---|---|
| AssignFile | Weist den Namen einer Datei einer Dateivariablen zu. |
| Eof | Prüft, ob das Dateiende erreicht wurde. |
| FilePos | Gibt die aktuelle Dateiposition zurück. |
| IOResult | Liefert den Status der letzten Ein/Ausgabe-Operation. |
| BlockRead | Liest einen oder mehrere Datenblöcke aus einer geöffneten Datei in eine Variable ein. |
| BlockWrite | Schreibt einen oder mehrere Datenblöcke aus einer Variablen in eine geöffnete Datei. |
| Reset | Öffnet eine vorhandene Datei, um aus ihr zu lesen. |
| Rewrite | Erstellt eine neue Datei und öffnet sie anschließend, um in sie zu schreiben. Ist das File schon vorhanden, wird es vorher gelöscht. |
| Write | Schreibt Daten in eine Datei. |
Praktische Fenster
Eine weitere Dialogkomponente ist FontDialog. Der Benutzer kann Schriftart, -größe und -format festlegen, siehe Abbildung 4. Ihr Programm erfährt über die Eigenschaft Font, welche Schrift der Anwender wünscht. Über eine ColorDialog-Komponente können Sie den Anwender auch eine Farbe wählen lassen (Abbildung 5). Das Ergebnis steht in der Color-Eigenschaft.
Mit den FindDialog– und ReplaceDialog-Komponenten lassen Sie den Anwender suchen. Die Dialoge enthalten die üblichen Einstellungsoptionen, etwa Auf- oder Abwärtssuche, ganzes Wort, Groß- und Kleinschreibung beachten. Welchen Suchstring der Anwender eingegeben hat, erfährt Ihr Programm aus der Eigenschaft FindText. Beim Ersetzen-Dialog gibt der User noch einen Ersatzstring ein (Eigenschaft ReplaceText).
Wie auch beim Dateimanagement sind die Suchen- und Ersetzen-Dialoge zwar gut geeignet, um dem Anwender standardisierte Eingabefelder zu geben, das Suchen und das Ersetzen müssen Sie aber von Hand programmieren. Per Event-Methode onFind und onReplace erfahren Sie, wann der User den nächsten Treffer wünscht.
Praxisbeispiel: Mini-Editor
Auf der Heft-CD finden Sie als Kylix-Projekt einen Mini-Editor, bei dem der Anwender Text eingeben und als Datei speichern, beliebige Textdateien öffnen, im angezeigten Text suchen sowie Schrift und Hintergrundfarbe des Anzeigebereichs ändern kann. Das Programm verwendet die besprochenen Dialogkomponenten, ist darüber hinaus aber recht unspektakulär.

Abbildung 6: Der Mini-Editor in der Designansicht (im so genannten Form). Links unter den Buttons sind die Dialogkomponenten abgelegt
Wenn Sie das Beispiel von Hand erstellen wollen, erzeugen Sie ein neues Projekt und fügen die Schaltflächen, Dialog-Komponenten, Label und das Memofeld ein. Ordnen Sie diese so an, wie Abbildung 6 zeigt und beschriften Sie die Buttons mit Öffnen, Speichern unter, Neu, Hintergrundfarbe, Textformat, Suchen und Ende.
Ein Label erhält die Caption-Eigenschaft Aktuelle Datei:, beim zweiten Label bleibt diese Eigenschaft vorerst leer. Die Caption-Eigenschaft des Formulars darf beliebig lauten, im Beispiel trägt sie den Wert Mini-Editor.
Um die folgenden Quelltextpassagen in Kylix einzugeben, öffnen Sie die jeweiligen Schablonen per Doppelklick auf das Element in der Form. Achtung: die Nummern der Schaltflächen können bei Ihnen von den hier verwendeten abweichen.
Wenn das Programm startet, leert es zunächst das Memofeld:
procedure TForm1.FormCreate(Sender: TObject); begin Memo1.Text:=''; end;
Erste Hürde: Datei öffnen
Der komplexeste Vorgang ist das Öffnen einer vorhandenen Datei, da das Programm deren Größe nicht kennt und daher die Anzahl der zu lesenden Zeichen zur Laufzeit bestimmt. Die Dialogkomponente benennt via OpenDialog1.FileName, welches File das Programm lesen soll. Eine repeat-Schleife liest mit BlockRead solange den Inhalt der Datei in ein Char-Array, bis sie das Ende der Datei erreicht.
Der Lesevorgang ist in zwei Passagen unterteilt, siehe Listing 1. Solange das Programm einen vollständigen Block mit 100 Zeichen lesen konnte, fügt es diesen an den Inhalt des Memofeldes an. Kurz vor dem Ende der Datei werden in der Regel beim letzten Lesevorgang weniger als 100 Zeichen eingelesen. Dann liest eine weitere Schleife die Zeichen einzeln und fügt sie in das Memofeld ein.
Listing 1
Datei öffnen
procedure TForm1.Button1Click(Sender: TObject);
var
F: TextFile;
S: string;
Quelle:file;
i,AnzahlLesen: Integer;
Buf: array[1..100] of Char;
begin
if OpenDialog1.Execute() then
begin
Label2.caption:= OpenDialog1.FileName;
// Leeren des Memofeldes
Memo1.Text:='';
try
// Zuordnung File aus dem Dialog
AssignFile(Quelle, OpenDialog1.FileName);
//Öffnen der Datei
Reset(Quelle,1);
repeat // äußeres repeat-Schleife
BlockRead(Quelle, Buf, SizeOf(Buf), AnzahlLesen);
if(AnzahlLesen=SizeOf(Buf)) then
begin
Memo1.Text := Memo1.Text + Buf;
end
else if(AnzahlLesen>0) then
begin
i:=0;
repeat // innnere repeat-Schleife
Memo1.Text := Memo1.Text + Buf[i];
i:=i+1;
until(i>AnzahlLesen); // Ende innere repeat-Schleife
end;
until (AnzahlLesen = 0) ; // Ende äußere repeat-Schleife
//Schließen der Datei
CloseFile(Quelle);
except // Behandeln von Ausnahmen
on EInOutError do
Application.MessageBox('Fehler beim Lesen der Datei', 'Stopp', [smbOK]);
end;
end;
end;
Das Öffnen des OpenDialog wird mit einer if-Abfrage getestet. Falls der Anwender den Dialog abbrechen und damit die Rückgabe eines Datei-URLs verhindern wird, gibt der Dialog den Wert False zurück. Folglich wird der nachfolgende begin-end-Block übersprungen und das Öffnen einer Datei unterbunden.
Die Zuweisung Label2.caption:= OpenDialog1.FileName; sorgt dafür, dass die Datei-URL im zweiten Label auf dem Formular angezeigt wird. Wenn Sie das Listing weiter untersuchen, wird Ihnen ein Konstrukt try … except … end; auffallen. Es stellt sicher, dass das Programm einen Fehler beim Öffnen oder Schreiben der Datei qualifiziert abfängt und nicht abstürzt oder hängen bleibt.
Die so genannte Ausnahme (Exception), die im Problemfall auftritt, wird im except-Abschnitt behandelt: Das Programm zeigt mit Application.MessageBox eine Fehlermeldung und läuft anschließend weiter.
Speichern unter
Auch beim Speichern kommt ein Standard-Dialog zum Einsatz. Das Schreiben der Datei ist einfach: Write überträgt den gesamten Inhalt des Memofeldes in die Datei (Listing 2). Wieder sorgt ein try-except-Konstrukt für die Ausnahmebehandlung.
Listing 2
Datei schreiben
procedure TForm1.Button2Click(Sender: TObject);
var
Ziel:Textfile;
begin
if SaveDialog1.Execute() then
begin
try
// Zuordnung File aus dem Dialog
AssignFile(Ziel, SaveDialog1.FileName);
//Öffnen der Datei
Rewrite(Ziel);
Write(Ziel, Memo1.text);
//Schließen der Datei
CloseFile(Ziel);
except
on EInOutError do
Application.MessageBox('Fehler beim Lesen der Datei', 'Stopp', [smbOK]);
end;
end;
end;
Farbe und Font
Die Hintergrundfarbe des Memofeldes lässt sich mit dessen Color-Eigenschaft ändern. Dieser Eigenschaft können Sie direkt den Color-Rückgabewert der ColorDialog-Komponente zuweisen. Der Test über ein if sorgt dafür, dass der Anwender den Dialog abbrechen und die Zuweisung verhindern kann:
if ColorDialog1.Execute() then Memo1.Color:=ColorDialog1.Color;
Für die Schriftart des Memofeldes ist dessen Font-Eigenschaft zuständig. Die neue Schrift stammt vom Font-Rückgabewert der FontDialog-Komponente:
if FontDialog1.Execute() then Memo1.Font:=FontDialog1.Font;
Suchen, Neu und Ende
Den Suchdialog öffnen Sie per FindDialog1.Execute();. Die Suchaktion, die der Benutzer im Suchdialogfenster startet, müssen Sie selbst programmieren. Listing 3 zeigt eine einfache Implementierung: Als Reaktion auf den Suchen-Button im Suchen-Dialogfenster fahndet der Code per FindText nach einer passenden Stelle. Implementiert ist nur Case-Sensitive-Suche, sprich das Programm beachtet immer die Groß-Kleinschreibung. Den ersten Treffer markiert der Code mit SelStart und SelLength.
Listing 3
Suchen
procedure TForm1.FindDialog1Find(Sender: TObject);
var
SelPos: Integer;
begin
with TFindDialog(Sender) do
begin
{ Suche case-sensitive nach dem Wert von FindText in Memo1 }
SelPos := Pos(FindText, Memo1.Text);
if SelPos > 0 then
begin
Memo1.SelStart := SelPos - 1;
Memo1.SelLength := Length(FindText);
end
else
MessageDlg(Concat('Gesuchter String "', FindText, '" wurde nicht gefunden.'), mtError, [mbOk], 0);
end;
end;
Das Erstellen eines neuen Textes ist wieder sehr einfach. Setzen Sie den Inhalt des Memofeldes auf einen leeren String: Memo1.Text:=”;. Um das Programm zu beenden genügt eine einfache Funktion: Application.Terminate;.
Das vollständige Projekt finden Sie auf der Heft-CD. Sie haben nun einen einfachen, aber funktionstüchtigen Editor zum Ansehen und Bearbeiten von Textdateien, den Sie auf Ihrem Rechner innerhalb der Kylix-IDE laufen lassen können. Um den Mini-Editor auch ohne Kylix-Umgebung auszuführen, sind noch ein paar Schritte nötig.
Depoyment
Das Verteilen und Installieren von Applikationen auf fremden Rechner ist immer eine Herausforderung. Im einfachsten Fall ist auf dem Zielrechner ebenfalls Kylix installiert. Sie können dann entweder den Quelltext samt aller notwendigen Dateien im Projektordner weitergeben und auf dem Zielrechner das Projekt innerhalb der Kylix-IDE kompilieren und ausführen.
Eine ähnliche Vorgehensweise ist unter Linux mit C/C++-Projekten üblich. Der Anwender übersetzt den Quelltext mit dem Dreischritt ./configure && make && make install. Es ist aber eher unwahrscheinlich, dass Kylix auf dem Zielsystem vorhanden ist. Sie müssen die Applikation also selbst kompilieren und das Ergebnis weitergeben.
Eine Kylix-Applikation benötigt allerdings eine ganze Reihen an Bibliotheken und gemeinsamen Objekten. Je komplexer Ihre Kylix-Applikation ist, desto mehr Dateien braucht sie auf der Zielplattform. Bei einfachen Programmen wie dem Mini-Editor ist das noch überschaubar, Sie müssen die Dateien aber mit Ihrer Applikation ausliefern.
Grafische Applikationen benötigen auf jeden Fall die Bibliothek libqtintf-6.5-qt2.3, zusätzlich oft libqt2.3 (mit Kylix installiert). Welche Bibliotheken Ihrem Programm fehlen, gibt es beim Start selbst aus. So führt ./Minieditor zu folgender Meldung:
libborqt-6.9-qt2.3.so: cannot open shared object file: Datei oder Verzeichnis nicht gefunden
Diese Bibliothek findet sich im Kylix-Installationsverzeichnis, auf dem Testrechner war das /usr/local/kylix3/bin/libborqt-6.9-qt2.3.so.
Pfad zu den Bibliotheken
Auf der Suche nach fehlenden Bibliotheken geht ein Linux-Programm die Verzeichnisse durch, die in der Umgebungsvariablen LD_LIBRARY_PATH spezifiziert sind. Danach sucht es eine Cache-Datei mit Namen /etc/ld.so.cache und dann in einer Liste, die in der Textdatei /etc/ld.so.conf steht. Wenn die Bibliothek bis dahin nicht lokalisiert wurde, durchsucht der dynamische Linker das /usr/lib-Verzeichnis.
Sie haben also drei Methoden zur Wahl, um dafür zu sorgen, dass Ihr Programm die benötigten Bibliotheken findet: * Die LD_LIBRARY_PATH-Umgebungsvariable setzen * Eintrag in /etc/ld.so.conf ergänzen * Bibliotheken in /usr/lib ablegen
Für den Mini-Editor ist LD_LIBRARY_PATH die beste Lösung. Sie gibt Ihnen die meiste Kontrolle darüber, wo sich Dateien befinden und braucht keine Root-Privilegien. Ein kleines Startup-Skript (Listing 4) kümmert sich um diese Aufgabe.
Dieses Skript erwartet die binäre Datei in einem bin-Verzeichnis und die zu ladenden Bibliotheken in einem lib-Directory. Ersetzen Sie das passende Installationsverzeichnis und den Namen der Anwendung und kopieren die benötigen Libryries in das lib-Verzeichnis. Um das Skript auszuführen, müssen Sie die Datei noch ausführbar machen: chmod a+x Skriptdatei. Sie können die Verzeichnisstruktur aus Start-Skript, bin und lib nun auf fremden Rechnern installieren und den Mini-Editor dort benutzen.
Listing 4
Startup-Skript
#!/bin/bash # Hier befindet sich die Applikation: app_install_dir=/hier/liegt/das/Verzeichnis app_path=$app_install_dir/bin/Minieditor app_ld_path=$app_install_dir/lib # Zuerst nach LD_LIBRARY_PATH-Variable suchen if [ -n "$LD_LIBRARY_PATH" ]; then # Gefunden: Weiteren Pfad anhängen LD_LIBRARY_PATH="$app_ld_path:$LD_LIBRARY_PATH" else # Nicht gefunden: Neu erstellen export LD_LIBRARY_PATH="$app_ld_path" fi # Sicherstellen, dass etwas für die LANG- # Umgebungsvariable eingetragen ist: if [ -z "$LANG" ]; then # Setze LANG auf einen passenden Wert export LANG=C fi # Anwendung ausführen $app_path $*
Der Autor
Ralph Steyer begann seine die Computerkarriere mit einem ZX81, den während des Mathematikstudiums ein Atari ST ablöste. Er arbeitet heute als Freelancer, Programmierer, EDV-Dozent und Fachautor. Zu erreichen ist er unter http://www.rjs.de.
Glossar
-
GUI
-
Das Graphical User Interface ist der sichtbare Teil eines Programms, mit dem der Benutzer per Maus und Tastatur Daten eingibt oder Texte liest.
-
Komponenten
-
Nach dem Baukastenprinzip fasst Kylix Oberflächenelemente, Dialoge und andere komplexe Gebilde zu Komponenten zusammen. Wie Bauklötzchen lassen sich Komponenten miteinander verbinden und als eine Einheit benutzen.
-
Form
-
Die Arbeitsfläche der Kylix-Oberfläche enthält unter anderem den Formulardesigner, mit dem Sie die Elemente der Oberfläche per Maus zusammensetzen können.
-
Exception
-
Trifft sie auf einen ungültigen internen Zustand, den sie nicht selbst behandelt kann, wirft eine Routine eine Exception. Der Code, von dem die Routine aufgerufen wurde, muss auf diese Exception geeignet reagieren. Exceptions können Fehler beim Leser einer Datei sein, etwa fehlende Rechte, oder eine Division durch Null.
Infos
[1] Kylix-Grundlagen, Installation und erste Schritte: LinuxUser 10/2002, S. 74 ff.
[2] Kylix-Projekte, Dateitypen, Objekte, Eigenschaften und Ereignisse: LinuxUser 01/2003, S. 78 ff.
[3] Object Pascal, Hauptprogramm und Units, Prozeuderen, Funktionen und Methoden: LinuxUser 04/2003, S. 72 ff.
[4] GUI-Programmierung aus dem Baukasten, Objektorientierung, Oberflächen-Komponenten: LinuxUser 11/2003, S. 83 ff.







