P
'
t
i
t
e
C
h
a
t
t
e
 
spacer~ THERE'S NO PLACE LIKE 127.0.0.1 Articles | Connexion
 
~Un navigateur de fichiers

Précédent  
  Python  
  Suivant
 Présentation

Grâce au développement d'un éditeur de texte léger, nous avons appris le mois dernier à manipuler quelques composants avancés de PyQT. Aujourd'hui, nous partirons sur de nouvelles bases et réaliserons un navigateur de fichiers.
 Sommaire


 Introduction

Avant de rédiger la première ligne de code, nous devons étudier nos besoins. L'utilitaire PyViewer que nous désirons créer doit permettre de naviguer dans un système de fichiers pour permettre à l'utilisateur d'afficher le contenu de certains documents. Concernant la navigation, le composant le plus approprié se révèle être un arbre. Nous souhaitons également que lorsque l'utilisateur sélectionne un fichier, le contenu de celui-ci soit affiché de manière agréable. PyViewer doit être en mesure de présenter le contenu d'images, de pages HTML, de fichiers textes et même d'archives ZIP.


 Les arbres

Certaines bibliothèques de composants graphiques offrent un composant dédié à la représentation d'un arbre. C'est le cas par exemple de la bibliothèque Swing et de sa classe JTree. QT possède une approche différente : un arbre correspond à une instance de la classe QListView. Celle-ci peut également se voir employée pour afficher des listes sur une ou plusieurs colonnes. En réalité, les arbres sont considérés par QT comme des listes spéciales; les enfants d'un noeud particulier définissent chacun une ligne (ou un élément) de la liste.

Dans le code source de notre exemple, la constitution de l'arborescence repose sur deux classes File et Directory ainsi que la variable explorer, instance de QListView. Le listing 1 présente les quelques lignes de code relatives à cette dernière. Comme à l'accoutumée, le paramètre du constructeur désigne le conteneur parent du composant. La seconde ligne de ce listing se révèle bien plus intéressante car nécessaire au fonctionnement du logiciel. Par défaut, une liste QT ne contient aucune colonne, or toutes les données doivent prendre la forme de cellules d'une colonne. Ce code en crée une puis cache son en-tête, qui contient le titre de la colonne. Le "TreeStepSize" permet au programmeur de définir l'indentation, en pixels, des enfants par rapport à leur parent. Pour terminer, le logiciel crée une instance de Directory servant de racine de l'arbre.

Tout élément d'un tel composant correspond à la classe QListViewItem. Aussi, nos deux classes Directory et File dérivent-elles de celle-ci. Le constructeur de la première recèle une petite subtilité reproduite par le listing 2. Dans le cas général, le parent d'un répertoire est un répertoire. Toutefois, le premier répertoire, la racine, ne possède pas de tel parent. Nous lui substituons le composant QListView lui-même. Ce petit code source vérifie simplement la nature du parent. Si ce dernier n'est autre que la liste, alors nous représentons la racine du disque dur, dont le chemin logique est "/".

Parcourir l'intégralité du disque d'un seul coup est impensable. C'est pourquoi le programme ne parcoure le contenu d'un répertoire que lorsque l'utilisateur le déroule. Développer ou fermer un noeud de l'arbre entraîne l'invocation de la méthode setOpen() dont le paramètre booléen précise la nature de l'action. En surchargeant cette méthode nous allons implémenter un petit algorithme qui vérifie le contenu du répertoire courant et construit les objets appropriés (File ou Directory). Le listing 3 expose comment faire usage de la bibliothèque QT pour lister un répertoire.

Outre la méthode setOpen(), notre classe surcharge quatre autre méthodes de QListViewItem : key(), pixmap(), setup() et text(). La première doit retourner une clé dont la valeur se trouve employée lors du tri des éléments de la liste. Nous avons opté pour un tri présentant les répertoires avant les fichiers, ces derniers étant ordonnés par ordre alphabétique croissant d'extension. Pour garantir l'apparition des répertoires avant les fichiers, il nous suffit de préfixer le nom du répertoire par un zéro en guise de clé. Par défaut, cette dernière n'est autre que la valeur de retour de la méthode text(). Cette valeur détermine également le nom de l'élément dans la liste. La méthode setup() quant à elle est invoquée par QT juste avant de procéder à l'affichage de l'élément. Nous la surchargeons ici pour permettre à tous les répertoires d'être déroulés :

