I. maSection▲
Le widget FileBrowser liste le contenu d'un répertoire. Si l'utilisateur double-clique sur le nom d'un sous-répertoire, le widget FileBrowser se met à jour avec le contenu du sous-répertoire (comme illustré ci-dessous). En double-cliquant sur le " .. ", l'utilisateur peut retourner au répertoire parent.
Cependant, si l'utilisateur sélectionne un nouveau fichier, le widget FileBrowser émet un signal qui est ensuite envoyé à l'application. Par exemple, la copie d'écran suivante montre une application qui combine le widget FileBrowser avec un QTextBrowser pour donner un simple navigateur HTML.
II. Implémentation▲
La façon la plus simple d'implémenter FileBrowser est de sous-classer QListBox et d'ajouter quelques fonctions membres :
class
FileBrowser : public
QListBox
{
Q_OBJECT
public
:
FileBrowser(const
QString
&
filter, QWidget
*
parent =
0
, const
char
*
name =
0
);
void
setDir(const
QString
&
path);
signals
:
void
picked(const
QString
&
fileName);
private
slots
:
void
itemHighlighted(int
index);
void
itemSelected(int
index);
private
:
QString
nameFilter;
QString
basePath;
}
;
Une autre solution aurait été de sous-classer QWidget et d'utiliser un attribut QListBox, mais cela aurait requis légèrement plus de code.
Le constructeur de FileBrowserprend un nom de filtre comme chaîne de caractères en plus des paramètres habituels parent et name de QWidget. Cette chaîne spécifie une liste de motifs de filtre (par exemple, " *.jpg *.gif *.png ") et FileBrowser n'affichera que les fichiers dont le nom correspond à ces filtres.
La méthode setDir() initialise le répertoire à partir duquel débutera la navigation. Le signal picked(const QString &) est émis lorsqu'une nouvelle entrée est sélectionnée, à moins que cela soit un répertoire. Dans la plupart des applications, ce signal devra être connecté sur un slot qui affichera le contenu du fichier.
Voici maintenant les deux slots privés. Ils sont connectés aux signaux de la classe QListBox highlighted(int) et selected(int). Le signal highlighted(int) est émis lorsque l'utilisateur crée un nouvel élément sur l'élément courant (par exemple, en cliquant une fois ou en appuyant sur les flèches du clavier), tandis que le signal selected(int) est émis seulement lorsque l'utilisateur double-clique sur un élément ou presse la touche Entrée. Pour le widget FileBrowser, on a besoin des deux - un clic sur un fichier est suffisant pour sélectionner le fichier, mais un double-clic (ou une pression sur la touche Entrée) est nécessaire pour entrer dans un répertoire.
Finalement, FileBrowser dispose de deux membres privés : nameFilter contient l'argument filter du constructeur etbasePath contient le chemin du répertoire qui est affiché actuellement dans la liste.
FileBrowser::
FileBrowser(const
QString
&
filter, QWidget
*
parent, const
char
*
name)
:
QListBox
(parent, name)
{
nameFilter =
filter;
setDir(QDir
::
currentDirPath());
connect
(this
, SIGNAL
(highlighted(int
)), this
, SLOT
(itemHighlighted(int
)));
connect
(this
, SIGNAL
(selected(int
)), this
, SLOT
(itemSelected(int
)));
}
Le constructeur enregistre le paramètre nameFilter pour une utilisation future. Ensuite, il appelle setDir() pour se peupler avec le contenu du répertoire de travail courant et connecte les deux signaux hérités de QListBox sur les slots privés appropriés de FileBrowser.
void
FileBrowser::
setDir(const
QString
&
path)
{
QDir
dir(path, nameFilter, QDir
::
DirsFirst);
dir.setMatchAllDirs(true
);
if
(!
dir.isReadable())
return
;
clear();
QStringList
entries =
dir.entryList();
QStringList
::
ConstIterator it =
entries.constBegin();
while
(it !=
entries.constEnd()) {
if
(*
it !=
"."
)
insertItem(*
it);
++
it;
}
basePath =
dir.canonicalPath();
}
La méthode setDir() utilise un objet QDir pour récupérer la liste des répertoires indiqués par path. Le paramètre nameFilter est alorspassé au constructeur de QDir ; ensuite, seuls les fichiers correspondant au motif sont retournés. Le drapeau DirsFirst est aussi passé en paramètre pour indiquer qu'il faut afficher les répertoires en premier dans la liste. La méthode setMatchAllDirs(true) est alors appelée pour demander à QDir de lister tous les sous-répertoires, plutôt que d'afficher juste les fichiers dont le nom correspond à l'un des filtres.
Avant de lire le répertoire, on s'assure que celui-ci existe et est accessible en lecture. Si le répertoire ne peut être lu, la méthode setDir() s'interrompt immédiatement, laissant la liste inchangée.
Ensuite, la liste QListBox est effacée et remplie avec le contenu du répertoire, retourné par QDir::entryList() - mais le répertoire " . " n'est pas affiché. Le répertoire courant est enregistré dans le membre privé basePath pour utilisation future.
void
FileBrowser::
itemHighlighted(int
index)
{
QString
path =
basePath +
"/"
+
text(index);
if
(QFileInfo
(path).isFile())
emit
picked(path);
}
Lorsqu'une entrée de la liste est surlignée (un seul clic), le slot itemHighlighted() est appelé avec l'index de l'entrée. Celui-ci est passé à la méthode QListBox::text() pour récupérer le nom du fichier. Un objet QFileInfo est utilisé pour détecter si l'entrée sélectionnée est un fichier, auquel cas un signal picked() est émis.
void
FileBrowser::
itemSelected(int
index)
{
QString
path =
basePath +
"/"
+
text(index);
if
(QFileInfo
(path).isDir())
setDir(path);
}
Lorsqu'une entrée de la liste est sélectionnée (double-clic), le slot itemSelected() est appelé avec l'index de l'entrée. Si l'entrée sélectionnée est un répertoire, FileBrowser se met à jour avec le contenu du répertoire.
III. Exemple d'utilisation▲
Voici maintenant un petit programme permettant d'utiliser cette classe :
int
main(int
argc, char
*
argv[])
{
QApplication
app(argc, argv);
QSplitter
win(Qt
::
Horizontal);
QString
filter =
"*.htm *.html *.txt *.xml"
;
FileBrowser *
fileBrowser =
new
FileBrowser(filter, &
win);
QTextBrowser
*
textBrowser =
new
QTextBrowser
(&
win);
QObject
::
connect
(fileBrowser, SIGNAL
(picked(const
QString
&
)),
textBrowser, SLOT
(setSource(const
QString
&
)));
app.setMainWidget(&
win);
win.show();
return
app.exec();
}
Ce programme positionne le widget FileBrowser sur le côté gauche et un QTextBrower sur le côté droit, avec un séparateur entre les deux. FileBrowser est initialisé avec un nom de filtre correspondant aux formats de fichiers supportés par QTextBrowser.
Une amélioration possible serait d'afficher les répertoires dans un style différent de celui des fichiers, pour que cela soit plus facile à distinguer. La classe QListBoxPixmap ou une sous-classe de QListBoxItem peuvent être utilisées pour présenter les répertoires.
IV. Remerciements▲
Je tiens à remercier Thibaut Cuvelier pour son aide à la traduction et Claude Leloup pour sa relecture.