-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsec-GenericTreeModel.html
More file actions
648 lines (646 loc) · 43 KB
/
Copy pathsec-GenericTreeModel.html
File metadata and controls
648 lines (646 loc) · 43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>14.11. Le TreeModel générique</title>
<link rel="stylesheet" href="pygtktutfr.css" type="text/css">
<meta name="generator" content="Bluefish 1.0.5">
<meta name="keywords" content="python,pygtk,tutoriel,traduction,treeview" />
<link rel="start" href="index.html" title="Tutoriel PyGTK 2.0">
<link rel="up" href="ch-TreeViewWidget.html" title="Chapitre 14. Le widget TreeView">
<link rel="prev" href="sec-TreeModelSortAndTreeModelFilter.html" title="14.10. TreeModelSort et TreeModelFilter">
<link rel="next" href="sec-GenericCellRenderer.html" title="14.12. The Generic CellRenderer">
</head>
<body>
<div class="localisation">
Vous êtes à peu près ici :
<a href="../../index.html">Accueil</a> »
<a href="../pygtktut.php">tutoriel PyGTK</a> »
<a href="index.html">PyGTK : sommaire</a>
</div> <!-- fin localisation -->
<div class="navheader">
<table width="100%" summary="Navigation header">
<tr><th colspan="3" align="center">14.11. Le TreeModel générique</th></tr>
<tr>
<td width="20%" align="left">
<a accesskey="p" href="sec-TreeModelSortAndTreeModelFilter.html">Préc.</a> </td>
<th width="60%" align="center">Chapitre 14. Le widget TreeView</th>
<td width="20%" align="right"> <a accesskey="n" href="sec-GenericCellRenderer.html">Suiv.</a>
</td>
</tr>
</table>
<hr>
</div>
<div class="sect1" lang="fr">
<div class="titlepage"><div><div><h2 class="title" style="clear: both">
<a name="sec-GenericTreeModel"></a>14.11. Le TreeModel générique</h2></div></div></div>
<p>Si le <code class="classname">TreeModel</code> standard n'est pas assez puissant pour
les besoins de l'application, il est possible d'utiliser le
<code class="classname">GenericTreeModel</code> pour construire son propre
<code class="classname">TreeModel</code> personnalisé en python. Créer un
<code class="classname">GenericTreeModel</code> peut être utile en cas de problème de
performance avec les objets <code class="classname">TreeStore</code> et
<code class="classname">ListStore</code> standards, ou si on veut une interface
directe avec une source de données externe (une base de données ou un système
de fichiers) pour éviter une duplication des données de et vers le
<code class="classname">TreeStore</code> ou le <code class="classname">ListStore</code>.
</p>
<div class="sect2" lang="fr">
<div class="titlepage"><div><div><h3 class="title">
<a name="sec-GenericTreeModelOverview"></a>14.11.1. Aperçu du GenericTreeModel</h3></div></div></div>
<p>Avec un <code class="classname">GenericTreeModel</code>, on construit et gère son
modèle de données et on fournit un accès externe à travers l'interface du
<code class="classname">TreeModel</code> standard en définissant un ensemble de
méthodes de classe. PyGTK implémente l'interface du
<code class="classname">TreeModel</code> et prend en charge les méthodes du
<code class="classname">TreeModel</code> appelées pour fournir le modèle de données
réel.
</p>
<p>Les détails de l'implémentation de votre modèle devraient rester complétement
cachés aux applications externes. Ce qui signifie que la manière dont votre
modèle identifie, range et retrouve les données n'est pas connue de
l'application. En général, la seule information qui est sauvegardée en-dehors
du <code class="classname">GenericTreeModel</code> sont les références de ligne qui sont
enveloppées par les <code class="classname">TreeIter</code> externes. Ces références ne
sont pas visibles par l'application.
</p>
<p>Voici un examen détaillé de l'interface <code class="classname">GenericTreeModel</code>
qu'il faut fournir.
</p>
</div>
<div class="sect2" lang="fr">
<div class="titlepage"><div><div><h3 class="title">
<a name="sec-GenericTreeModelInterface"></a>14.11.2. Interface du GenericTreeModel</h3></div></div></div>
<p>L'interface du <code class="classname">GenericTreeModel</code> comprend les
méthodes suivantes qui doivent être implémentées dans le modèle
personnalisé :
</p>
<code class="methodsynopsis"> def <span class="methodname">on_get_flags</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_n_columns</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_column_type</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>index</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_iter</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>path</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_path</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>rowref</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_value</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>rowref</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>column</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_next</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>rowref</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_children</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>parent</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_has_child</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>rowref</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_n_children</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>rowref</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_nth_child</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>parent</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>n</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_parent</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>self</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>child</code></em></span></span>)</code><br><p>Il faut remarquer que ces méthodes supportent entièrement l'interface du
<code class="classname">TreeModel</code>, y compris :
</p>
<code class="methodsynopsis"> def <span class="methodname">get_flags</span>(<span class="methodparam"></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_n_columns</span>(<span class="methodparam"></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_column_type</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>index</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_iter</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>path</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_iter_from_string</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>path_string</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_string_from_iter</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>iter</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_iter_root</span>(<span class="methodparam"></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_iter_first</span>(<span class="methodparam"></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_path</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_value</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>, <span class="methodparam"><span class="parameter"><strong class="parameter"><code>column</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_next</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_children</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>parent</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_has_child</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_n_children</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_nth_child</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>parent</code></strong></span></span>, <span class="methodparam"><span class="parameter"><strong class="parameter"><code>n</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_parent</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>child</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>iter</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>column</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>...</code></em></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">foreach</span>(<span class="methodparam"><span class="parameter"><em class="parameter"><code>func</code></em></span></span>, <span class="methodparam"><span class="parameter"><em class="parameter"><code>user_data</code></em></span></span>)</code><p>Pour illustrer l'utilisation du <code class="classname">GenericTreeModel</code>
j'ai modifié le programme <a href="exemples/listefichiers.py" target="_top"><span><strong class="command">listefichiers.py</strong></span></a> et
montre comment les méthodes d'interface sont réalisées. Le programme
<a href="exemples/listefichiers-gtm.py" target="_top"><span><strong class="command">listefichiers-gtm.py</strong></span></a>
affiche les fichiers d'un répertoire avec une icône indiquant si le fichier est ou
non un répertoire, le nom du fichier, sa taille, son mode et sa date de dernière
modification.
</p>
<p>La méthode <code class="methodname">on_get_flags</code>() doit retourner une
valeur qui est une combinaison de :
</p>
<table border="0" width="100%" bgcolor="#FFECCE">
<col align="left" valign="top" width="0*">
<tbody>
<tr>
<td>
<span class="term"><code class="literal">gtk.TREE_MODEL_ITERS_PERSIST</code></span></td>
<td>Les <code class="classname">TreeIter</code> perdurent quels que soient
les signaux émis par l'arbre.</td>
</tr>
<tr>
<td>
<span class="term"><code class="literal">gtk.TREE_MODEL_LIST_ONLY</code></span></td>
<td>Le modèle est uniquement une liste et n'a jamais d'enfant.
</td>
</tr>
</tbody>
</table>
<p>Si un modèle posséde des références de lignes valides malgré les changements
de lignes (réordonnancement, ajout, suppression), on utilise
<code class="literal">gtk.TREE_MODEL_ITERS_PERSIST</code>. De la même façon, si un
modèle est seulement une liste, on utilise <code class="literal">gtk.TREE_MODEL_LIST_ONLY</code>.
Autrement, on renvoie 0 si le modèle ne possède pas de références de lignes
persistantes et est une arborescence. Dans l'exemple, le modèle est une liste
avec des <code class="classname">TreeIter</code> persistants.
</p>
<pre class="programlisting">
def on_get_flags(self):
return gtk.TREE_MODEL_LIST_ONLY|gtk.TREE_MODEL_ITERS_PERSIST
</pre>
<p>La méthode <code class="methodname">on_get_n_columns</code>() doit retourner le
nombre de colonnes que le modèle exporte vers l'application. L'exemple
garde une liste de types de colonnes, ainsi on peut renvoyer la longueur
de la liste :
</p>
<pre class="programlisting">
class Fichmodeleliste(gtk.GenericTreeModel):
...
column_types = (gtk.gdk.Pixbuf, str, long, str, str)
...
def on_get_n_columns(self):
return len(self.types_colonnes)
</pre>
<p>La méthode <code class="methodname">on_get_column_type</code>() doit renvoyer le type
de la colonne pour l'<em class="parameter"><code>index</code></em> indiqué. Cette méthode est généralement
appelée par un <code class="classname">TreeView</code> quand le modèle est établi.
On peut soit créer une liste ou un tuple contenant l'information de type de colonne
soit le créer à la volée. Dans l'exemple :
</p>
<pre class="programlisting">
def on_get_column_type(self, n):
return self.types_colonnes[n]
</pre>
<p>L'interface <code class="classname">GenericTreeModel</code> convertit le type
Python en un GType, donc le code suivant</p>
<pre class="programlisting">
flm = Fichmodeleliste()
print flm.on_get_column_type(1), flm.get_column_type(1)
</pre>
<p>imprimerait :</p>
<pre class="programlisting">
<type 'str'> <GType gchararray (64)>
</pre>
<p>Les méthodes suivantes utilisent les références de ligne qui sont conservées
comme données privées dans un <code class="classname">TreeIter</code>. L'application
ne peut lire la référence de ligne dans un <code class="classname">TreeIter</code>
donc on peut utiliser n'importe quel item unique voulu comme référence de
ligne. Par exemple, dans un modèle comportant des lignes comme des tuples, il
est possible d'utiliser l'index de tuple comme la référence de ligne. Un
autre exemple serait d'utiliser un nom de fichier comme référence de ligne
dans un modèle représentant des fichiers dans un répertoire. Dans ces deux
cas, le référence de ligne n'est pas modifiée par les changements du modèle,
aussi les <code class="classname">TreeIter</code> peuvent être déclarés persistants.
L'interface de l'application PyGTK <code class="classname">GenericTreeModel</code>
extraira vos références de ligne à partir des <code class="classname">TreeIter</code>
et enveloppera vos références de ligne dans les <code class="classname">TreeIter</code>
selons les besoins.
</p>
<p>Dans les méthodes suivantes, <code class="literal">refligne</code> se réfère à une
référence de ligne interne.</p>
<p>La méthode <code class="methodname">on_get_iter</code>() devrait renvoyer un refligne
pour le chemin de l'arborescence indiqué par le <em class="parameter"><code>chemin</code></em>.
Le chemin de l'arborescence doit toujours être représenté par un tuple. L'exemple
utilise le nom de fichier comme refligne. Les noms de fichier sont conservés dans
une liste dans le modèle, ainsi on prend le premier index du chemin comme index
du nom de fichier :
</p>
<pre class="programlisting">
def on_get_iter(self, chemin):
return self.fichiers[chemin[0]]
</pre>
<p>Il faut être cohérent dans l'utilisation de la référence de ligne puisque on
récupérera une référence de ligne dans les appels de méthode des méthodes
<code class="classname">GenericTreeModel</code> qui prennent les arguments
<code class="classname">TreeIter</code> :
<code class="methodname">on_get_path</code>(),
<code class="methodname">on_get_value</code>(),
<code class="methodname">on_iter_next</code>(),
<code class="methodname">on_iter_children</code>(),
<code class="methodname">on_iter_has_child</code>(),
<code class="methodname">on_iter_n_children</code>(),
<code class="methodname">on_iter_nth_child</code>() et
<code class="methodname">on_iter_parent</code>().</p>
<p>La méthode <code class="methodname">on_get_path</code>() devrait retourner un chemin
de l'arborescence donnant un <em class="parameter"><code>refligne</code></em>. Par exemple, poursuivant l'exemple précédent
où le nom de fichier est utilisé comme <em class="parameter"><code>refligne</code></em>, on pourrait définir la méthode
<code class="methodname">on_get_path</code>() comme suit :
</p>
<pre class="programlisting">
def on_get_chemin(self, refligne):
return self.fichiers.index(refligne)
</pre>
<p>Cette méthode trouve l'index de la liste contenant le nom de fichier dans
<em class="parameter"><code>refligne</code></em>. Cet exemple montre clairement qu'un choix
judicieux de référence de ligne rend l'exécution plus efficiente. Par exemple,
on pourrait utiliser un dictionnaire Python pour relier
<em class="parameter"><code>refligne</code></em> à un chemin.
</p>
<p>La méthode <code class="methodname">on_get_value</code>() devrait renvoyer les
données rangées à la ligne et colonne indiquées par <em class="parameter"><code>refligne</code></em>
et <em class="parameter"><code>colonne</code></em>. Dans l'exemple :
</p>
<pre class="programlisting">
def on_get_value(self, refligne, colonne):
fname = os.path.join(self.nomrep, refligne)
try:
statutfich = os.stat(fname)
except OSError:
return None
mode = statutfich.st_mode
if colonne is 0:
if stat.S_ISDIR(mode):
return dossierpb
else:
return fichierpb
elif colonne is 1:
return refligne
elif colonne is 2:
return statutfich.st_size
elif colonne is 3:
return oct(stat.S_IMODE(mode))
return time.ctime(statutfich.st_mtime)
</pre>
<p>on doit extraire l'information de fichier associée et renvoyer la valeur
appropriée selon la colonne indiquée.
</p>
<p>La méthode <code class="methodname">on_iter_next</code>() devrait retourner une
référence de ligne à la ligne (de même niveau) après la ligne indiquée
par <em class="parameter"><code>refligne</code></em>. Dans l'exemple :
</p>
<pre class="programlisting">
def on_iter_next(self, refligne):
try:
i = self.fichiers.index(refligne)+1
return self.fichiers[i]
except IndexError:
return None
</pre>
<p>L'index du nom de fichier de <em class="parameter"><code>refligne</code></em> est établi
et le nom de fichier suivant est renvoyé ou s'il n'existe pas,
<code class="literal">None</code> est renvoyé.
</p>
<p>La méthode <code class="methodname">on_iter_children</code>() devrait retourner une
référence de ligne vers la première ligne enfant de la ligne indiquée par
<em class="parameter"><code>refligne</code></em>. Si <em class="parameter"><code>refligne</code></em> vaut
<code class="literal">None</code>, une référence à la première ligne du niveau
supérieur est retournée. S'il n'existe pas de ligne enfant,
<code class="literal">None</code> est retourné. Dans l'exemple :
</p>
<pre class="programlisting">
def on_iter_children(self, refligne):
if refligne:
return None
return self.fichiers[0]
</pre>
<p>Puisque le modèle est une liste, seul le niveau supérieur
(<em class="parameter"><code>refligne</code></em>=<code class="literal">None</code>) peut posséder
des lignes enfant. <code class="literal">None</code> est retourné si
<em class="parameter"><code>refligne</code></em> contient un nom de fichier.
</p>
<p>La méthode <code class="methodname">on_iter_has_child</code>() doit renvoyer
<code class="literal">TRUE</code> si la ligne indiquée par <em class="parameter"><code>refligne</code></em>
possède des lignes enfant, <code class="literal">FALSE</code> sinon. Dans l'exemple, on renvoie
<code class="literal">FALSE</code> puisque aucune ligne ne peut posséder d'enfant :
</p>
<pre class="programlisting">
def on_iter_has_child(self, refligne):
return False
</pre>
<p>La méthode <code class="methodname">on_iter_n_children</code>() renvoie le nombre
de lignes enfant que posséde la ligne indiquée par <em class="parameter"><code>refligne</code></em>.
Si <em class="parameter"><code>refligne</code></em> vaut <code class="literal">None</code>, on renvoie le
nombre de lignes du niveau supérieur. Dans l'exemple, on renvoie 0 si
<em class="parameter"><code>refligne</code></em> ne vaut pas <code class="literal">None</code> :
</p>
<pre class="programlisting">
def on_iter_n_children(self, refligne):
if refligne:
return 0
return len(self.fichiers)
</pre>
<p>La méthode <code class="methodname">on_iter_nth_child</code>() renvoie une
référence de ligne à la énième ligne enfant de la ligne indiquée par
<em class="parameter"><code>parent</code></em>. Si <em class="parameter"><code>parent</code></em> vaut
<code class="literal">None</code>, une référence à la énième ligne du niveau
supérieur est retournée. Dans l'exemple, on retourne une référence à
la énième ligne du niveau supérieur si <em class="parameter"><code>parent</code></em>
vaut <code class="literal">None</code>, <code class="literal">None</code> sinon :
</p>
<pre class="programlisting">
def on_iter_nth_child(self, refligne, n):
if refligne:
return None
try:
return self.fichiers[n]
except IndexError:
return None
</pre>
<p>La méthode <code class="methodname">on_iter_parent</code>() renvoie une référence de
ligne à la ligne parent de la ligne indiquée par <em class="parameter"><code>refligne</code></em>.
Si <em class="parameter"><code>refligne</code></em> pointe sur une ligne du niveau supérieur, on
renvoie <code class="literal">None</code>. Dans l'exemple, <code class="literal">None</code> est
toujours renvoyé en supposant que <em class="parameter"><code>refligne</code></em> doit pointer
sur une ligne du niveau supérieur :
</p>
<pre class="programlisting">
def on_iter_parent(child):
return None
</pre>
<p>Ces exemples sont assemblés dans le programme
<a href="exemples/listefichiers-gtm.py" target="_top">listefichiers-gtm.py</a>. La
<a href="sec-GenericTreeModel.html#listefichiers-gtmfig" title="Figure 14.11. Exemple de TreeModel générique">Figure 14.11, « Exemple de TreeModel générique »</a> montre le résultat
</p>
<div class="figure">
<a name="listefichiers-gtmfig"></a><p class="title"><b>Figure 14.11. Exemple de TreeModel générique</b></p>
<div class="mediaobject" align="center"><img src="figures/listefichiers-gtm.png" align="middle" alt="Exemple de TreeModel générique."></div>
</div>
</div>
<div class="sect2" lang="fr">
<div class="titlepage"><div><div><h3 class="title">
<a name="sec-AddRemoveGenericTreeModelRows"></a>14.11.3. Ajouter et supprimer des lignes</h3></div></div></div>
<p>Le programme <a href="exemples/listefichiers-gtm.py" target="_top">listefichiers-gtm.py</a> calcule la liste
des noms de fichier lorsqu'il crée une instance de <code class="classname">FileListModel</code>.
Si l'on souhaite vérifier réguliérement la création de nouveaux fichiers et ajouter ou
retiter des fichiers du modèle, on peut soit créer un nouveau
<code class="classname">FileListModel</code> pour le même répertoire, soit ajouter une méthode
pour ajouter ou retirer des lignes dans le modèle. Selon le type de modèle créé, il
peut être nécessaire d'ajouter une méthode semblable à celles des modèles de
<code class="classname">TreeStore</code> et de <code class="classname">ListStore</code> :
</p>
<div class="itemizedlist"><ul type="disc">
<li>
<code class="methodname">insert</code>()</li>
<li>
<code class="methodname">insert_before</code>()</li>
<li>
<code class="methodname">insert_after</code>()</li>
<li>
<code class="methodname">prepend</code>()</li>
<li>
<code class="methodname">append</code>()</li>
<li>
<code class="methodname">remove</code>()</li>
<li>
<code class="methodname">clear</code>()</li>
</ul></div>
<p>Il n'est, bien sûr, pas nécessaire d'utiliser toutes ou même
une seule de ces méthodes. On peut créer ses propres méthodes plus adaptées
à son modèle.
</p>
<p>En utilisant le programme exemple prédécent pour montrer l'ajout de
méthodes pour supprimer ou ajouter des fichiers, voici comment on implémente
les méthodes
</p>
<code class="methodsynopsis"> def <span class="methodname">remove</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">add</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>filename</code></strong></span></span>)</code><br><p>La méthode <code class="methodname">remove</code>() retire le fichier indiqué par le
paramétre <em class="parameter"><code>iter</code></em>. Outre retirer la ligne du modèle, la
méthode efface aussi le fichier du répertoire. Évidemment, si l'utilisateur
n'a pas les droits de suppression du ficher, la ligne n'est pas supprimée
non plus. Par exemple :
</p>
<pre class="programlisting">
def remove(self, iter):
path = self.get_path(iter)
pathname = self.get_pathname(path)
try:
if os.path.exists(pathname):
os.remove(pathname)
del self.files[path[0]]
self.row_deleted(path)
except OSError:
pass
return
</pre>
<p>La méthode transmet un <code class="classname">TreeIter</code> qu'il faut
transformer en un chemin à utiliser pour récupérer le chemin du fichier par
la méthode <code class="methodname">get_pathname</code>(). Il est possible que le
fichier ait déjà été supprimé, il faut donc tester son existence avant de
le supprimer. Si une exception OSError intervient pendant la suppression
du fichier, c'est probablement parce que le fichier est dans un répertoire
où l'utilisateur n'a pas suffisamment de droits. Finalement, le fichier
est supprimé et le signal "row-deleted" est émis par la méthode
<code class="methodname">rows_deleted</code>(). Le signal "file-deleted" indique
aux <code class="classname">TreeView</code> utilisant le modèle que ce modèle a changé,
ainsi ils peuvent mettre à jour leur état interne et afficher le modèle
modifié.
</p>
<p>La méthode <code class="methodname">add</code>() impose de créer un fichier dans le
répertoire courant avec le nom donné. Si le fichier est créé, son nom est
ajouté à la liste des fichiers du modèle. Par exemple :
</p>
<pre class="programlisting">
def add(self, filename):
pathname = os.path.join(self.dirname, filename)
if os.path.exists(pathname):
return
try:
fd = file(pathname, 'w')
fd.close()
self.dir_ctime = os.stat(self.dirname).st_ctime
files = self.files[1:] + [filename]
files.sort()
self.files = ['..'] + files
path = (self.files.index(filename),)
iter = self.get_iter(path)
self.row_inserted(path, iter)
except OSError:
pass
return
</pre>
<p>Cet exemple simple s'assure que le fichier n'existe pas déjà, puis tente
d'ouvrir le fichier en écriture. Si cela réussit, le fichier est refermé
et son nom inséré dans la liste de fichiers. Le chemin et le
<code class="classname">TreeIter</code> de la ligne du fichier ajouté sont
récupérés pour être utilisés dans la methode
<code class="methodname">row_inserted</code>() qui émet le signal
"row-inserted". Ce signal "row-inserted" sert à indiquer aux
<code class="classname">TreeView</code> utilisant le modèle qu'ils doivent
mettre à jour leur état et rafraîchir leur affichage.
</p>
<p>Les autres méthodes mentionnées précèdemment (par exemple
<code class="methodname">append</code> et <code class="methodname">prepend</code>) n'ont
pas de signification pour cet exemple puisque le modèle sa liste de fichiers
triée.
</p>
<p>D'autres méthodes peuvent être utiles à utiliser dans un
<code class="classname">TreeModel</code> découlant du
<code class="classname">GenericTreeModel</code> :
</p>
<div class="itemizedlist"><ul type="disc">
<li>
<code class="methodname">set_value</code>()</li>
<li>
<code class="methodname">reorder</code>()</li>
<li>
<code class="methodname">swap</code>()</li>
<li>
<code class="methodname">move_after</code>()</li>
<li>
<code class="methodname">move_before</code>()</li>
</ul></div>
<p>L'implémentation de ces méthodes est similaire à celle des méthodes
précédentes. Il faut synchroniser le modèle et l'état externe, et ensuite indiquer
aux <code class="classname">TreeView</code> que le modèle a changé. Les méthodes suivantes
sont utilisées pour indiquer aux <code class="classname">TreeView</code> les
changement du modèle en envoyant le signal approprié :
</p>
<code class="methodsynopsis"> def <span class="methodname">row_changed</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>path</code></strong></span></span>, <span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">row_inserted</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>path</code></strong></span></span>, <span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">row_has_child_toggled</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>path</code></strong></span></span>, <span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">row_deleted</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>path</code></strong></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">rows_reordered</span>(<span class="methodparam"><span class="parameter"><strong class="parameter"><code>path</code></strong></span></span>, <span class="methodparam"><span class="parameter"><strong class="parameter"><code>iter</code></strong></span></span>, <span class="methodparam"><span class="parameter"><strong class="parameter"><code>new_order</code></strong></span></span>)</code><p></p>
</div>
<div class="sect2" lang="fr">
<div class="titlepage"><div><div><h3 class="title">
<a name="sec-GenericTreeModelMemoryManagement"></a>14.11.4. Gestion de la mémoire</h3></div></div></div>
<p>L'un des problèmes avec le <code class="classname">GenericTreeModel</code> est
que le <code class="classname">TreeIter</code> contient une référence à un
objet Python venent du modèle personnalisé. Puisque le
<code class="classname">TreeIter</code> peut être crée et initialisé dans un
module en C et être présent dans la pile, il n'est pas possible de
connaître le moment où le <code class="classname">TreeIter</code> est détruit
et la référence à l'objet Python n'est plus d'utilité. Donc l'objet
Python référencé dans un <code class="classname">TreeIter</code> voit par défaut
son cimpteur incrémenté, mais il n'est pas décrémenté lorque le
<code class="classname">TreeIter</code> est détruit. Ceci garantit que l'objet
Python ne peut être détruit quand il est utilisé par un
<code class="classname">TreeIter</code> et produire éventuellement une erreur
de segmentation. Malheureusement les comptes de référence supplémentaires
font que, au mieux l'objet Python aura un compte de référence excessif et,
au pire, il ne sera jamais libéré même lorsque il n'est pas utilisé.
Le dernier cas cause des fuites de mémoire et le premier, des fuites de
références.
</p>
<p>Pour parer à la situation où le <code class="classname">TreeModel</code> personnalisé
maintient une référence à l'objet Python jusqu'à ce qu'il ne soit plus disponible
(le <code class="classname">TreeIter</code> est invalide car le modèle a changé) et il
n'y a pas besoin de relacher les références, le <code class="classname">GenericTreeModel</code>
possède une propriété "leak-references". Par défaut, "leak-references" vaut
<code class="literal">TRUE</code> pour indiquer que le <code class="classname">GenericTreeModel</code>
relachera les références. Si "leak-references" vaut <code class="literal">FALSE</code>, le
compteur de références de l'objet Python ne sera pas incrémenté quand il sera référencé
dans un <code class="classname">TreeIter</code>. Ce qui signifie que le le
<code class="classname">TreeModel</code> personnalisé doit conserver une référence
à tous les objets Python utilisés dans un <code class="classname">TreeIter</code> jusqu'à
la destruction du modèle. Malheureusement, même ceci ne protège pas d'un mauvais
code qui tente d'utiliser un <code class="classname">TreeIter</code> sauvegardé dans un
<code class="classname">GenericTreeModel</code> différent. Pour se protéger contre ce cas de
figure, l'application doit conserver une référence à tous les objets Python
référencés dans un <code class="classname">TreeIter</code> pour toutes les instances du
<code class="classname">GenericTreeModel</code>. Naturellement, ceci a le le même effet
qu'une fuite de références.
</p>
<p>Avec PyGTK 2.4 et ultérieurs, les méthodes <code class="methodname">invalidate_iters</code>()
et <code class="methodname">iter_is_valid</code>() sont disponibles comme aide à la
gestion des <code class="classname">TreeIter</code> et des références des objets Python :
</p>
<pre class="programlisting">
generictreemodel.invalidate_iters()
result = generictreemodel.iter_is_valid(<strong class="parameter"><code>iter</code></strong>)
</pre>
<p>Ceci est particulièrement utile lorsque la propriété "leak-references" vaut
<code class="literal">FALSE</code>. Les modèles d'arbre dérivés du
<code class="classname">GenericTreeModel</code> sont protégés des problèmes de
<code class="classname">TreeIter</code> périmés car la validité des iters est automatiquement
vérifiée avec le modèle arbre.
</p>
<p>Si un modèle d'arbre personnalisé ne gère pas les iters persistants (par exemple,
<code class="literal">gtk.TREE_MODEL_ITERS_PERSIST</code> n'est pas établi dans le retour de
la méthode <code class="methodname">TreeModel.get_flags</code>(), il est possible d'appeler
la methode <code class="methodname">invalidate_iters</code>() pour annuler tous les
<code class="classname">TreeIter</code> en cours quand le modèle est modifié (après insertion
d'une nouvelle ligne par ex.). Le modèle d'arbre peut aussi utiliser n'importe quel objet
Python qui a été référencé par un <code class="classname">TreeIter</code> après l'appel à
la méthode <code class="methodname">invalidate_iters</code>().
</p>
<p>Les applications peuvent utiliser la méthode <code class="methodname">iter_is_valid</code>()
pour déterminer si un <code class="classname">TreeIter</code> est encore valide pour le
modèle d'arbre personnalisé.
</p>
</div>
<div class="sect2" lang="fr">
<div class="titlepage"><div><div><h3 class="title">
<a name="sec-OtherInterfaces"></a>14.11.5. Autres interfaces</h3></div></div></div>
<p>Les modèles <code class="classname">ListStore</code> et <code class="classname">TreeStore</code>
comprennent outre l'interface <code class="classname">TreeModel</code>, les interfaces
<code class="classname">TreeSortable</code>, <code class="classname">TreeDragSource</code> et
<code class="classname">TreeDragDest</code>. Le <code class="classname">GenericTreeModel</code>
ne comprend que l'interface <code class="classname">TreeModel</code>. Je pense que c'est
à cause de la référence directe au modèle dans le langage C par les modèles
<code class="classname">TreeView</code>, <code class="classname">TreeModelSort</code> et
<code class="classname">TreeModelFilter</code>. Créer et utiliser un <code class="classname">TreeIter</code>
exige un code collant au C pour l'interface avec le modèle d'arbre personnalisé Python
qui contient les données. Ce code collant est fourni par le <code class="classname">GenericTreeModel</code>
et il semble qu'il n'y a pas d'alternative purement Python de réaliser ceci car le
<code class="classname">TreeView</code> et les autres modèles appellent les fonctions du
GtkTreeModel en C en passant leur référence au modèle d'arbre personnalisé.
</p>
<p>L'interface <code class="classname">TreeSortable</code> nécessite aussi un code collant
au C pour agir sur le mécanisme de tri par défaut du <code class="classname">TreeViewColumn</code>
ainsi qu'il est expliqué dans la <a href="sec-TreeModelInterface.html#sec-SortingTreeModelRows" title="14.2.9. Ordonner les lignes d'un TreeModel">Section 14.2.9, « Ordonner les lignes d'un TreeModel »</a>.
Cependant un modèle personnalisé doit réaliser ses propres tris et une application
doit gérer l'utilisation des critéres de tri en prenant en compte les clics sur
les en-têtes des <code class="classname">TreeViewColumn</code> et en appelant les méthodes
de tri du modèle d'arbre personnalisé. Le modèle effectue la mise à jour du
<code class="classname">TreeView</code> en émettant le signal "rows-reordered" grace à
la méthode <code class="methodname">rows_reordered</code>() du <code class="classname">TreeModel</code>.
Ainsi le <code class="classname">GenericTreeModel</code> ne nécessite probablement pas
d'implémenter l'interface <code class="classname">TreeSortable</code>.
</p>
<p>Pareillement, le <code class="classname">GenericTreeModel</code> n'a pas besoin d'
implémenter les interfaces <code class="classname">TreeDragSource</code> et
<code class="classname">TreeDragDest</code> puisque le modèle d'arbre personnalisé
peut effectuer ses propres interfaces de glisser-déposer et l'application
peut gérer les signaux <code class="classname">TreeView</code> appropriés et faire
appel aux méthodes du modèle d'arbre personnalisé tant que nécessaire.
</p>
</div>
<div class="sect2" lang="fr">
<div class="titlepage"><div><div><h3 class="title">
<a name="sec-ApplyingGenericTreeModel"></a>14.11.6. Utilisation du GenericTreeModel</h3></div></div></div>
<p>Je crois que le <code class="classname">GenericTreeModel</code> ne devrait être
utilisé qu'en dernier ressort. Les objets standard du <code class="classname">TreeView</code>
comprennent des mécanismes puissants qui devraient être suffisants pour la
plupart des applications. Sans doute, il existe des applications qui peuvent
avoir besoin du <code class="classname">GenericTreeModel</code> mais il faudrait d'abord
essayer d'utiliser ce qui suit :
</p>
<table border="0" width="100%" bgcolor="#FFECCE">
<col align="left" valign="top" width="0*">
<tbody>
<tr>
<td>
<span class="term">Cell Data Functions</span></td>
<td><p>Comme il est montré dans la <a href="sec-CellRenderers.html#sec-CellDataFunction" title="14.4.5. Fonction d'affichage des données cellulaires">Section 14.4.5, « Fonction d'affichage des données cellulaires »</a>, les
fonctions de données des cellules peuvent être utilisées pour modifier et
même produire les données pour un affichage d'une colonne du
<code class="classname">TreeView</code>. On peut créer autant d'affichages de colonnes
avec des données générées que l'on souhaite. Cela donne beaucoup de contrôle
de la présentation de données d'une source de données sous-jacente.
</p></td>
</tr>
<tr>
<td>
<span class="term">TreeModelFilter</span></td>
<td><p>En PyGTK 2.4, le <code class="classname">TreeModelFilter</code>, comme indiqué dans la
<a href="sec-TreeModelSortAndTreeModelFilter.html#sec-TreeModelFilter" title="14.10.2. Le TreeModelFilter">Section 14.10.2, « Le TreeModelFilter »</a>, permet un fort contrôle sur
l'affichage des colonnes et lignes d'un <code class="classname">TreeModel</code>
enfant, y compris afficher seulement les lignes filles d'une ligne.
Les colonnes de données peuvent être produite d'une façon semblable à
l'utilisation des fonctions de données en cellule ; ici le modèle est
un <code class="classname">TreeModel</code> avec un nombre et un type de colonnes
indiqués alors qu'une fonction de données de cellule laisse les colonnes modèles
inchangées et modifie juste l'affichage dans un <code class="classname">TreeView</code>.
</p></td>
</tr>
</tbody>
</table>
<p>Si un <code class="classname">GenericTreeModel</code> doit être utilisé, il faut
veiller à :
</p>
<div class="itemizedlist"><ul type="disc">
<li>l'interface complète du <code class="classname">TreeModel</code> doit
être créée et être en mesure de fonctionner comme indiqué. I y a des finesses
qui peuvent induire des erreurs. Par opposition, le <code class="classname">TreeModel</code>
standard a été complètement testé.
</li>
<li>gérer les références des objets Python utilisés par un
<code class="classname">TreeIter</code> peut se révéler difficile, particulièrement
pour des programmes longs avec beaucoup d'affichages variés.
</li>
<li>il faut créer un interface pour ajouter, supprimer ou modifier
le contenu des lignes. Le lien d'un TreeIter aux objets de Python et aux rangées
modèles dans cette interface n'est pas trés élégant.
</li>
<li>le développement d'interfaces de glisser-déposer et de tri demande
un effort important, l'application doit trés probablement participer à ce que ces
interfaces soient entièrement fonctionnelles.
</li>
</ul></div>
</div>
</div>
<div class="navfooter">
<hr>
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="sec-TreeModelSortAndTreeModelFilter.html">Préc.</a> </td>
<td width="20%" align="center"><a accesskey="u" href="ch-TreeViewWidget.html">Chapitre parent</a></td>
<td width="40%" align="right"> <a accesskey="n" href="sec-GenericCellRenderer.html">Suiv.</a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">14.10. TreeModelSort et TreeModelFilter </td>
<td width="20%" align="center"><a accesskey="h" href="index.html">Table des matières</a></td>
<td width="40%" align="right" valign="top"> 14.12. The Generic CellRenderer</td>
</tr>
</table>
</div>
</body>
</html>