/usr/share/doc/python-gtk2-tutorial/html/sec-GenericTreeModel.html is in python-gtk2-tutorial 2.4-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 | <html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>14.11. The Generic TreeModel</title><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="PyGTK 2.0 Tutorial"><link rel="up" href="ch-TreeViewWidget.html" title="Chapter 14. Tree View Widget"><link rel="previous" href="sec-TreeModelSortAndTreeModelFilter.html" title="14.10. TreeModelSort and TreeModelFilter"><link rel="next" href="sec-GenericCellRenderer.html" title="14.12. The Generic CellRenderer"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">14.11. The Generic TreeModel</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="sec-TreeModelSortAndTreeModelFilter.html">Prev</a> </td><th width="60%" align="center">Chapter 14. Tree View Widget</th><td width="20%" align="right"> <a accesskey="n" href="sec-GenericCellRenderer.html">Next</a></td></tr></table><hr></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="sec-GenericTreeModel"></a>14.11. The Generic TreeModel</h2></div></div><div></div></div><p>When you find that the standard <tt class="classname">TreeModel</tt>s
are not sufficiently powerful for your application needs, you can use the
<tt class="classname">GenericTreeModel</tt> to build your own custom
<tt class="classname">TreeModel</tt> in Python. Creating a
<tt class="classname">GenericTreeModel</tt> may be useful when there are
performance issues with the standard <tt class="classname">TreeStore</tt> and
<tt class="classname">ListStore</tt> objects or when you want to directly
interface to an external data source (say, a database or filesystem) to save
copying the data into and out of a <tt class="classname">TreeStore</tt> or
<tt class="classname">ListStore</tt>.</p><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-GenericTreeModelOverview"></a>14.11.1. GenericTreeModel Overview</h3></div></div><div></div></div><p> With the <tt class="classname">GenericTreeModel</tt> you build and
manage your data model and provide external access though the standard
<tt class="classname">TreeModel</tt> interface by defining a set of class
methods. PyGTK implements the <tt class="classname">TreeModel</tt> interface and
arranges for your <tt class="classname">TreeModel</tt> methods to be called to
provide the actual model data.</p><p>The implementation details of your model should be kept completely
hidden from the external application. This means that the way that your
model identifies, stores and retrieves data is unknown to the
application. In general the only information that is saved outside your
<tt class="classname">GenericTreeModel</tt> are the row references that are
wrapped by the external <tt class="classname">TreeIter</tt>s. And these
references are not visible to the application.</p><p>Let's examine in detail the
<tt class="classname">GenericTreeModel</tt> interface that you have to
provide.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-GenericTreeModelInterface"></a>14.11.2. The GenericTreeModel Interface</h3></div></div><div></div></div><p>The <tt class="classname">GenericTreeModel</tt> interface consists of
the following methods that must be implemented in your custom tree
model:</p><code class="methodsynopsis"> def <span class="methodname">on_get_flags</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_n_columns</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_column_type</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>index</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_iter</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>path</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_path</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>rowref</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_get_value</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>rowref</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>column</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_next</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>rowref</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_children</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>parent</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_has_child</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>rowref</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_n_children</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>rowref</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_nth_child</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>parent</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>n</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">on_iter_parent</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>self</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>child</tt></i></span></span>)</code><br><p>You should note that these methods support all of the
<tt class="classname">TreeModel</tt> interface including:</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"><b class="parameter"><tt>index</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_iter</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>path</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_iter_from_string</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>path_string</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_string_from_iter</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>iter</tt></i></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"><b class="parameter"><tt>iter</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get_value</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>iter</tt></b></span></span>, <span class="methodparam"><span class="parameter"><b class="parameter"><tt>column</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_next</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>iter</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_children</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>parent</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_has_child</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>iter</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_n_children</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>iter</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_nth_child</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>parent</tt></b></span></span>, <span class="methodparam"><span class="parameter"><b class="parameter"><tt>n</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">iter_parent</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>child</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">get</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>iter</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>column</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>...</tt></i></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">foreach</span>(<span class="methodparam"><span class="parameter"><i class="parameter"><tt>func</tt></i></span></span>, <span class="methodparam"><span class="parameter"><i class="parameter"><tt>user_data</tt></i></span></span>)</code><p>To illustrate the use of the
<tt class="classname">GenericTreeModel</tt> I'll change the <a href="examples/filelisting.py" target="_top"><span><b class="command">filelisting.py</b></span></a>
example program and show how the interface methods are created. The <a href="examples/filelisting-gtm.py" target="_top"><span><b class="command">filelisting-gtm.py</b></span></a>
program displays the files in a folder with a pixbuf indicating if the file
is a folder or not, the file name, the file size, mode and time of last
change.</p><p>The <tt class="methodname">on_get_flags</tt>() method
should return a value that is a combination of:</p><table border="0" width="100%" bgcolor="#FFECCE"><col align="left" valign="top" width="0*"><tbody><tr><td><span class="term"><tt class="literal">gtk.TREE_MODEL_ITERS_PERSIST</tt></span></td><td><tt class="classname">TreeIter</tt>s survive all signals
emitted by the tree.</td></tr><tr><td><span class="term"><tt class="literal">gtk.TREE_MODEL_LIST_ONLY</tt></span></td><td>The model is a list only, and never has
children</td></tr></tbody></table><p>If your model has row references that are valid over row changes
(reorder, addition, deletion) then set
<tt class="literal">gtk.TREE_MODEL_ITERS_PERSIST</tt>. Likewise if your model is a
list only then set <tt class="literal">gtk.TREE_MODEL_LIST_ONLY</tt>. Otherwise,
return 0 if your model doesn't have persistent row references and it's a
tree model. For our example, the model is a list with persistent
<tt class="classname">TreeIter</tt>s.</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_get_flags(self):
return gtk.TREE_MODEL_LIST_ONLY|gtk.TREE_MODEL_ITERS_PERSIST
</pre></td></tr></table><p>The <tt class="methodname">on_get_n_columns</tt>() method should
return the number of columns that your model exports to the application. Our
example maintains a list of column types so we return the length of the
list:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
class FileListModel(gtk.GenericTreeModel):
...
column_types = (gtk.gdk.Pixbuf, str, long, str, str)
...
def on_get_n_columns(self):
return len(self.column_types)
</pre></td></tr></table><p>The <tt class="methodname">on_get_column_type</tt>() method should
return the type of the column with the specified
<i class="parameter"><tt>index</tt></i>. This method is usually called from a
<tt class="classname">TreeView</tt> when its model is set. You can either create
a list or tuple containing the column data type info or generate it
on-the-fly. In our example:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_get_column_type(self, n):
return self.column_types[n]
</pre></td></tr></table><p>The <tt class="classname">GenericTreeModel</tt> interface converts the
Python type to a GType so the following code:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
flm = FileListModel()
print flm.on_get_column_type(1), flm.get_column_type(1)
</pre></td></tr></table><p>would print:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
<type 'str'> <GType gchararray (64)>
</pre></td></tr></table><p>The following methods use row references that are kept as private
data in a <tt class="classname">TreeIter</tt>. The application can't see the row
reference in a <tt class="classname">TreeIter</tt> so you can use any unique
item you want as a row reference. For example in a model containing rows as
tuples you could use the tuple id as the row reference. Another example
would be to use a filename as the row reference in a model representing
files in a directory. In both these cases, the row reference is unchanged by
model changes so the <tt class="classname">TreeIter</tt>s could be flagged as
persistent. The PyGTK <tt class="classname">GenericTreeModel</tt> application
interface will extract your row references from
<tt class="classname">TreeIter</tt>s and wrap your row references in
<tt class="classname">TreeIter</tt>s as needed.</p><p>In the following methods <tt class="literal">rowref</tt> refers to an
internal row reference.</p><p>The <tt class="methodname">on_get_iter</tt>() method should return an
rowref for the tree path specified by <i class="parameter"><tt>path</tt></i>. The tree
path will always be represented using a tuple. Our example uses the file
name string as the rowref. The file names are kept in a list in the model so
we take the first index of the path as an index to the file name:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_get_iter(self, path):
return self.files[path[0]]
</pre></td></tr></table><p>You have to be consistent in your row reference usage since you'll
get a row reference back in method calls from the
<tt class="classname">GenericTreeModel</tt> methods that take
<tt class="classname">TreeIter</tt> arguments:
<tt class="methodname">on_get_path</tt>(),
<tt class="methodname">on_get_value</tt>(),
<tt class="methodname">on_iter_next</tt>(),
<tt class="methodname">on_iter_children</tt>(),
<tt class="methodname">on_iter_has_child</tt>(),
<tt class="methodname">on_iter_n_children</tt>(),
<tt class="methodname">on_iter_nth_child</tt>() and
<tt class="methodname">on_iter_parent</tt>().</p><p>The <tt class="methodname">on_get_path</tt>() method should return a
tree path given a rowref. For example, continuing the above example where
the file name is used as the rowref, you could define the
<tt class="methodname">on_get_path</tt>() method as:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_get_path(self, rowref):
return self.files.index(rowref)
</pre></td></tr></table><p>This method finds the index of the list containing the file name
in <i class="parameter"><tt>rowref</tt></i>. It's obvious from this example that a
judicious choice of row reference will make the implementation more
efficient. You could, for example, use a Python dict to map
<i class="parameter"><tt>rowref</tt></i> to a path.</p><p>The <tt class="methodname">on_get_value</tt>() method should return
the data stored at the row and column specified by
<i class="parameter"><tt>rowref</tt></i> and <i class="parameter"><tt>column</tt></i>. For our
example:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_get_value(self, rowref, column):
fname = os.path.join(self.dirname, rowref)
try:
filestat = statcache.stat(fname)
except OSError:
return None
mode = filestat.st_mode
if column is 0:
if stat.S_ISDIR(mode):
return folderpb
else:
return filepb
elif column is 1:
return rowref
elif column is 2:
return filestat.st_size
elif column is 3:
return oct(stat.S_IMODE(mode))
return time.ctime(filestat.st_mtime)
</pre></td></tr></table><p>has to extract the associated file information and return the
appropriate value depending on which column is specified.</p><p>The <tt class="methodname">on_iter_next</tt>() method should return a
row reference to the row (at the same level) after the row specified by
<i class="parameter"><tt>rowref</tt></i>. For our example:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_iter_next(self, rowref):
try:
i = self.files.index(rowref)+1
return self.files[i]
except IndexError:
return None
</pre></td></tr></table><p>The index of the <i class="parameter"><tt>rowref</tt></i> file name is
determined and the next file name is returned or <tt class="literal">None</tt> is
returned if there is no next file.</p><p>The <tt class="methodname">on_iter_children</tt>() method should
return a row reference to the first child row of the row specified by
<i class="parameter"><tt>rowref</tt></i>. If <i class="parameter"><tt>rowref</tt></i> is
<tt class="literal">None</tt>, a reference to the first top level row is
returned. If there is no child row <tt class="literal">None</tt> is returned. For
our example:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_iter_children(self, rowref):
if rowref:
return None
return self.files[0]
</pre></td></tr></table><p>Since the model is a list model only the top level
(<i class="parameter"><tt>rowref</tt></i>=<tt class="literal">None</tt>) can have child
rows. <tt class="literal">None</tt> is returned if <i class="parameter"><tt>rowref</tt></i>
contains a file name.</p><p>The <tt class="methodname">on_iter_has_child</tt>() method should
return <tt class="literal">TRUE</tt> if the row specified by
<i class="parameter"><tt>rowref</tt></i> has child rows; <tt class="literal">FALSE</tt>
otherwise. Our example returns <tt class="literal">FALSE</tt> since no row can
have a child:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_iter_has_child(self, rowref):
return False
</pre></td></tr></table><p>The <tt class="methodname">on_iter_n_children</tt>() method should
return the number of child rows that the row specified by
<i class="parameter"><tt>rowref</tt></i> has. If <i class="parameter"><tt>rowref</tt></i> is
<tt class="literal">None</tt>, the number of top level rows is returned. Our
example returns 0 if <i class="parameter"><tt>rowref</tt></i> is not
<tt class="literal">None</tt>:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_iter_n_children(self, rowref):
if rowref:
return 0
return len(self.files)
</pre></td></tr></table><p>The <tt class="methodname">on_iter_nth_child</tt>() method should
return a row reference to the nth child row of the row specified by
<i class="parameter"><tt>parent</tt></i>. If <i class="parameter"><tt>parent</tt></i> is
<tt class="literal">None</tt>, a reference to the nth top level row is
returned. Our example returns the nth top level row reference if
<i class="parameter"><tt>parent</tt></i> is <tt class="literal">None</tt>. Otherwise
<tt class="literal">None</tt> is returned:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_iter_nth_child(self, rowref, n):
if rowref:
return None
try:
return self.files[n]
except IndexError:
return None
</pre></td></tr></table><p>The <tt class="methodname">on_iter_parent</tt>() method should return
a row reference to the parent row of the row specified by
<i class="parameter"><tt>rowref</tt></i>. If <i class="parameter"><tt>rowref</tt></i> points to a
top level row, <tt class="literal">None</tt> should be returned. Our example
always returns <tt class="literal">None</tt> assuming that
<i class="parameter"><tt>rowref</tt></i> must point to a top level row:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
def on_iter_parent(child):
return None
</pre></td></tr></table><p>This example is put together in the <a href="examples/filelisting-gtm.py" target="_top">filelisting-gtm.py</a>
program. <a href="sec-GenericTreeModel.html#filelistinggtmfig" title="Figure 14.11. Generic TreeModel Example Program">Figure 14.11, “Generic TreeModel Example Program”</a> shows the result of running
the program.</p><div class="figure"><a name="filelistinggtmfig"></a><p class="title"><b>Figure 14.11. Generic TreeModel Example Program</b></p><div class="mediaobject" align="center"><img src="figures/filelisting-gtm.png" align="middle" alt="Generic TreeModel Example Program"></div></div></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-AddRemoveGenericTreeModelRows"></a>14.11.3. Adding and Removing Rows</h3></div></div><div></div></div><p>The <a href="examples/filelisting-gtm.py" target="_top">filelisting-gtm.py</a> program
calculates the list of file names while creating a
<tt class="classname">FileListModel</tt> instance. If you want to check for new
files periodically and add or remove files from the model you could either
create a new <tt class="classname">FileListModel</tt> for the same folder or you
could add methods to add and remove rows in the model. Depending on the type
of model you are creating you would need to add methods similar to those in
the <tt class="classname">TreeStore</tt> and <tt class="classname">ListStore</tt>
models:</p><div class="itemizedlist"><ul type="disc"><li><tt class="methodname">insert</tt>()</li><li><tt class="methodname">insert_before</tt>()</li><li><tt class="methodname">insert_after</tt>()</li><li><tt class="methodname">prepend</tt>()</li><li><tt class="methodname">append</tt>()</li><li><tt class="methodname">remove</tt>()</li><li><tt class="methodname">clear</tt>()</li></ul></div><p>Of course not all or any of these need to be implemented. You can
create your own methods that are more closely related to your model.</p><p>Using the above example program to illustrate adding methods for
removing and adding files, let's implement the methods:</p><code class="methodsynopsis"> def <span class="methodname">remove</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>iter</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">add</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>filename</tt></b></span></span>)</code><br><p>The <tt class="methodname">remove</tt>() method removes the file
specified by <i class="parameter"><tt>iter</tt></i>. In addition to removing the row
from the model the method also should remove the file from the folder. Of
course, if the user doesn't have the permissions to remove the file then the
row shouldn't be removed either. For example:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><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></td></tr></table><p>The method is passed a <tt class="classname">TreeIter</tt> that has to
be converted to a path to use to retrieve the file path using the method
<tt class="methodname">get_pathname</tt>(). It's possible that the file has
already been removed so we check if it exists before trying to remove it. If
an OSError exception is thrown during the file removal it's probably because
the file is a directory or the user doesn't have sufficient privilege to
remove it. Finally, the file is removed and the "row-deleted" signal is
emitted from the <tt class="methodname">rows_deleted</tt>() method. The
"file-deleted" signal notifies the <tt class="classname">TreeView</tt>s using
the model that the model has changed so that they can update their internal
state and display the revised model.</p><p>The <tt class="methodname">add</tt>() method needs to create a file
with the given name in the current folder. If the file was created its name
is added to the list of files in the model. For example:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><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></td></tr></table><p>This simple example makes sure that the file doesn't exist then
tries to open the file for writing. If successful, the file is closed and
the file name sorted into the list of files. The path and
<tt class="classname">TreeIter</tt> for the added file row are retrieved to use
in the <tt class="methodname">row_inserted</tt>() method that emits the
"row-inserted" signal. The "row-inserted" signal is used to notify the
<tt class="classname">TreeView</tt>s using the model that they need to update
their internal state and revise their display.</p><p>The other methods mentioned above (for example,
<tt class="methodname">append</tt> and <tt class="methodname">prepend</tt>) don't
make sense for the example since the model keeps the file list
sorted.</p><p>Other methods that may be worth implementing in a
<tt class="classname">TreeModel</tt> subclassing the
<tt class="classname">GenericTreeModel</tt> are:</p><div class="itemizedlist"><ul type="disc"><li><tt class="methodname">set_value</tt>()</li><li><tt class="methodname">reorder</tt>()</li><li><tt class="methodname">swap</tt>()</li><li><tt class="methodname">move_after</tt>()</li><li><tt class="methodname">move_before</tt>()</li></ul></div><p>Implementing these methods is similar to the above methods. You
have to synchronize the model with the external state and then notify the
<tt class="classname">TreeView</tt>s if the model has changed. The following
methods are used to notify the <tt class="classname">TreeView</tt>s of model
changes by emitting the appropriate signal:</p><code class="methodsynopsis"> def <span class="methodname">row_changed</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>path</tt></b></span></span>, <span class="methodparam"><span class="parameter"><b class="parameter"><tt>iter</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">row_inserted</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>path</tt></b></span></span>, <span class="methodparam"><span class="parameter"><b class="parameter"><tt>iter</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">row_has_child_toggled</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>path</tt></b></span></span>, <span class="methodparam"><span class="parameter"><b class="parameter"><tt>iter</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">row_deleted</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>path</tt></b></span></span>)</code><br><code class="methodsynopsis"> def <span class="methodname">rows_reordered</span>(<span class="methodparam"><span class="parameter"><b class="parameter"><tt>path</tt></b></span></span>, <span class="methodparam"><span class="parameter"><b class="parameter"><tt>iter</tt></b></span></span>, <span class="methodparam"><span class="parameter"><b class="parameter"><tt>new_order</tt></b></span></span>)</code><p></p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-GenericTreeModelMemoryManagement"></a>14.11.4. Memory Management</h3></div></div><div></div></div><p>One of the problems with the
<tt class="classname">GenericTreeModel</tt> is that
<tt class="classname">TreeIter</tt>s hold a reference to a Python object
returned from your custom tree model. Since the
<tt class="classname">TreeIter</tt> may be created and initialized in C code and
live on the stack, it's not possible to know when the
<tt class="classname">TreeIter</tt> has been destroyed and the Python object
reference is no longer being used. Therefore, the Python object referenced
in a <tt class="classname">TreeIter</tt> has by default its reference count
incremented but it is not decremented when the
<tt class="classname">TreeIter</tt> is destroyed. This ensures that the Python
object will not be destroyed while being used by a
<tt class="classname">TreeIter</tt> and possibly cause a segfault. Unfortunately
the extra reference counts lead to the situation that, at best, the Python
object will have an excessive reference count and, at worst, it will never
be freed even when it is not being used. The latter case leads to memory
leaks and the former to reference leaks.</p><p>To provide for the situation where the custom
<tt class="classname">TreeModel</tt> holds a reference to the Python object
until it is no longer available (i.e. the <tt class="classname">TreeIter</tt> is
invalid because the model has changed) and there is no need to leak
references, the <tt class="classname">GenericTreeModel</tt> has the
"leak-references" property. By default "leak-references" is
<tt class="literal">TRUE</tt> to indicate that the
<tt class="classname">GenericTreeModel</tt> will leak references. If
"leak-references" is set to <tt class="literal">FALSE</tt>, the reference count of
the Python object will not be incremented when referenced in a
<tt class="classname">TreeIter</tt>. This means that your custom
<tt class="classname">TreeModel</tt> must keep a reference to all Python objects
used in <tt class="classname">TreeIter</tt>s until the model is
destroyed. Unfortunately, even this cannot protect against buggy code that
attempts to use a saved <tt class="classname">TreeIter</tt> on a different
<tt class="classname">GenericTreeModel</tt>. To protect against that case your
application would have to keep references to all Python objects referenced
from a <tt class="classname">TreeIter</tt> for any
<tt class="classname">GenericTreeModel</tt> instance. Of course, this ultimately
has the same result as leaking references.</p><p>In PyGTK 2.4 and above the
<tt class="methodname">invalidate_iters</tt>() and
<tt class="methodname">iter_is_valid</tt>() methods are available to help
manage the <tt class="classname">TreeIter</tt>s and their Python object
references:</p><table border="0" bgcolor="#E0E0E0" width="100%"><tr><td><pre class="programlisting">
generictreemodel.invalidate_iters()
result = generictreemodel.iter_is_valid(<b class="parameter"><tt>iter</tt></b>)
</pre></td></tr></table><p>These are particularly useful when the "leak-references" property
is set to <tt class="literal">FALSE</tt>. Tree models derived from
<tt class="classname">GenericTreeModel</tt> are protected from problems with out
of date<tt class="classname"> TreeIters</tt> because the iters are automatically
checked for validity with the tree model.</p><p>If a custom tree model doesn't support persistent iters
(i.e. <tt class="literal">gtk.TREE_MODEL_ITERS_PERSIST</tt> is not set in the
return from the <tt class="methodname">TreeModel.get_flags</tt>() method), it
can call the <tt class="methodname">invalidate_iters</tt>() method to
invalidate all its outstanding <tt class="classname">TreeIter</tt>s when it
changes the model (e.g. after inserting a new row). The tree model can also
dispose of any Python objects, that were referenced by
<tt class="classname">TreeIter</tt>s, after calling the
<tt class="methodname">invalidate_iters</tt>() method.</p><p>Applications can use the <tt class="methodname">iter_is_valid</tt>()
method to determine if a <tt class="classname">TreeIter</tt> is still valid for
the custom tree model.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-OtherInterfaces"></a>14.11.5. Other Interfaces</h3></div></div><div></div></div><p>The <tt class="classname">ListStore</tt> and
<tt class="classname">TreeStore</tt> models support the
<tt class="classname">TreeSortable</tt>, <tt class="classname">TreeDragSource</tt>
and <tt class="classname">TreeDragDest</tt> interfaces in addition to the
<tt class="classname">TreeModel</tt> interface. The
<tt class="classname">GenericTreeModel</tt> only supports the
<tt class="classname">TreeModel</tt> interface. I believe that this is because
of the direct reference of the model at the C level by
<tt class="classname">TreeView</tt>s and the
<tt class="classname">TreeModelSort</tt> and
<tt class="classname">TreeModelFilter</tt> models. To create and use
<tt class="classname">TreeIter</tt>s requires C glue code to interface with the
Python custom tree model that has the data. That glue code is provided by
the <tt class="classname">GenericTreeModel</tt> and there appears to be no
alternative purely Python way of doing it because the
<tt class="classname">TreeView</tt>s and the other models call the GtkTreeModel
functions in C passing their reference to the custom tree model.</p><p>The <tt class="classname">TreeSortable</tt> interface would need C
glue code as well to work with the default
<tt class="classname">TreeViewColumn</tt> sort mechanism as explained in <a href="sec-TreeModelInterface.html#sec-SortingTreeModelRows" title="14.2.9. Sorting TreeModel Rows">Section 14.2.9, “Sorting TreeModel Rows”</a>. However a custom model can do
its own sorting and an application can manage the use of sort criteria by
handling the <tt class="classname">TreeViewColumn</tt> header clicks and calling
the custom tree model sort methods. The model completes the update of the
<tt class="classname">TreeView</tt>s by emitting the "rows-reordered" signal
using the <tt class="classname">TreeModel</tt>'s
<tt class="methodname">rows_reordered</tt>() method. Thus the
<tt class="classname">GenericTreeModel</tt> probably doesn't need to implement
the <tt class="classname">TreeSortable</tt> interface.</p><p>Likewise, the <tt class="classname">GenericTreeModel</tt> doesn't have
to implement the <tt class="classname">TreeDragSource</tt> and
<tt class="classname">TreeDragDest</tt> interfaces because the custom tree model
can implement its own drag and drop interfaces and the application can
handle the appropriate <tt class="classname">TreeView</tt> signals and call the
custom tree model methods as needed.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="sec-ApplyingGenericTreeModel"></a>14.11.6. Applying The GenericTreeModel</h3></div></div><div></div></div><p>I believe that the <tt class="classname">GenericTreeModel</tt> should
only be used as a last resort. There are powerful mechanisms in the standard
group of <tt class="classname">TreeView</tt> objects that should be sufficient
for most applications. Undoubtedly there are applications which may require
the use of the <tt class="classname">GenericTreeModel</tt> but you should
attempt to first use the following instead:</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>As illustrated in <a href="sec-CellRenderers.html#sec-CellDataFunction" title="14.4.5. Cell Data Function">Section 14.4.5, “Cell Data Function”</a>, cell data functions can be used to
modify and even synthesize the data for a <tt class="classname">TreeView</tt>
column display. You can effectively create as many display columns with
generated data as you wish. This gives you a great deal of control over the
presentation of data from an underlying data source.</p></td></tr><tr><td><span class="term">TreeModelFilter</span></td><td><p>In PyGTK 2.4, the <tt class="classname">TreeModelFilter</tt> as
described in <a href="sec-TreeModelSortAndTreeModelFilter.html#sec-TreeModelFilter" title="14.10.2. TreeModelFilter">Section 14.10.2, “TreeModelFilter”</a> provides a great
degree of control over the display of the columns and rows of a child
<tt class="classname">TreeModel</tt> including presenting just the child rows of
a row. Data columns can be synthesized similar to using Cell Data Functions
but here the model appears to be a <tt class="classname">TreeModel</tt> with the
number and type of columns specified whereas a cell data function leaves the
model columns unchanged and just modifies the display in a
<tt class="classname">TreeView</tt>.</p></td></tr></tbody></table><p>If a <tt class="classname">GenericTreeModel</tt> must be used you
should be aware that:</p><div class="itemizedlist"><ul type="disc"><li>the entire <tt class="classname">TreeModel</tt> interface must
be created and made to work as documented. There are subtleties that can
lead to bugs. By contrast, the standard <tt class="classname">TreeModel</tt>s
are thoroughly tested.</li><li>managing the references of Python objects used by
<tt class="classname">TreeIter</tt>s can be difficult especially for long
running programs with lots of variety of display.</li><li>an interface has to be developed for adding, deleting and
changing the contents of rows. There is some awkwardness with the mapping of
<tt class="classname">TreeIter</tt>s to the Python objects and model rows in
this interface.</li><li>there is significant effort in developing sortable and drag
and drop interfaces. The application probably needs to be involved in making
these interfaces fully functional.</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">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch-TreeViewWidget.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="sec-GenericCellRenderer.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">14.10. TreeModelSort and TreeModelFilter </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> 14.12. The Generic CellRenderer</td></tr></table></div></body></html>
|