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.

Image non disponible
Avant de cliquer sur licenses.
Image non disponible
Après avoir cliqué sur licenses.

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.

Image non disponible

II. Implémentation

La façon la plus simple d'implémenter FileBrowser est de sous-classer QListBox et d'ajouter quelques fonctions membres :

 
Sélectionnez
    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 FileBrowser prend 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 et basePath contient le chemin du répertoire qui est affiché actuellement dans la liste.

 
Sélectionnez
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.

 
Sélectionnez
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.

 
Sélectionnez
    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.

 
Sélectionnez
    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 :

 
Sélectionnez
    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.