def setup(self):
  self.setExpandable(1)
  QListViewItem.setup(self)
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
Le rôle de la dernière méthode, pixmap(), est de différencier graphiquement les répertoires des fichiers. Nous devons, au sein de celle-ci, renvoyer un objet de type QPixmap représentant une image. Si le mois dernier nous avions chargé de telles images en codant des XPM en dur dans le code, nous avons ici préféré charger des images PNG depuis le disque dur. Voici par exemple comment charger l'image des fichiers :

filePixmap = QPixmap("images/tree_open.png")
       
      
JextCopier dans Jext
QT supporte de nombreux formats d'images tels que BMP ou XPM. Néanmoins, les deux formats les plus répandus, GIF et JPEG, ne sont disponibles qu'à la seule condition que la bibliothèque n'ait été compilée avec des options spécifiques (ce qui n'est pas le cas de QT 2.3).


 Les fichiers

La classe File possède plusieurs similarités avec Directory. La différence majeure réside dans sa méthode paintCell(). Par simple souci d'esthétique, nous avons décidé d'afficher certains fichiers dans d'autres couleurs. La discrimination s'effectue sur l'extension des fichiers. Python offre l'opportunité de réaliser ceci simplement en définissant un dictionnaire :

colors = { 'zip' : QColor(0, 128, 64), ' py'  : QColor(128, 0, 128) ... }
       
      
JextCopier dans Jext | Jext | Plugin Codegeek
A une extension de fichier précise, ce dictionnaire associe une couleur particulière. La suite consiste à modifier la méthode paintCell(). Le paramètre "cg" de celle-ci désigne une instance de la classe QColorGroup dont l'attribut Text définit la couleur utilisée pour dessiner le nom des éléments à l'écran. En remplaçant cette dernière par l'une de celles appartenant à notre dictionnaire, si tant est qu'une corrélation existe entre l'extension du fichier courant et les éléments de ce dictionnaire, nous pouvons modifier l'apparence de l'arbre.

Un aspect très intéressant de notre application d'exemple se veut la fonction selectionChanged() dont le rôle consiste à présenter les documents sur lesquels l'utilisateur a cliqué. Il s'agit d'une méthode relativement longue mais très simple dans sa conception. Sa seule particularité réside dans la manière de différencier les fichiers dits "binaires" des fichiers en mode texte. Le logiciel teste pour cela la présence du caractère nul (\0) dans ceux-ci.

explorer = QListView(splitter)
explorer.addColumn('File')
explorer.header().hide()
explorer.setTreeStepSize(20)
root = Directory(explorer)
root.setOpen(1)
       
      
JextCopier dans Jext
if isinstance(parent, QListView):
  self.parentDir = None
  self.fullName = '/'
else:
  self.parentDir = parent
  self.fullName = name
       
      
JextCopier dans Jext
def setOpen(self, o):
  if o and not self.childCount():
    files = QDir(self.getFullName()).entryInfoList()
    if files:
      for f in files:
        fileName = f.fileName().__unicode__()
        if fileName == '.' or fileName == '..':
          continue
        if f.isDir():
          d = Directory(self, fileName)
        else:
          d = File(self, fileName)
  QListViewItem.setOpen(self, o)
       
      
JextCopier dans Jext
def paintCell(self, p, cg, column, width, align):
  if self.extension in self.colors:
    newCg = QColorGroup(cg)
    newCg.setColor(QColorGroup.Text, self.colors[self.extension])
    QListViewItem.paintCell(self, p, newCg, column, width, align)
  else:
    QListViewItem.paintCell(self, p, cg, column, width, align)
       
      
JextCopier dans Jext

 Téléchargements

Le code source complet est disponible en téléchargement.



par Romain Guy
romain.guy@jext.org
http://www.jext.org
Dernière mise à jour : 14/10/2006


Précédent  
  Python  
  Suivant

 
#ProgX©2005 Mathieu GINOD - Romain GUY - Erik LOUISE