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.
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 »
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
"" 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.
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 :
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.
TreeStore.
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.
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é.