Vous êtes à peu près ici : Accueil  »   tutoriel PyGTK  »   PyGTK : sommaire

14.10. TreeModelSort et TreeModelFilter

Les TreeModelSort et TreeModelFilter sont des modèles d'arbre qui s'intercalent entre le TreeModel de base (un TreeStore ou un ListStore) et le TreeView pour fournir un modèle modifié en conservant la structure d'origine du modèle de base. Ces modèles interposables implémentent les interfaces TreeModel et TreeSortable mais ne fournissent aucune méthode pour insérer ou retirer des lignes dans le modèle, il faut insérer ou retirer ces lignes dans le magasin de données sous-jacent. Le TreeModelSort fournit un modèle dans lequel les lignes sont toujours classées, le TreeModelFilter fournit un modèle contenant un sous-ensemble des lignes du modèle de base.

Ces modèles peuvent être reliés sur une longueur arbitraire, si désiré. Par exemple, un TreeModelFilter peut avoir un enfant TreeModelSort qui peut peut avoir un enfant TreeModelFilter et ainsi de suite. Tant qu'il existe un TreeStore ou un ListStore comme point de départ de la chaine, cela devrait fonctionner. Sous PyGTK :2.0 et 2.2 les objets TreeModelSort et TreeModelFilter ne fonctionnent pas avec le protocole de mappage (mise en correspondance) de Python.

14.10.1. Le TreeModelSort

Le TreeModelSort maintient un modèle trié du modèle enfant indiqué dans son constructeur. L'usage principal du TreeModelSort est de fournir, pour un modele, des vues multiples qui peuvent être classées diversement. Lorsque l'on a plusieurs vues d'un même modèle, toute opération de tri se répercute dans toutes les vues. Utiliser le TreeModelSort permet de laisser le modèle de départ dans son état originel pendant que les modèles de tri absorbent toutes les opérations de tri. Pour créer un TreeModelSort, il faut utiliser le constructeur :

  treemodelsort = gtk.TreeModelSort(child_model)

... où child_model est un TreeModel. La plupart des méthodes du TreeModelSort portent sur la conversion des chemins de l'arborescence et des TreeIter du modèle enfant vers le modèle trié, et réciproquement :

  sorted_path = treemodelsort.convert_child_path_to_path(child_path)
  child_path = treemodelsort.convert_path_to_child_path(sorted_path)

Ces méthodes de conversion de chemin renvoient None si le chemin donné ne peut être converti en chemin dans le modèle trié ou dans le modèle enfant. Les méthodes de conversion du TreeIter sont :

  sorted_iter = treemodelsort.convert_child_iter_to_iter(sorted_iter, child_iter)
 child_iter = treemodelsort.convert_iter_to_child_iter(child_iter, sorted_iter)

Les méthodes de conversion du TreeIter dupliquent l'argument converti (la valeur de retour comme le premier argument) pour une présever une compatibilité antérieure. Il faut donner la valeur None au premier argument et n'utiliser que la valeur de retour. Par exemple :

  sorted_iter = treemodelsort.convert_child_iter_to_iter(None, child_iter)
  child_iter = treemodelsort.convert_iter_to_child_iter(None, sorted_iter)

Comme les méthodes de conversion du chemin, ces méthodes renvoient None si le TreeIter indiqué ne peut être converti.

On peut retrouver le TreeModel enfant gràce à la méthode get_model().

treemodelsort.py est un exemple simple utilisant les objets du TreeModelSort. Les résultats obtenus avec 6 lignes sont illustrés par Figure 14.9, « Exemple de TreeModelSort »

Figure 14.9. Exemple de TreeModelSort

Exemple de TreeModelSort.

Chacune des colonnes d'une fenêtre peut être réordonnée avec un click sur son titre indépendamment des autres fenêtres. Lorsque le bouton "Add a Row" est pressé, une nouvelle ligne est ajoutée dans le ListStore de base et cette nouvelle ligne est affichée dans chaque fenêtre comme étant la ligne sélectionnée.

14.10.2. Le TreeModelFilter

Note

Le TreeModelFilter est disponible avec PyGTK 2.4 et supérieur.

Un objet TreeModelFilter fournit plusieurs façons de modifier la vue du TreeModel de base, y compris :

  • afficher un sous-ensemble de lignes dans le modèle fils basé soit sur la valeur booléenne d'une "colonne visible" soit sur la valeur booléenne de retour d'une "fonction visible" ayant comme arguments un modèle fils, un TreeIter pointant sur une ligne du modèle fils et des données utilisateurs. Dans les deux cas, si la valeur booléenne vaut TRUE, la ligne est affichée sinon elle est cachée.
  • utiliser une racine virtuelle qui fournit une vue d'un sous-arbre des enfants d'une ligne dans un modele fils. Ceci n'est réalisable que si les données sont dans un TreeStore.
  • combiner les colonnes et données d'un modèle relativement aux données du modèle fils. Par exemple, on peut afficher une colonne dans laquelle les données sont calculées à partir de données dans plusieurs colonnes du modèle fils.

Un objet TreeModelFilter est crée par la méthode TreeModel :

  treemodelfilter = treemodel.filter_new(root=None)

... où root est un chemin dans l'arbre du treemodel précisant une racine virtuelle du modèle ou None si la racine du treemodel doit être utilisée.

Indiquer une "racine virtuelle" quand on crée le TreeModelFilter permet de limiter la vue aux enfants de cette ligne "racine" dans la hiérarchie du modèle fils. Ceci, bien sûr, n'est utile que si le modèle fils est basé sur un TreeStore. Par exemple, on peut vouloir fournir une vue de la liste des pièces qui composent un lecteur de CDROM distincte de la liste complète des pièces de l'ordinateur.

