3. Open- und Save-File Dialoge

In diesem Beitrag geht es um Öffnen und Speichern Dialoge. Qt stellt dafür zwei Möglichkeiten bereit. Entweder man kreiert sich sein QFileDialog Objekt selbst, oder verwendet die vorgefertigten statischen Objekte. Ich stelle hier beide Möglichkeiten vor. Unser öffnen Dialog wird mittels eines QPushButton aufgerufen und die Rückgabe aus dem Dialog (der Pfad samt Datei) wird in ein LineEdit geschrieben. Auf die Signal/Slot Verbindung wie der QPushButton mit unserem Slot openFile() verknüpft wird, gehe ich hier nicht mehr ein, da ich dies schon in einem früheren Beitrag beschrieben habe. Beginnen wir mit dem leichteren, den Static Public Members.

Öffnen Dialog einzelner Files - Static Public Member

void MeinWidget::openFile()
{
QString initialDir = LineEdit->text();

if(initialDir.isEmpty())
initialDir = QDir::homePath();

QString InputFile = QFileDialog::getOpenFileName(
this,
"open file dialog",
initialDir,
"video (*.avi *.mpg *.flv);;Bitmaps (*.bmp);;All Files(*.*)");

if(!InputFile.isEmpty())
LineEdit->setText(InputFile);
}

Die erste Zeile versucht ein eventuell schon früher geöffnetes Verzeichnis aus unserem LineEdit zu holen und der Variablen initialDir zuzuweisen. Schlägt das fehl, weil wir bisher kein Verzeichnis geöffnet haben, wird mittels QDir::homePath() der Heimatpfad der Variablen zugewiesen. Das Startverzeichnis muß später bei unserem Öffnen-Dialog angegeben werden und auf diese Weise befindet man sich immer im zuletzt geöffnetem Verzeichnis damit man bei jedem Aufruf des Dialogs das zuletzt gewählte Verzeichnis nicht neu anwählen muß. Das erleichtert die Bedienung immens, wenn man lediglich im selben Verzeichnis eine andere Datei wählen möchte. Anschließend kommt auch schon unser QFileDialog, den ich der Übersichtlichkeit halber untereinander geschrieben habe. In der Variablen QString InputFile wird später unsere File-Angabe samt Pfad gespeichert. Unser Static Public Member getOpenFileName stellt uns einen Dialog zum öffnen eines einzelnen Files zur Verfügung. Für mehrere Files gibt es z.B. getOpenFileNames. Dann muß allerdings die Variable für die Dateinamen eine QStringList sein. Siehe hierzu auch die Qt-Referenz für die einzelnen Members. Gleich als Erstes folgt der Zeiger auf unser Qt-Haupt-Objekt, gefolgt vom Fenstertitel unseres Open-Dialogs. Danach folgt die Angabe des Verzeichnisses in dem sich unser Dialog befinden soll, welche wir ja zuvor ermittelt haben. Nun kommt die File-Filter Angabe, damit nur Files angezeigt und ausgewählt werden können, die dem Muster entsprechen. So kann man zum Einen eventuell verhindern, daß ein falsches File geöffnet wird und zum Anderen erleichtert es die Suche nach dem File. Die Zusammensetzung ist simpel. Eine Filterangabe besteht aus einer Bezeichnung, z.B. video und den entsprechenden Masken, z.B. *.zip, welche in Klammern dahinter geschrieben werden. Möchte man mehrere verschiedene Filter zur Auswahl haben, trennt man diese mit doppeltem Semikolon. Im Dialog erscheinen sie als Pull-Down-Auswahl untereinander. Das war's auch schon mit der Definition unseres Öffnen-Dialogs. Um ganz sicher zu gehen, daß uns unser Dialog auch einen Wert geliefert hat, fragen wir mit isEmpty() ab, ob die Variable InputFile auch wirklich einen Inhalt hat. Ist die Variable nicht leer wird das Ergebnis in unser LineEdit geschrieben.

