Toutes les sous-classes TreeModel
implémentent l'interface TreeModel
. Elle fournit des méthodes pour :
TreeIter
(une référence temporaire pointant sur une ligne du modèle).TreeModel
Les classes de base de stockage des données, le ListStore
et le TreeStore
, permettent de définir et de manipuler les lignes et les colonnes de données dans le modèle. Dans les constructeurs de ces deux objets, les colonnes devront être déclarées comme étant de l'un des types suivants :
Button
, VBox
, gdk.Rectangle
, gdk.Pixbuf
un type GObject
(les GTypes de GTK+), spécifié sous la forme soit d'une constante gobject.TYPE, soit d'une chaine de caractères. La majorité des GTypes sont mappés sur un type Python :
Par exemple, la création d'un ListStore
ou d'un TreeStore
dont les lignes contiendraient un gdk.Pixbuf
, un entier, une chaine et un booléen, pourrait ressembler à ceci :
liststore = ListStore(gtk.gdk.Pixbuf, int, str, 'gboolean') treestore = TreeStore(gtk.gdk.Pixbuf, int, str, 'gboolean')
Une fois un ListStore
ou un TreeStore
créé et ses colonnes définies, ils ne peuvent plus être modifiés. Il est également important de savoir qu'il n'y a pas de relation prédéfinie entre les colonnes d'un TreeView
et celles de son TreeModel
. Ainsi, la cinquième colonne de données d'un TreeModel
peut être affichée dans la première colonne d'un TreeView
et dans la troisième d'un autre. On n'a donc pas à se soucier de l'affichage des données lorsque l'on crée un modèle.
Si ces deux modèles ne conviennent pas à votre application, il est possible de définir votre propre modèle personnalisé en Python, pourvu qu'il implémente l'interface TreeModel. Nous verrons ceci plus en détail dans la Section 14.11, « Le TreeModel générique ».
Avant de pouvoir manipuler des lignes de données dans un TreeStore
ou un ListStore
, il nous faut pouvoir désigner les lignes à traiter. PyGTK offre trois moyens de faire référence à des lignes de TreeModel
: le chemin d'accès, le TreeIter
et le TreeRowReference
.
Le chemin d'accès consiste en un entier, une chaine ou un tuple représentant l'emplacement d'une ligne dans le modèle. Une valeur entière indique le numéro d'une ligne dans un modèle en partant de 0 (le premier niveau). Par exemple, un chemin d'accès valant 4 indiquerait la cinquième ligne du modèle. Par comparaison, la même ligne serait désignée par "4" sous forme de chaine et par (4) sous forme de tuple. Si cela suffit effectivement à désigner toutes les lignes d'un ListStore
, il nous reste encore à désigner les lignes enfants pour un TreeStore
. On n'utilisera pour ce faire que les représentations sous forme de chaine ou de tuple.
La profondeur de l'arborescence d'un TreeStore
étant complètement arbitraire, la représentation sous forme de chaine indique le chemin d'accès en partant du premier niveau jusqu'à la ligne voulue, et en séparant chaque valeur par le caractère ":". La représentation par tuple suit le même principe, le tuple étant constitué d'une séquence d'entiers conduisant à la ligne voulue en partant du premier niveau. Par exemple, les chemins "0:2" (troisième ligne enfant de la première ligne) et "4:0:1" (deuxième ligne enfant du premier enfant de la cinquième ligne) sont des représentations valides sous la forme de chaines de caractères. Les équivalents en tuples de ces chemins d'accès sont respectivement (0, 2) et (4, 0, 1).
Le chemin d'accès représente la seule possiblité de mapper une ligne d'un TreeView
sur une ligne d'un TreeModel
, car leurs chemins d'accès sont identiques. Le chemin d'accès pose toutefois quelques problèmes :
ListStore
ou dans le TreeStore
.ListStore
ou dans le TreeStore
.
La représentation par tuple est utilisée par PyGTK lors du renvoi de chemins d'accès mais les trois formes sont acceptées indifféremment en entrée. La représentation par tuple est conseillée dans un souci de cohérence.
On peut récupérer un chemin d'accès à partir d'un TreeIter
à l'aide de la méthode get_path
() :
chemin = modele.get_path(iter
)
...où iter
est un TreeIter
pointant sur une ligne de modele et chemin
le chemin d'accès de la ligne sous la forme d'un tuple.
Un TreeIter
est un objet offrant une référence temporaire à une ligne de ListStore
ou de TreeStore
. Si le contenu du modèle est modifié (généralement par l'insertion ou la suppressiono d'une ligne), le TreeIter
peut devenir invalide. Un TreeModel
supportant les TreeIter
persistants devrait avoir le drapeau gtk.TREE_MODEL_ITERS_PERSIST
. Une application peut vérifier la présence de ce drapeau avec la méthode get_flags
().
Pour créer un TreeIter
, on utilise une des méthodes du TreeModel
qui sont applicables aux TreeStore
comme aux ListStore
:
treeiter = modele.get_iter(path
)
...où treeiter
pointe sur la ligne indiquée par le chemin d'accès path
. Si le chemin d'accès est invalide, l'exception ValueError est levée.
treeiter = modele.get_iter_first()
...où treeiter
est un TreeIter
pointant sur la ligne située au chemin (0,). treeiter
vaudra None
si le modèle est vide.
treeiter = modele.iter_next(iter
)
...où treeiter
est un TreeIter
qui pointe sur la ligne suivante, sur le même niveau que le TreeIter
indiqué par iter
. treeiter
vaudra None
s'il n'y a pas de ligne suivante (et iter
sera invalidé).
Les méthodes qui suivent ne servent à recupérer un TreeIter
qu'à partir d'un TreeStore
:
treeiter = treestore.iter_children(parent
)
...où treeiter
est un TreeIter
pointant sur le premier enfant de la ligne indiquée par le TreeIter
parent
. treeiter
vaudra None
s'il n'y pas de ligne enfant.
treeiter = treestore.iter_nth_child(parent
,n
)
...où treeiter
est un TreeIter
pointant sur la ligne enfant d'index n
de la ligne désignée par le TreeIter
parent
. parent
peut valoir None
dans le cas où l'on souhaite récupérer un ligne de premier niveau. treeiter
vaudra None
s'il n'y a pas de ligne enfant.
treeiter = treestore.iter_parent(child
)
...où treeiter
est un TreeIter
pointant sur la ligne parent de la ligne désignée par le TreeIter
child
. treeiter
vaudra None
s'il n'y a pas de ligne enfant.
La méthode get_path
() permet de récupérer un chemin d'accès à partir d'un TreeIter
:
chemin = modele.get_path(iter
)
...où iter
est un Treeiter
pointant sur un ligne de modele et chemin
le chemin d'accès de la ligne sous la fome d'un tuple.
Un TreeRowReference
est une référence persistante à une ligne de données dans un modèle. Alors que le chemin d'accès (c'est-à-dire la situation) de la ligne (c'est-à-dire son emplacement) est susceptible de changer avec l'ajout ou la suppression d'autres lignes, le TreeRowReference
pointera toujours sur la même ligne de données.
Les TreeRowReference
ne sont disponibles dans PyGTK qu'à partir de la version 2.4.
On crée un TreeRowReference
grâce à son constructeur :
treerowref = TreeRowReference(model
,path
)
...où model
est le TreeModel
contenant la ligne et path
le chemin d'accès vers la ligne à laquelle faire référence. Si path
n'est pas un chemin d'accès valide pour model
, None
est renvoyé.
Une fois que vous avez un ListStore
, il vous faut lui ajouter des lignes de données. Plusieurs méthodes sont à votre disposition :
iter = append(row
=None) iter = prepend(row
=None) iter = insert(position
,row
=None) iter = insert_before(sibling
,row
=None) iter = insert_after(sibling
,row
=None)
Chacune de ces méthodes insère une ligne à un emplacement implicite ou spécifié du
ListStore
. Les méthodes append
() et prepend
() utilisent des emplacements implicites : respectivement après la dernière ligne et avant la première. La méthode insert
() attend un entier (le paramètre position
) qui spécifie l'emplacement où insérer la ligne. les deux autres méthodes attendent un TreeIter
(sibling
) pointant sur un ligne du ListStore
, afin d'insérer la ligne avant ou après.
Le paramètre row
indique les données devant être insérées dans la ligne après sa création. Si row
vaut None
ou n'est pas spécifié, la ligne créée sera vide. Si row
est spécifié, il doit être un tuple ou une liste contenant autant d'éléments que le nombre de colonnes dans le ListStore
. Les éléments doivent également respecter le type de données de leurs colonnes respectives dans le ListStore
.
Toutes ces méthodes renvoient un TreeIter
pointant sur la ligne qui vient d'être insérée. La portion de code suivante illustre la création d'un ListStore
et l'ajout de lignes de données :
... liststore = gtk.ListStore(int, str, gtk.gdk.Color) liststore.append([0,'red',colormap.alloc_color('red')]) liststore.append([1,'green',colormap.alloc_color('green')]) iter = liststore.insert(1, (2,'blue',colormap.alloc_color('blue')) ) iter = liststore.insert_after(iter, [3,'yellow',colormap.alloc_color('blue')]) ...
L'ajout de lignes à un TreeStore
est identique à l'ajout de lignes dans un ListStore
à ceci près qu'il faut également indiquer une ligne parent (avec un TreeIter
) à laquelle ajouter la nouvelle ligne. Les méthodes pour les TreeStore
sont :
iter = append(parent
,row
=None) iter = prepend(parent
,row
=None) iter = insert(parent
,position
,row
=None) iter = insert_before(parent
,sibling
,row
=None) iter = insert_after(parent
,sibling
,row
=None)
Si parent
vaut None
, la ligne sera ajoutée au premier niveau.
Chacune de ces méthodes insère une ligne à un emplacement implicite ou spécifié du TreeStore
. Les méthodes append
() et prepend
() utilisent des emplacements implicites : respectivement après la dernière ligne enfant et avant la première. La méthode insert
() attend un entier (le paramètre position
) qui spécifie l'emplacement où insérer la ligne enfant. Les deux autres méthodes attendent un TreeIter
(sibling
) pointant sur une ligne enfant du TreeStore
, afin d'insérer la ligne avant ou après.
Le paramètre row
indique les données devant être insérées dans la ligne après sa création. Si row
vaut None
ou n'est pas spécifié, la ligne créée sera vide. Si row
est spécifié, il doit être un tuple ou une liste contenant autant d'éléments que le nombre de colonnes dans le TreeStore
. Les éléments doivent également respecter le type de données de leurs colonnes respectives dans le TreeStore
.
Toutes ces méthodes renvoient un TreeIter
pointant sur la ligne qui vient d'être insérée. La portion de code suivante illustre la création d'un TreeStore
et l'ajout de lignes de données :
... pbdossier = gtk.gdk.pixbuf_from_file('dossier.xpm') pbfichier = gtk.gdk.pixbuf_from_file('fichier.xpm') treestore = gtk.TreeStore(int, str, gtk.gdk.Pixbuf) iter0 = treestore.append(None, [1,'(0,)',pbdossier] ) treestore.insert(iter0, 0, [11,'(0,0)',pbfichier]) treestore.append(iter0, [12,'(0,1)',pbfichier]) iter1 = treestore.insert_after(None, iter0, [2,'(1,)',pbdossier]) treestore.insert(iter1, 0, [22,'(1,1)',pbfichier]) treestore.prepend(iter1, [21,'(1,0)',pbfichier]) ...
Lorsqu'un ListStore
ou un TreeStore
contient déjà un grand nombre de lignes, en ajouter de nouvelles peut se révéler très lent. Pour pallier cet inconvénient, on pourra suivre les conseils suivants :
TreeModel
de son TreeView
(en utilisant la méthode set_model
() avec son paramètre model
à None
). Ceci afin d'éviter que le TreeView
n'actualise l'affichage à chaque nouvelle ligne.set_default_sort_func
() avec son paramètre sort_func
à None
).TreeRowReference
utilisés car ils actualisent leur chemin d'accès à chaque ajout/suppression.TreeView
sur TRUE
pour faire en sorte que toutes les lignes aient la même hauteur et ainsi éviter le calcul individuel de la hauteur pour chaque ligne (disponible seulement à partir de PyGTK 2.4).On peut supprimer une ligne de données d'un ListStore
en faisant appel à la méthode remove
() :
treeiter = liststore.remove(iter
)
...où iter
est un TreeIter
pointant sur la ligne à supprimer. Le TreeIter
renvoyé (treeiter
) pointera sur la ligne suivante ou sera invalide si iter
pointait sur la dernière ligne.
La méthode clear
() supprimer toutes les lignes du ListStore
:
liststore.clear()
Les méthodes servant à suprimer des lignes de données d'un TreeStore
sont similaires à celles du ListStore
:
result = treestore.remove(iter
)
treestore.clear()
...où resultat
vaudra TRUE
si la ligne a été supprimée et iter
pointera sur la ligne valide suivante. Dans le cas contraire, resultat
vaudra FALSE
et iter
sera invalidé.
Les méthodes permettant d'accéder aux valeurs stockées dans un ListStore
et dans un TreeStore
ont le même format. Toutes les manipulations des données du modèle nécessitent un TreeIter
pour indiquer la ligne sur laquelle on travaille. Une fois qu'on a un TreeIter
, la méthode get_value
() permet de récupérer les valeurs d'une colonne dans une rangée donnée :
valeur = modele.get_value(iter
,column
)
...où iter
est un TreeIter
pointant sur une ligne, column
le numéro d'une colonne de modele
, et valeur la valeur
stockée à l'emplacement ligne-colonne.
Pour récupérer les valeurs de plusieurs colonnes en un seul appel, on utilisera la méthode get
() :
valeurs = modele.get(iter, column, ...)
...où iter
est un TreeIter
pointant sur une ligne, column
le numéro d'une colonne de modele, et ...
représente d'éventuels numéros de colonne supplémentaires et valeurs
est un tuple contenant les valeurs récupérées. Par exemple, pour récupérer les valeurs des colonnes 0 et 2 :
val0, val2 = modele.get(iter, 0, 2)
La méthode get
() n'est disponible qu'à partir de PyGTK 2.4
Pour définir la valeur d'une colonne unique, on utilise la méthode set_value
() :
modele.set_value(iter
,column
,value
)
...où iter
(un TreeIter
) et column
(un entier) indiquent l'emplacement ligne-colonne dans modele
et column
est le numéro de la colonne où l'on veut stocker value
. value
doit être du même type de données que la colonne du modele
.
Pour définir les valeurs de plusieurs colonnes d'un même ligne à la fois, on utilisera la méthode set
() :
modele.set(iter
,...
)
...où iter
désigne la ligne du modèle et ...
est un ou plusieurs numéros de colonne - les paires de valeurs indiquant la colonne et la valeur à stocker. Par exemple, l'appel suivant :
modele.set(iter, 0, 'Foo', 5, 'Bar', 1, 123)
...stockera 'Foo' dans la première colonne, 'Bar' dans la sixième et 123 dans la deuxième, tout ceci à la ligne de modele
indiquée par iter
.
Les lignes d'un ListStore
peuvent être déplacées à l'aide des méthodes suivantes (disponibles à partir de PyGTK 2.2) :
liststore.swap(a
,b
) liststore.move_after(iter
,position
) liststore.move_before(iter
,position
)
swap
() intervertit les positions des lignes désignées par les TreeIter
a
et b
. move_after
() et move_before
() déplacent la ligne désignée par le TreeIter
iter
après ou avant la ligne désignée par le TreeIter
position
. Si position
vaut None
, move_after
() placera la ligne au début du modèle et move_before
() à la fin.
Pour réordonner complètement les lignes d'un ListStore
, on fera appel à la méthode suivante :
liststore.reorder(nouvel_ordre
)
...où nouvel_ordre
est une liste d'entiers représentant le nouvel ordre des lignes tel que :
nouvel_ordre
[nouvelleposition] = ancienneposition
Par exemple, si liststore
contient quatre lignes :
'un' 'deux' 'trois' 'quatre'
...l'appel suivant :
liststore.reorder([2, 1, 3, 0])
...produirait le nouvel ordre suivant :
'trois' 'deux' 'quatre' 'un'
Ces méthodes réordonnent uniquement les ListStore
non-ordonnés.
Pour réordonner les lignes avec PyGTK 2.0, il vous faudra les supprimer et les insérer avec les méthodes décrites aux Section 14.2.4, « Ajouter des lignes » et Section 14.2.5, « Supprimer des lignes ».
Les méthodes utilisées pour réordonner les lignes du TreeStore
sont identiques à celles du ListStore
à ceci près qu'elles n'affectent que les lignes enfants d'une ligne parent implicite - il n'est pas possible, par exemple, d'intervertir des lignes de parents différents :
treestore.swap(a
,b
) treestore.move_after(iter
,position
) treestore.move_before(iter
,position
)
swap
() intervertit les positions des lignes enfants désignées par les TreeIter a
et b
. a
et b
doivent avoir la même ligne parent. move_after
() et move_before
() déplacent la ligne désignée par le TreeIter
iter
après ou avant la ligne désignée par le TreeIter
position
. iter
et position
doivent avoir la même ligne parent. Si position
vaut None
, move_after
() placera la ligne au début du modèle et move_before
() à la fin.
La méthode reorder
() attend un paramètre supplémentaire qui désigne la ligne parent dont les enfants doivent être réordonnés :
treestore.reorder(parent
,new_order
)
...où new_order
est une liste d'entiers représentant le nouvel ordre des lignes enfants pour la ligne parent spécifiée par le TreeIter
parent
, telle que :
new_order
[nouvelleposition] = ancienneposition
Par exemple, si treestore
contient quatre lignes :
'ligne parent' 'un' 'deux' 'trois' 'quatre'
... l'appel suivant
treestore.reorder(parent, [2, 1, 3, 0])
...produirait le nouvel ordre suivant :
'ligne parent' 'trois' 'deux' 'quatre' 'un'
Ces méthodes réordonnent uniquement les TreeStore
non-ordonnés.
L'une des opérations les plus délicates avec les ListStore
ou des TreeStore
est la manipulation de plusieurs lignes, par exemple le déplacement de plusieurs lignes d'une ligne parent à une autre, ou encore la suppression de certaines lignes en fonction de critères spéciaux. La difficulté provient de la nécessité d'utiliser un TreeIter
qui peut devenir invalide du fait même de l'opération. Pour les ListStore
et les TreeStore
, les TreeIter
sont persistants (afin de s'en assurer, la présence du drapeau gtk.TREE_MODEL_ITERS_PERSIST
peut être vérifiée avec la méthode get_flags
()). En revanche, les classes empilables TreeModelFilter
et TreeModelSort
ne disposent pas de TreeIter
persistants.
Comment déplacer toutes les lignes enfants d'une ligne vers une autre si les TreeIter
ne résistent pas à l'opération ? Il faut :
On ne peut pas se fier à la méthode remove
() pour renvoyer un TreeIter
valide. On demandera donc simplement l'iter de la première ligne enfant, et ce jusqu'à recevoir None
. Voici un exemple de fonction pouvant déplacer des lignes enfants :
def deplace_enfants(treestore, parent_source, parent_destination): nb_colonnes = treestore.get_n_columns() iter = treestore.iter_children(parent_source) while iter: valeurs = treestore.get(iter, *range(nb_colonnes)) treestore.remove(iter) treestore.append(parent_destination, valeurs) iter = treestore.iter_children(parent_source) return
La fonction ci-dessus convient pour le simple cas où l'on souhaiterait déplacer tous les enfants d'une même ligne parent. Mais que faire si l'on veut supprimer toutes les lignes du TreeStore
qui correspondent à un critère donné, comme la valeur de la première colonne par exemple ? Vous pensez peut-être à utiliser la méthode foreach
() pour itérer sur toutes les lignes et supprimer celles qui correspondent :
modele.foreach(fonction
,donnees_utilisateur
)
...où fonction
est une fonction à invoquer pour chaque ligne du modèle et dont la signature est :
def fonction(modele
,chemin
,iter
,donnees_utilisateur
):
...où modele
est le TreeModel
, chemin
le chemin d'accès à une ligne de modele
, iter
un TreeIter
pointant sur chemin
et donnees_utilisateur
les données transmises. Si fonction
renvoie TRUE
, la méthode foreach
() cesse d'itérer et se termine.
Le problème de ce procédé est que modifier le contenu du modèle pendant que la méthode foreach
() est en train d'itérer peut avoir des conséquences imprévisibles. Une meilleure stratégie serait d'utiliser la méthode foreach
() pour créer et sauvegarder des TreeRowReferences pointant sur les lignes à supprimer, puis de supprimer celles-ci une fois la méthode foreach
() terminée. Mais cela ne fonctionnerait pas avec PyGTK 2.0 et 2.2 dans lesquels les TreeRowReference
n'existent pas.
Si l'on souhaite couvrir toutes les variantes de PyGTK, on peut utiliser la méthode foreach
() pour rassembler les chemins d'accès des lignes à supprimer, puis les supprimer dans l'ordre inverse afin de préserver la validité des chemins d'accès. Par exemple, la portion de code suivante utilise cette stratégie :
... # On verifie que la valeur de la premiere colonne >= la valeur transmise # donnees est un tuple contenant la valeur de comparaison ainsi qu'une liste # pour sauvegarder les chemins d'acces def rappel_compar_valeur(modele, chemin, iter, donnees): if modele.get_value(iter, 0) >= donnees[0]: donnees[1].append(chemin) return False # foreach continue d'iterer listechemins = [] treestore.foreach(rappel_compar_valeur, (10, listechemins)) # foreach fonctionne en profondeur d'abord (depth-first) listechemins.reverse() for chemin in listechemins: treestore.remove(treestore.get_iter(chemin)) ...
Dans le cas où l'on voudrait rechercher la première ligne d'un TreeStore
répondant à un critère donné, on pourrait effectuer soi-même l'itération :
treestore = TreeStore(str) ... def fonction_compar(modele, iter, donnees): colonne, critere = donnees # donnes est un tuple contenant un numero de colonne et un critere valeur = modele.get_value(iter, colonne) return valeur == critere def recherche(modele, iter, fonction, donnees): while iter: if fonction(modele, iter, donnees): return iter resultat = recherche(modele, modele.iter_children(iter), fonction, donnees) if resultat: return resultat iter = modele.iter_next(iter) return None ... compar_iter = recherche(treestore, treestore.iter_children(None), fonction_compar, (0, 'bla'))
La fonction recherche
() effectue une itération en profondeur sur la ligne (spécifiée par iter
), sur ses pairs et sur leurs enfants à la recherche d'une ligne dont une colonne correspond à la chaine de caractère donnéee. La recherche prend fin lorsqu'une ligne est trouvée.
Les classes qui implémentent l'interface TreeModel
(TreeStore
, ListStore
et, à partir de PyGTK 2.4 TreeModelSort
et TreeModelFilter
) supportent les protocoles Python de mappage et d'itération. Le protocole d'itération permet d'utiliser la fonction iter
() de Python sur un Treemodel
pour créer un itérateur qui servira à itérer sur ses lignes de premier niveau. Une possibilité plus utile est d'itérer en utilisant l'instruction for
ou une création fonctionnelle de liste (list comprehension). Par exemple :
... liststore = gtk.ListStore(str, str) ... # on ajoute des lignes au ListStore ... # une boucle for for ligne in liststore: # traitement individuel des lignes ... # creation fonctionnelle de liste renvoyant une liste des valeurs de la premiere colonne valeurs = [ l[0] for l in liststore ] ...
L'utilisation de del
pour supprimer une ligne du modèle et l'extraction d'un TreeModelRow
w PyGTK de ce dernier à partir d'une valeur-clé qui est un chemin d'accès ou un TreeIter
sont d'autres parties supportées des protocoles de mappage. Par exemple, les instructions suivantes renvoient toutes la première ligne d'un TreeModel
et la dernière instruction supprime le premier enfant de la première ligne :
ligne = modele[0] ligne = modele['0'] ligne = modele["0"] ligne = modele[(0,)] i = modele.get_iter(0) ligne = modele[i] del modele[(0,0)]
De plus, on peut fixer les valeurs d'une ligne existante comme ceci :
... liststore = gtk.ListStore(str, int, object) ... liststore[0] = ['Bouton', 23, gtk.Button('Etiquette')]
Les objets TreeModelRow
de PyGTK supportent les protocoles de séquence et d'itération Python. On peut faire en sorte qu'un itérateur itère sur les valeurs des colonnes de la ligne, ou bien utiliser l'instruction for
ou encore une création fonctionnelle de liste. Les TreeModelRow
utilisent le numéro de colonne comme l'index à partir duquel extraire une valeur. Par exemple :
... liststore = gtk.ListStore(str, int) liststore.append(['Chaine de caracteres', 514]) ... ligne = liststore[0] valeur1 = ligne[1] valeur0 = liststore['0'][0] for valeur in ligne: print valeur val0, val1 = ligne ...
On utilise l'exemple de la section précédente pour itérer sur un TreeStore
afin de localiser une ligne contenant une valeur particulière, le code devient :
treestore = TreeStore(str) ... def fonction_compar(ligne, donnees): colonne, critere = donnees # donnes est un tuple contenant un numero de colonne et un critere return ligne[colonne] == critere ... def recherche(lignes, fonction, donnees): if not lignes: return None for ligne in lignes: if fonction(ligne, donnees): return ligne resultat = recherche(ligne.iterchildren(), fonction, donnees) if resultat: return resultat return None ... compar_ligne = recherche(treestore, fonction_compar, (0, 'bla'))
On peut également définir une valeur dans une colonne existante ainsi :
treestore[(1,0,1)][1] = 'abc'
Les TreeModelRow
supportent aussi l'instruction del
et la conversion en listes ou tuples avec les fonctions list
() et tuple
() de Python. Comme illustré dans l'exemple suivant, le TreeModelRow
dispose de la méthode iterchildren
() qui renvoie un itérateur pour itérer sur les lignes enfants du TreeModelRow
.
Votre application peut suivre les changements d'un TreeModel
en se connectant aux signaux qu'il émet : "row-changed", "row-deleted", "row-inserted", "row-has-child-toggled" et "rows-reordered". Ces signaux sont utilisés par un TreeView
pour suivre les changements dans son TreeModel
.
Dans une application connectée à ces signaux, l'appel de certaines méthodes peut déclencher l'émission de plusieurs signaux. Par exemple, l'appel pour ajouter à une ligne sa première ligne enfant :
treestore.append(parent, ['qwe', 'asd', 123])
...déclenchera l'émission des signaux suivants :
ligne-insérée
), du fait de l'insertion de la ligne (vide).ligne-possède-enfant-modifié
), puisque parent
n'avait pas de ligne enfant auparavant.ligne-modifiée
) car la valeur de la première colonne est fixée à 'qwe'.ligne-modifiée
) car la valeur de la deuxième colonne est fixée à 'asd'.ligne-modifiée
) car la valeur de la troisième colonne est fixée à 123.
Notez qu'il n'est pas possible de récupérer l'ordre des lignes dans la fonction de rappel du signal "rows-reordered" car le nouvel ordre des lignes est transmis sous la forme d'un pointeur opaque vers un tableau d'entiers.
On trouvera plus d'information sur les signaux des TreeModel
dans le PyGTK
Reference Manual.
Les objets ListStore
et TreeStore
implémentent l'interface TreeSortable
qui fournit des méthodes pour contrôler le classement des lignes de TreeModel
. L'élément clé de l'interface est l'identificateur de classement de colonne, une valeur entière arbitraire faisant référence à une fonction de comparaison de classement et à des données utilisateur associées. Un identificateur de classement de colonne doit être supérieur ou égal à zéro. On en crée un avec la méthode suivante :
treesortable.set_sort_func(id_class_colonne
,fonction_classement
,donnees_utilisateur
=None
)
...où id_class_colonne
est une valeur entière assignée par le programmeur, fonction_classement
une fonction ou une méthode utilisée pour comparer des lignes et donnees_utilisateur
des données contextuelles. fonction_classement
a la signature suivante :
def fonction_classement(modele, iter1, iter2, donnees) def methode_classement(self, modele, iter1, iter2, donnees)
... où modele
est le TreeModel
contenant les lignes sur lesquelles pointent les TreeIter
iter1
et iter2
, et où donnees
est donnees_utilisateur
. fonction_classement
doit renvoyer -1 si la ligne iter1
précède la ligne iter2
, 0 si les lignes sont égales, et 1 si la ligne iter2
précède la ligne iter1
. La fonction de comparaison de classement devrait toujours considérer que l'ordre de classement est gtk.SORT_ASCENDING
puisque l'ordre de classement sera pris en compte par les implémentations TreeSortable
.
La même fonction de comparaison de classement peut être utilisée pour plusieurs identificateurs de classement de colonne en variant les données utilisateur afin de fournir des informations contextuelles. Par exemple, les donnees_utilisateur
spécifiées dans la méthode set_sort_func
() pourraient être l'index de la colonne d'où extraire les données de classement..
Une fois qu'un identificateur de classement de colonne est créé, un modèle peut s'en servir pour le classement en appelant la méthode :
treesortable.set_sort_column_id(sort_column_id
,order
)
...où order
est l'ordre de classement ascendant
gtk.SORT_ASCENDING
ou descendant gtk.SORT_DESCENDING
.
Un identificateur de classement de colonne valant -1 signifie que le modèle devrait utiliser la fonction de classement par défaut qui se définit avec la méthode suivante :
treesortable.set_default_sort_func(fonction_classement
,donnees_utilisateur
=None
)
On peut vérifier si un modèle a une fonction de classement par défaut avec la méthode :
resultat = treesortable.has_default_sort_func()
...qui renvoie TRUE
si une fonction de classement par défaut a été définie.
Une fois qu'un identificateur de classement de colonne a été appliqué sur un TreeModel
implémentant l'interface TreeSortable
, il ne peut plus revenir à son état original non classé. On peut changer la fonction de classement ou utiliser une fonction de classement par défaut mais on ne peut pas retirer au TreeModel
la fonction de classement.
Lorsqu'un objet ListStore
ou TreeStore
est créé, il définit automatiquement des identificateurs de classement de colonne correspondant aux colonnes du modèle en utilisant le numéro de l'index de la colonne. Par exemple, un ListStore
avec trois colonnes aurait trois identificateurs de classement de colonnes (0, 1, 2) créés automatiquement. Ces identificateurs de classement de colonne sont associés avec une fonction interne de comparaison de classement qui gère les types fondamentaux :
Initialement, un ListStore
ou un TreeStore
sont définis avec un identificateur de classement de colonne de -2 qui indique qu'aucune fonction de classement n'est utilisée et que le modèle est non-classé. Une fois défini un identificateur de classement de colonne sur un ListStore
ou sur un TreeStore
, il est impossible de le ramener à -2.
Si l'on souhaite maintenir les identificateurs de classement de colonne par défaut, on peut définir un identificateur de classement de colonne bien en-dehors de l'intervalle du nombre de colonnes, comme 1000 ou plus. Puis l'on peut alterner entre la fonction de classement par défaut et les fonctions de classement de l'application selon les besoins.