Les modes de visibilité sont mutuellement exclusifs et ne peuvent être fixés qu'une seule fois ; Une fois la visibilité de la colonne ou de la fonction établie, on ne peut plus la modifier et l'autre mode ne peut plus être utilisé. Le mode de visibilité le plus simple extrait une valeur booléenne d'une colonne du modèle fils pour déterminer si la ligne doit être affichée. La visibilité colonne est fixée par :

  treemodelfilter.set_visible_column(column)

... où column est le numéro de la colonne dans le TreeModel fils, de laquelle extraire les valeurs booléennes. Par exemple, le code suivant utilise les valeurs de la troisième colonne pour fixer la visibilité des lignes :

  ...
  treestore = gtk.TreeStore(str, str, "gboolean")
  ...
  modelfilter = treestore.filter_new()
  modelfilter.set_visible_column(2)
  ...

Ainsi, toutes les lignes du treestore qui possèdent la valeur TRUE dans la troisième colonne seront affichées.

Si on veut utiliser des critères de visibilité plus complexes, une fonction visibilité devrait fournir des capacités suffisantes :

  treemodelfilter.set_visible_func(func, data=None)

... où func est la fonction appelée pour chaque ligne du modèle fils afin de décider si elle doit être affichée et data représente les données utilisateur transmises à la fonction func. La fonction func renvoie TRUE si la ligne doit être affichée. Cette fonction a pour signature :

  def func(modele, iter, donnees_utilisateur)

... où modele est le TreeModel fils, iter est un TreeIter pointant sur une ligne du modele et donnees_utilisateur sont les data passées dans la fonction.

Si on modifie le critère de visibilité, il faut utiliser :

  treemodelfilter.refilter()

pour imposer un nouveau filtrage des lignes du modèle fils.

Par exemple, l'extrait de code ci-dessous illustre un TreeModelFilter qui affiche des lignes selon une comparaison entre la valeur de la troisième colonne et le contenu des données utilisateurs :

  ...
  def match_type(modele, iter, donnees_utilisateur):
      valeur = model.get_value(iter, 2)
      return valeur in donnees_utilisateur
  ...
  show_vals = ['OPEN', 'NEW', 'RESO']
  liststore = gtk.ListStore(str, str, str)
  ...
  modelfilter = liststore.filter_new()
  modelfilter.set_visible_func(match_type, show_vals)
  ...

Le programme treemodelfilter.py illustre l'utilisation de la méthode set_visible_func(). Figure 14.10, « Exemple de visibilité d'un TreeModelFilter » montre le résultat obtenu.

Figure 14.10. Exemple de visibilité d'un TreeModelFilter

Exemple de TreeModelFilter.

En agissant sur les boutons du bas, on modifie le contenu du TreeView pour afficher seulement les lignes qui correspondent à l'étiquette du bouton.

Une fonction modify permet un autre niveau de contrôle sur l'affichage du TreeView sur la manière dont on peut combiner une ou plusieurs (ou toutes) colonnes présentées par le TreeModelFilter. Il faut utiliser un modèle fils de base, un TreeStore ou un ListStore pour déterminer le nombre de lignes et la hiérarchie, mais les colonnes peuvent être tout ce qui est indiqué dans la méthode :

  treemodelfilter.set_modify_func(types, func, data=None)

... où types est une suite (liste ou tuple) précisant le type de colonne présentes, func est une fonction appelée pour renvoyer la valeur pour une rangée et une colonne et data est un argument passé à la fonction func. La fonction func a pour signature :

  def func(modele, iter, colonne, donnees_utilisateur)

...où modele est le TreeModelFilter, iter le TreeIter qui pointe sur une ligne du modele, colonne est le numéro de la colonne pour laquelle une valeur est nécessaire et donnees_utilisateur est le paramètre data. La fonction func doit renvoyer une valeur correspondant au type de colonne.

Une fonction de modification est utile quand on souhaite fournir une colonne de données qui doivent être générées à partir de données de colonnes du modèle fils. Par exemple, on dispose d'une colonne contenant des dates de naissance et on veut fournir une colonne affichant les âges ; une fonction de modification peut générer l'information sur l'âge à partir de la date de naissance et de la date actuelle. Un autre exemple serait de choisir l'image à afficher selon l'analyse des données (un nom de fichier) dans une colonne. Cette action peut aussi être réalisée par la methode set_cell_data_func() du TreeViewColumn.

Généralement, avec la fonction modify, il faut convertir le TreeIter du TreeModelFilter en un TreeIter dans le modèle fils par :

  child_iter = treemodelfilter.convert_iter_to_child_iter(filter_iter)

Bien sûr, il faut aussi retrouver le modèle fils par :

  child_model = treemodelfilter.get_model()

Ceci donne accès à la ligne du modèle fils et à son contenu pour fournir la valeur de la ligne et colonne indiquées du TreeModelFilter. Il existe aussi une méthode pour convertir les chemins du modèle filtre de et vers les chemins de l'arborescence fille.

  filter_iter = treemodelfilter.convert_child_iter_to_iter(child_iter)

  child_path = treemodelfilter.convert_path_to_child_path(filter_path)
  filter_path = treemodelfilter.convert_child_path_to_path(child_path)

Bien sûr, il est possible de combiner les modes de visibilité et la fonction modify pour, à la fois, filtrer les lignes et produire les colonnes. Pour obtenir encore plus de contrôle sur la vue, il faudrait utiliser un TreeModel personnalisé.