Auswahl eines Ordners zum Speichern - Static Public Member

void MeinWidget::saveDir()
{
QString initialDir = LineEdit->text();

if(initialDir.isEmpty())
initialDir = QDir::homePath();

QString SaveDir = QFileDialog::getExistingDirectory(
this,
"open Save Directory",
initialDir,
QFileDialog::DontConfirmOverwrite);

if(!SaveDir.isEmpty())
LineEdit->setText(SaveDir);
}

Mit diesem Static Public Member wird ein Ordner zum speichern ausgewählt. Der Aufbau ist ähnlich wie beim Öffnen-Dialog. Hier ist allerdings die vierte Angabe keine Filter-Angabe, sondern eine Option um das Verhalten des Dialogs zu modifizieren. In unserem Fall wird mit QFileDialog::DontConfirmOverwrite angewiesen, daß keine Sicherheitsabfrage beim überschreiben der Dateien erscheint. Diese wäre bei der Auswahl eines Ordner sinnlos, da man den Ordner ja nicht überschreibt, sondern in ihn hineinspeichert. Der Rest ist genau wie schon beim Öffnen-Dialog.

Öffnen Dialog für mehrere Files mit QFileDialog Objekt

void MeinWidget::openFiles()
{
QString initialDir = LineEdit->text();

if(initialDir.isEmpty())
initialDir = QDir::homePath();

QFileDialog openDialog(this, tr("Open File(s)"));
openDialog.setAcceptMode(QFileDialog::AcceptOpen);
openDialog.setFileMode(QFileDialog::ExistingFiles);
openDialog.setLabelText(QFileDialog::FileName,tr("File(s)"));
openDialog.setDirectory(initialDir);
openDialog.setFilter(tr("flash-video (*.flv);;All Files(*.*)"));
openDialog.setViewMode(QFileDialog::List);

QStringList InputFiles;
if(openDialog.exec())
InputFiles = openDialog.selectedFiles();

if(!InputFiles.isEmpty())
LineEdit->setText(InputFiles[0]);
}

Wie man auf den ersten Blick sieht, ist es ein wenig aufwendiger einen eigenen Öffnen-Dialog mittels eines QFileDialog-Objekts zu erstellen und braucht daher ein paar Zeilen mehr Code. Zuerst erstellen wir unser Objekt und nennen es sinnigerweise openDialog. Die Angabe in Klammern veweist wieder auf unser Haupt-Objekt und danach kommt der Fenstertitel unseres Dialogs. Mit Hilfe des AcceptMode bestimmen wir um welche Art Dialog es sich handelt, ob es ein Öffnen- oder Speichern-Dialog wird. Mittels setFileMode wird bestimmt, was geöffnet wird. In unserem Fall mehrere, existierende Files, die wir später in eine QStringList schreiben. Soweit die Theorie laut Qt-Reference. In der Praxis hat sich jedoch gezeigt, daß beide Werte richtig zusammengesetzt werden müssen, damit das gewünschte Ergebnis erzielt wird. Einen Save-File-Dialog bekommt man z.B. nur dann, wenn man setFileMode auf AnyFile setzt. Mit diesem FileMode bestimmt der AcceptMode die Art des Buttons, ob Open oder Save. Stellt man den FileMode auf ExistingFile ist es egal, welche Angabe ich unter dem AcceptMode mache, es erscheint immer ein Open-File-Dialog, so wie bei der Angabe DirectoryOnly immer ein Verzeichnis-auswählen-Dialog entsteht. Zumindest haben das meine Versuche unter KDE 3.5 so gezeigt. setLabelText bestimmt den Text neben dem Eingabefeld für die Dateinamen. Mit setDirectory bestimmen wir wieder unser Startverzeichnis und mit setFilter die Anzeigefilter für die File-Art. Interessant ist noch die Angabe setViewMode welche das Aussehen der Auflistung unserer Files bestimmt. Hier gibt es zwei mögliche Angaben, Detail und List. Wie es die Namen schon verraten, ist Detail für die Detailansicht und verrät Zusatzinformationen zu den Files und List ist für die Listenansicht. Mittels der if-Schleife if(openDialog.exec()) wird Schlußendlich unser ÖffneFiles-Dialog gestartet und der Befehl selectedFiles übergibt sämtliche, ausgewählte Dateien inkl. Pfad der QStringList. Interessant ist, daß selectedFiles für alle Übergaben zuständig ist, sei es jetzt einzelne Dateien oder Ordner und als Variable auch nur eine QStringList und kein QString aktzeptiert. Das bedeutet, daß man bei einem einzelnen File auch zur QStringList greifen muß, auch wenn diese natürlich nur einen File-Namen erhält. Um nun einen einzelnen Eintrag unserer QStringList einem LineEdit zu übergeben, muß man sich des Array-Bezeichners bedienen. Im obigen Beispiel habe ich die Übergabe der QStringList sehr vereinfacht dargestellt, da von unserer Liste von Files lediglich das Erste in unser LineEdit geschrieben wird. Dies ist nur einer der vielen Nachteile, wenn man den Dialog selbst schreibt. Aber es wird noch besser (im ironischen Sinne)...

Speichern Dialog mit nur Ordnerauswahl mit QFileDialog Objekt

void MeinWidget::saveDir()
{
QString initialDir = LineEdit->text();

if(initialDir.isEmpty())
initialDir = QDir::homePath();

QFileDialog saveDialog(this, tr("Save in Folder"));
saveDialog.setAcceptMode(QFileDialog::AcceptSave);
saveDialog.setFileMode(QFileDialog::DirectoryOnly);
saveDialog.setLabelText(QFileDialog::FileName,tr("Folder"));
saveDialog.setDirectory(initialDir);
saveDialog.setViewMode(QFileDialog::List);

QStringList SaveDir;
if(saveDialog.exec())
SaveDir = saveDialog.selectedFiles();

if(!SaveDir.isEmpty())
{
if(!SaveDir[0].endsWith("/"))
SaveDir[0]=SaveDir[0]+"/";

LineEdit->setText(SaveDir[0]);
}
}

Auf den ersten Blick sieht unser Dialog ziemlich gleich aus wie der Öffnen-Dialog. Etwas weiter unten fällt aber eine zusätzliche if-Abfrage auf wegen des Pfadtrenners. Wiederum interessant beim eigenen QFileDialog-Objekt ist, daß bei bestimmten Ordnerauswahlen beim letzten Verzeichnis der Pfadtrenner mitkommt, bei anderen Auswahlen nicht. Klickt man in der Drop-Down-Liste der History den Pfad an, kommt der Trenner mit, bei allen anderen auswahlen nicht. Übrigens unter squeeze kommt er grundsätzlich nicht mit, nicht mal wenn man einen Static Public Member verwendet, was wiederum unter Debian lenny schon der Fall ist. Daher empfehle ich diese zusätzlich Pfadtrenner-Abfrage bei allen Ordner-öffnen-Dialogen um Probleme zu umgehen. Wer jetzt noch nicht genug hat vom eigenem FileDialog, der wird am letzten Problem seine helle Freude haben. Während man bei den Static Public Members Optionen wie z.B. DontConfirmOverwrite mitgeben kann, ist dies bei einem QFileDialog-Objekt unter Qt 4.3 schlicht und ergreifend nicht möglich, da die dafür benötigte Funktion setOption erst ab Qt 4.5 eingebaut wurde. In diesem Sinne ist meine Empfehlung eindeutig: auch wenn ein QFileDialog-Objekt die Möglichkeit einräumen würde die Schriftgröße selbst zu bestimmen, ist es wesentlich leichter, besser und einfacher die statischen Varianten für einen FileDialog zu verwenden.