Py-gtktree: Pythonic Tree Models and More

Author: Paul Pogonyshev
Contact: pogonyshev@gmx.net
Copyright: © 2008, 2009 Paul Pogonyshev

Table of Contents

Document Overview

This document describes Py-gtktree, a set of Pythonic implementations of gtk.TreeModel interface combined with utilities that simplify common tree-related tasks. The document consists of rationale for custom tree models, quick cookbook and, finally, an in-depth reference.

Read the rationale section if you are not sure why you should use these classes at all and what is problematic with standard GTK+ implementations of the interface. Skip to the cookbook to quickly start writing code and learn how to apply the models for your problem. And read the reference when you need to know more of what and how is possible to achieve.

Rationale for Custom Tree Models

This section is abstract discussion trying to highlight inconsistencies of standard GTK+ tree models with Python programming and advantages of custom models.

What’s Wrong with gtk.ListStore and gtk.TreeStore

Standard implementations of gtk.TreeModel interface — gtk.ListStore and gtk.TreeStore — are written in C and only wrapped in PyGTK for access from Python. Since in C there are no objects [1], these implementations are not written in object-oriented manner.

Typically, data in modern programs is presented in form of objects. So, each row in a list or tree usually represents single object in code. For instance, a row may contain name, email address and phone number taken from one instance of Person class. With stock tree model implementations you need to manually extract data from such objects and represent it as a set of separate values. This is not much of a problem for one-time model creation, but when you need the model to reflect the changes in its back-end or operate on data selected in a gtk.TreeView, things start to get ugly.

One quite common approach is to create a column (sometimes the only column at all) containing Python objects. Then you can, for instance, use gtk.TreeViewColumn.set_cell_data_func() method to fetch data from that object and set properties on cell renderers. However, this approach has certain flaws. There is the practical difficulty in that it requires some additional code. You still have to deal with those unwieldy gtk.TreeIter, gtk.TreeRowReference etc. objects. It can be viewed as a breach of model/view pattern where things are just done at wrong side — view, not model — though this is admittedly a corner case. It is a workaround over unfortunate interface and, as a workaround, not nice at all. Last but not least, you don’t get straightforward Pythonic access to objects in the model.

[1]Actually, it is possible to implement objects in C, as GObject proves. However, it is not easy and requires a lot of code just to define a class, not to mention virtual methods or properties. So, standard tree models could be object-oriented, but were probably not made such to avoid forcing a lot of complexities on users of the library.

Object-Based Tree Models

Py-gtktree defines a few new implementations of gtk.TreeModel interface, aimed at solving the problem outlined in the previous section. In these models each row directly corresponds to one object. Moreover, type of these objects is not fixed and you can use types defined in other parts of your program with small, if any, modifications.

Most advantages of object-based tree models roughly correspond to the problems I see with standard gtk.TreeModel implementations.

  • There is no data duplication between your back-end and tree model, as you can use the same objects in both places.
  • It is easier to swing rows around, because a row is one object, not a bunch of loosely related fields. This includes moving or copying rows between different models. Also, it is easier to relate user selection with actual objects.
  • Practically no need to use gtk.TreeIters, gtk.TreeRowReferences, etc. In general, interface is much closer to being ‘natively Pythonic’.
  • Lazy loading (load-on-demand) of individual values and whole tree structures. That is not possible or very cumbersome to implement with standard models.

All in all, I consider these models easier to use than standard ones and easier to blend with your existing data structures. The more complicated operations you perform on a data model, the more noticeable the difference is.

Major disadvantage is, of course, speed. Object-based models are implemented in Python and are therefore significantly slower than standard C-written gtk.ListStore and gtk.TreeStore. I didn’t measure actual difference, but it can easily be one or two orders of magnitude. So, using object-based models is not recommended if you have many thousands of rows. However, the speed of the models is probably comparable to one-column-as-Python-object workaround outlined in the previous section, although, again, I didn’t do any benchmarks.

All implementations of gtk.TreeModel interface below are based on PyGTK helper called gtk.GenericTreeModel. It has that unfortunate property that it leaks by default [2], but all object-based models are carefully written to not leak. Also note that your code should not depend on gtk.GenericTreeModel being a superclass: that can be changed later.

[2]Due to technical reasons Python objects used for creating gtk.TreeIter in the gtk.GenericTreeModel must have a reference, else memory corruption can happen. Therefore, by default, all such objects are added a virtual reference that is never removed, and so objects are leaked. However, models below all set leak_references to False to prevent this. Py-gtktree models are written in such a way that objects used for iterators are always referenced anyway.

Using Custom Tree Models

This section describes how to use object-based tree model implementations defined in module gtktree.model in your code. See the reference section for details on specific methods and properties.

Columns

In object-based tree models columns mostly serve presentational purposes and do not enforce any particular structure on row objects. In particular, you can use the same objects in different models with different columns. Columns are required by gtk.TreeModel interface, but are not something models described here really depend on.

A column for an object-based tree model is defined as a tuple of type and attribute. The type can be either gobject.GType or certain Python types (see gtk.ListStore documentation for details). Attribute can be either a string or any callable.

In the case of a string, column cell values will be retrieved as if with code getattr (row_object, attribute). Note that attributes can be anything Python supports, including properties. Additionally, attributes can be ‘dotted‘, similar to what operator.attrgetter() supports starting with Python 2.6. For instance, owner.address.street will first retrieve owner, then take attribute address on that and, finally, street on the address object.

If column attribute is a callable, it will be invoked with one argument — the row object — and should return cell value.

So, to summarize, you can create tree models like this:

def build_description (row_object):
    ...  # Returns some string computed from 'row_object'.

store = RowObjectListStore ([(str, 'name')
                             (str, 'location.url'),
                             (int, 'num_people'),
                             (str, build_description)])

Tuple of column declarations on a model is available as read-only property columns. The tuple is a custom subclass with one extra operation: if you ‘call‘ it with an attribute, it will return column number for that attribute. This is useful to create gtk.TreeViewColumn objects and in general bind attributes for cell renderers. E.g. with the model created above:

column = gtk.TreeViewColumn ('Name', gtk.CellRendererText (),
                             text = store.columns ('name'))

Of course, nothing prevents you from hard-coding column numbers either (0 in this case). Calling columns simply allows to avoid ‘magic numbers’.

Modifying the Row Objects

Object-based tree models do not restrict contained objects in any way and do not interact with them except for all that ‘containing’ thing. One consequence is that it is impossible for models to automatically know when a contained object changes. For instance, say you have some objects with attribute name in a model and the name of object X changes. Model, or more precisely, whatever displays it, needs to know that one row must be redrawn. However, since model cannot detect the change itself you have to tell it yourself by calling note_changes() method.

In general, this is the only case when you need to explicitly emit any standard GTK+ signals on the model (note_changes() is a more convenient variant of row_changed()). When you perform any structural modification of a model by adding, removing or reording rows or replacing one row object with another, such a change will be automatically detected and proper signal(s) will be emitted by the model itself. But when you change the row object itself, you have to tell the model about that manually.

This is not a flaw of object-based models. While standard gtk.ListStore and gtk.TreeStore don’t require you to call row_changed() on them, they still force you to modify rows only by using model value setting methods. Object-based models don’t restrict the way you change row objects in any way, but they need you to inform about such changes for proper operation.

Using the List Store

gtktree.model.RowObjectListStore is a replacement for the standard gtk.ListStore. It is a simple model for lists of data, i.e. structure without hierarchy. It very closely mimics normal Python list interface (see reference for details).

To construct a RowObjectListStore you pass a list (or any iterable) with column declarations and, optionally, initial model contents — another iterable. Column declarations are explained in more details in the previous section. The second argument works exactly as the only argument to the built-in list() function.

As with normal Python lists, you can access individual rows and perform most modifications using [] operation. Indices can be integers, gtk.TreeIters or slices. As built-in lists, RowObjectListStore also supports negative indices with the same semantics and restriction, e.g. -1 means last row in a non-empty list store.

RowObjectListStore fully conforms to description of signal emission in the previous section. You only need to manually emit signals with note_changes() method after changing any row objects. As with other methods, you can specify integral indices in place of gtk.TreeIters for this method.

Note that despite the similarity of interfaces, the model is not a subclass of Python list class. The easiest way to receive a true list of all row objects in a model is store[:] code (i.e. using ‘include all‘ slice).

Py-gtktree comes with several examples of using RowObjectListStore. Easiest one, examples/simple_list.py, demonstrates the model created for pre-existing objects. See examples/README for a full list.

Using the Tree Store

In addition to the list store described in the previous section, Py-gtktree naturally comes with RowObjectTreeStore. Unlike the previous model, this one lacks any direct methods of modifying its contents. Instead, all such functionality is delegated to nodes.

Constructing a RowObjectTreeStore is pretty much similar to creating a list store. You need to specify a list (any iterable) of column declarations and, optionally, initial content nodes. So, let’s have a look at what those nodes are.

Nodes

A row object can be seen as value, but in addition to value, you usually want to know its structural position, i.e. its position corresponding to other values. With lists it is simple — just numeric index is enough. With trees, things become complicated.

Here we need [3] another concept: a node. Objects of type TreeNode describe how row objects are positioned one against another and carry hierarchical information.

Interface TreeNode is specifically tailored for GTK+ needs, so it is not very easy to use. I recommend using DefaultTreeNode, which gives access to a normal Pythonic list of its child nodes. For instance, to add another node, insert it into the list of child nodes of its purported parent. You can even insert a whole subtree of nodes at once. Likewise, simply delete a node from its parent child nodes list to get rid of it.

It is important to understand that a tree is composed of nodes. In fact, a RowObjectTreeStore — the model — is not needed for a tree. You can build a tree out of nodes that are not attached to any model and perform some useful operations on it. However, once you want to have the tree displayed in any GTK+ widgets — gtk.TreeView or maybe gtk.ComboBox — a model becomes necessary. Additionally, when a tree is attached to a model, proper GObject signals will get emitted on that model when the tree is modified.

So, how exactly do you attach a tree to a model? When you already have such a tree you can expand it with more nodes or even other subtrees. You can pass initial nodes for the model’s tree to RowObjectTreeStore constructor. But the most general way is to use model’s root node.

[3]Strictly speaking, not really need. There could be other solutions, e.g. requiring that row objects themselves carry this information. However, this design was deemed the best available choice.

The Root Node

All trees have a root node: that without a parent. RowObjectTreeStore always have a reference to the root node of their tree. Such node is always created by the model’s constructor and initial contents nodes (if any) are attached to this root node as children. Later, if you need to add or remove nodes in the model’s tree, you can always access it through the root property.

As far as model user widgets are concerned, its root node is invisible. gtk.TreeView begins display with root’s direct children. In other words, what you see in the widget are nodes on levels 1 and up, while level 0 — the root — is not shown. Thus, row object stored in the root node of the model’s tree is not important for displaying purposes. By Py-gtktree conventions, it is the model itself.

Nodes, Row Objects and Duck Typing

Py-gtktree doesn’t use duck typing generally, but in a few instances, it was deemed too useful. Some methods and properties inherently belong to tree nodes, e.g. various drag’n’drop predicates. However, the main design principle is that tree models can contain anything and you don’t need to write specific classes just for a model (in this case, subclass TreeNode or DefaultTreeNode). You still can, of course, but it would be easier if you could just tweak row object classes a little — those, presumably, exist regardless of Py-gtktree.

To achieve that, nodes delegate such methods and properties to their row objects by default. So, for example, if a row object has property is_draggable, its node will automatically report its value as its own property. This way, you don’t need to write custom node class — just add that property to your row object class and it will be picked up with duck typing. Even better, such properties and methods are optional: if a row object doesn’t define them, its node will fall back to some useful defaults. E.g. with drag’n’drop it will fall back to allowing all kinds of drag operation.

Full list of properties and methods that are picked up from row objects with duck typing can be found in reference for TreeNode and LazyDefaultTreeNode classes.

Lazy Nodes

An often needed feature that is very cumbersome to achieve with standard implementations, is lazy tree structure loading. Luckily, with RowObjectTreeStore it is not difficult to code at all.

LazyDefaultTreeNode is meant to be subclassed. It provides two methods which should be overriden — lazy_has_children() and lazy_load(). The latter method is essential. It should return a list (any iterable) of nodes that will become children of the current node. Nodes in the returned list can in turn be lazy nodes, in which case they will be loaded on demand as well.

Method lazy_has_children() is optional. It should be overriden if it is possible to determine if there are children without first performing a full load. It is also possible for overriden method to unconditionally return true. In this case, all nodes will be shown with expander icon next to them, but when lazy_load() returns an empty list, the icon just disappears. Resulting user experience is somewhat confusing, but on the upside, the tree becomes really fast.

There is a simple program demonstrating such lazy loading in file examples/lazy_tree_structure.py. It displays directory tree and reads directory contents on demand only. Check stderr output of the program. The example also demonstrates what happens when lazy_has_children() unconditionally returns true.

Tree Traversal

gtk.TreeModel has a generic foreach() method. However, it is not quite easy to use. TreeNode class provides alternative ways of traversing trees, in a more Pythonic way.

The class has three methods — depth_first_order(), breadth_first_order() and traversal_postorder() — that return an iterator over node’s subtree. The returned iterators traverse over the tree in depth-first fashion (also known as preorder), breadth-first (level-order) and postorder correspondingly. Each method has an optional parameter to avoid iterating over the node itself: this is useful, because when you need to search a whole model, you are usually not interested in its root node, which is not even visible to the user.

So, to print contents of a RowObjectTreeStore in a breadth-first order, you can just use the following code:

for node in store.root.breadth_first_order (False):
    print node.row_object

In addition to being easier to use than the generic foreach(), Py-gtktree methods allow you to traverse arbitrary subtrees and even subtrees of nodes not attached to any model.

Finally, remember that you have full access to properties like parent_node, first_child_node or, with DefaultTreeNode, even full list of child_nodes. Using these properties you can easily code your own tree search algorithms.

Drag’n’Drop Between Object-Based Models

A common UI pattern is two lists, one containing all possible object, the other containing those objects user selected. It is usually possible to move objects between the lists with buttons or with drag’n’drop. In any case, enabling drag’n’drop between different tree views (and containing different models) is quite common.

To enable drag’n’drop in GTK+ you need to perform at least two things:

  1. Make sure models implement gtk.TreeDragSource and gtk.TreeDragDest and can accept drags from each another.
  2. Enable drag source and/or destination handling on the gtk.TreeViews.

Both items are very easy to achieve with Py-gtktree.

Object-based tree models implement the required interfaces and have built-in support to drag rows between themselves. Since rows are objects, that is actually pretty easy: row objects get transferred from one model to another. You don’t even need to have the same columns in source and destination models.

By default models accept drags from other RowObjectTreeModel with equal (and not None) value of object_type property. So, to enable drag’n’drop between models A and B you only need to set A.object_type and B.object_type to equal values.

Easiest way of achieving the second point is using TreeViewUtils.enable_standard_drags() function. All you need is to call it once for every gtk.TreeView you want to support drags.

Look at examples/drag_between_lists.py or examples/drag_between_tree_and_list.py for some demonstrations of drag’n’drop between models.

Details of Pythonic Implementations of gtk.TreeModel

This section serves as a reference, providing detailed description of methods and properties of object-based models and related classes.

Package Contents

Py-gtktree consists of the following public modules and classes:

  • Module gtktree.model

    • Abstract class RowObjectTreeModel
    • Class RowObjectListStore
    • Abstract class TreeNode
    • Class DefaultTreeNode
    • Class LazyDefaultTreeNode
    • Class RowObjectTreeStore
  • Module gtktree.util

    • Class TreeViewUtils

RowObjectTreeModel

gtktree.model.RowObjectTreeModel is the base class of all gtk.TreeModel implementations in Py-gtktree. Currently provides common handling of columns and a few drag’n’drop helper methods.

Interface Reference

RowObjectTreeModel (columns)

Initializes the model to use given columns. The argument must be any iterable with each element being a tuple of type and attribute for a column. Here, type is either gobject.GType or certain Python types (refer to gtk.ListStore documentation for details) and attribute is a string or a callable. Column value will be retrieved as getattr (row_object, attribute) for string attributes and as attribute (row_object) for callables.

This method must be used only from subclasses as RowObjectTreeModel is an abstract class.

Note:Currently, it is not possible to modify columns after a model is created. This may be implemented later.
Property columns (read-only)
Column declarations as specified for the constructors. Useful to create a model with the same columns, e.g. to drag objects from ‘available’ to ‘selected’ list. (Note that columns being the same is not a requirement for drag’n’drop to work, though.)
Property node_based (read-only)
Whether the model contains just row objects or row objects wrapped in TreeNodes. For the two standard implementation this is simple RowObjectListStore is not node-based, while RowObjectTreeStore is node-based.
note_changes (*rows)

Notify the model about changes in row(s) at given rows (any iterable). Each row can be specified either as a gtk.TreeIter or something the model understands (e.g. plain integer indices for RowObjectListStore). Changed rows can be listed in any order and can contain duplicates — those are simply ignored. Number of actually changed rows (i.e. after discarding all duplicates) is returned.

Remember that for proper display it is required to notify the model (and thus all widgets displaying it) of any changes in row objects. Unlike with stock GTK+ tree models, you don’t set cell values in the model, so changes in rows cannot be detected automatically.

Property object_type

Optionally can be set to the (expected) type of row objects in the tree model.

Currently used only by is_accepting_drags_from() method when foreign_drags_policy is None (its default value). See the method documentation for details.

get_row_object (iter)
Get the row object at given iter (a gtk.TreeIter instance). This method can be used regardless of whether the model is node-based or not.
get_node (iter)
Get the TreeNode at given iter (a gtk.TreeIter instance). This method can only be used on node-based models.
get_cell (row, attribute)
FIXME
set_cell (row, attribute, value)
FIXME
is_accepting_drags_from (source_model)

Determine if this model should accept drag’n’drop from source_model. Default implementation (used in all standard subclasses) bases its decision on the value of foreign_drags_policy property.

  • None: accept drags from other instances of RowObjectTreeModel when they have equal (and not None) object_type value to value of said property of this model. This is the default setting.
  • True or False: either accept all drags from other instances of RowObjectTreeModel or accept none at all.
  • Any callable, which will be invoked with two arguments: first destination (this model), than the source. This is the most flexible setting.
Property foreign_drags_policy
Policy of accepting drag’n’drop from different models. See is_accepting_drags_from() method documentation for details.

Internal Methods

These methods are meant only for overriding or usage by derived classes. They should be of no interest for users of standard models.

_do_get_row_object (row)
Return the row object corresponding to the passed in gtk.TreeIter user data. I.e. the argument is the data you return from methods like on_get_iter. This method is ‘abstract’ and must be implemented in subclasses.
_do_get_iter (row)
Get or create gtk.TreeIters object for row. The argument can be a gtk.TreeIter itself (in which case it should be returned unaltered) or any subclass-specific native representation of row position. For instance, list models could accept integer row indices.
_get_drag_data (selection_data)
FIXME

Subclassing Overview

If for any reason you decide to subclass RowObjectTreeModel directly, instead of one of its standard implementations, you need to define at least the following methods:

  • _do_get_row_object (row)
  • _do_get_iter (row)
  • on_get_flags ()
  • on_get_iter (path)
  • on_get_path (row)
  • on_iter_parent (row)
  • on_iter_next (row)
  • on_iter_has_child (row)
  • on_iter_children (row)
  • on_iter_n_children (row)
  • on_iter_nth_child (parent, n)

All of those methods except _do_get_row_object() and _do_get_iter() come from gtk.GenericTreeModel. RowObjectTreeModel defines on_get_n_columns(), on_get_column_type() and on_get_value() itself.

If you create a custom subclass, you should likely set props.leak_references to False. However, to do this and not cause memory corruption, you must understand why gtk.GenericTreeModel leaks by default and how to prevent this. Standard implementations all take special care to make setting the property to False safe.

RowObjectListStore

gtktree.model.RowObjectListStore is very similar to plain Python list and additionally provides interface similar to gtk.ListStore. It supports all list operations except for the following:

  • operator +: you cannot concatenate two models together;
  • operator *: you cannot create a model with several copies of another model objects;

Most methods of this class accept integers or gtk.TreeIters as indices. Like in normal Python list, integer indices can be negative, e.g. -1 stands for the last row. However, indices generally must not be smaller than -len (model). Methods that require you to specify something like insertion point (as opposed to specific row) silently clamp integer indices to valid range, similar to what list.insert() does (e.g. you can insert at point 10 in a three-row model). Finally, where Python list accepts slices so does this class.

Interface Reference

RowObjectListStore (columns, values = None)
Create a new RowObjectListStore with given columns (see RowObjectTreeModel section). Optionally, you can specify values (any iterable) the store will initially contain.
Property is_sorted (read-only)

Whether the list is sorted through gtk.TreeSortable interface, i.e. most likely by the user. Note that when the model is sorted, many methods will not work and raise an exception. Therefore, those methods must not be used for models that might ever become sortable at all.

Note:This doesn’t have anything to do with the sort() method. This latter actually works only on non-sorted models and doesn’t make them sorted from gtk.TreeSortable perspective. The is_sorted property can also be viewed as reporting ‘user-sortedness‘, while the method performs ‘programmatic sorting‘.
len (model)
Return number of rows in the model.
row_object in model
Determine if there is at least one row equal to row_object in the model. Remember, such row must be equal, not identical to row_object.
iter (model)

Iterate over all rows in the model, from first to last. This returns a normal Python iterator, which has nothing to do with gtk.TreeIter. Returned iterator can be used, for instance, in for ... in statement.

Note:Iterators don’t mix well with model modifications. Later an exception might be raised if you use the same Python iterator both before and after a modification of the corresponding model.
model[index]
Return the row at given position. index can be an integer, a slice or a gtk.TreeIter. If index is actually a slice, a list of specified rows is returned. In particular, model[:] returns a list of all object in the model.
model[index] = row_object
Change the row at given position. index can be either an integer a gtk.TreeIter. See also assignment to slices below.
model[i:j] = iterable
Replace row objects in the slice i:j with those taken from the iterable. There are no restrictions on the numbers of old or new rows, i.e. they are not required to be equal and either can or can not be zero. Similar to slice assignment in Python list.
model[i:j:k] = iterable
Replace row objects in the slice i:j:k with those taken from the iterable. As with standard Python list, it is required that length of the iterable exactly matches number of replaced rows. Note that there is no such restriction for non-extended slices (see above).
del model[index]
Remove the row at given position. index can be an integer, a slice or a gtk.TreeIter.
get_row_index (iter_or_path)
Convert a gtk.TreeIter or any PyGTK representaton of a tree path to numeric row index. This method is can be used to convert data provided by objects like gtk.TreeSelection into format native to the model — simple row indices.
index (row_object, i = None, j = None)
Identical to Python list method. Return the smallest index k such that model[k] == row_object and i <= k < j (if i and/or j is specified). Note that you can pass negative i or j: they will mean exactly the same as for the Python method.
count (row_object)
Identical to Python list method. Return number of indices k for which model[k] == row_object. For tree models this is hardly useful, but was implemented for completeness.
append (*row_objects)

Append one or more objects to the end of the model. With one argument this is identical to Python list method. Doesn’t invalidate outstanding gtk.TreeIters.

If you append one object, returns gtk.TreeIter pointing to it. Else returns None.

Note:When the store is sorted, the row or rows might not necessarily be appended. Rather, they will end up in the positions they should be to maintain current sorting.
prepend (*row_objects)
Same as model.insert (0, *row_objects). Implemented for consistency with other GTK+/PyGTK interfaces.
insert (index, *row_objects)

Insert one or more objects at given position. index can be either an integer, a gtk.TreeIter or None. In first two cases, row(s) are inserted before the row currently at that position. None means to insert at the end (same as len (model)). With one row object insert() is identical to Python list method. Unless you insert at the very end of the store, all outstanding gtk.TreeIters will be invalidated.

If you insert one object, returns gtk.TreeIter pointing to it. Else returns None.

Note that as with Python list.insert(), you can use out-of-bounds indices. Such indices will be silently clamped to valid range. E.g. you can insert at position 100 into a model with 5 objects — insertion will be done as if at position 5.

Note:When the store is sorted, index parameter is ignored. The row or rows will end up in the positions they should be to maintain current sorting.
insert_before (index, *row_objects)
Exactly the same as just insert(). Provided for similarity with gtk.ListStore.
insert_after (index, *row_objects)

Insert one or more objects after given position. index can be either an integer, a gtk.TreeIter or None. In first two cases, row(s) are inserted after the row currently at that position. None means to insert at the beginnign (same as len (model)). Unless you insert at the very end of the store, all outstanding gtk.TreeIters will be invalidated.

If you insert one object, returns gtk.TreeIter pointing to it. Else returns None.

Note:When the store is sorted, index parameter is ignored. The row or rows will end up in the positions they should be to maintain current sorting.
extend (iterable)
The same as append (*iterable) except that always returns None, regardless of iterable length.
remove (row_object)

Remove first row that is equal to row_object. If there is no such rows, raises ValueError.

Note that this method removes by value. To remove row at given index or gtk.TreeIter, use del model[...] code (also accepts Python slices), or remove_at() method.

remove_at (*indices)

Remove one or more rows at given indices. Each index can be either an integer or a gtk.TreeIter. Negative indices are allowed, but must not underflow. Indices can repeat — repetitions are just ignored. Note that all indices are checked prior to actually removing anything, so if you have an error (e.g. an index pointing out of the model), nothing will be removed and IndexError raised. Else number of actually removed rows is returned.

This method is useful to remove several unrelated rows at once, e.g. those selected in a gtk.TreeView with multiselection. With this method you don’t need to worry about creating gtk.TreeRowReference or removing in correct order. For removing one row or a set of rows that can be expressed in Python slice syntax, del model[...] may be more straightforward.

pop (index = -1)
Identical to Python list method except that index can also be a gtk.TreeIter. The argument defaults to -1, so that by default last row will be removed and returned.
clear ()
Removes all rows in the model. Same as del model[:].
reorder (new_order)

Reorder the rows in the model. new_order must be a list (or any other iterable) of integers from 0 to len (model) - 1, which don’t repeat. In other words, new_order must be a permutation of valid row indices. After reordering row at position k the same as old row at position new_order[k].

Note:This method cannot be used if the store is sorted. If that’s the case, it will raise a RuntimeError.
reverse ()

Reverse the rows in the model so that first becomes last, second becomes next to last and so on.

Note:This method cannot be used if the store is sorted. If that’s the case, it will raise a RuntimeError.
sort (cmp = None, key = None, reverse = False)

Sorts the row of the model using specified callbacks or else natural Python object order. For details on what arguments mean, see documentation of list.sort().

Note:This method cannot be used if the store is sorted. If that’s the case, it will raise a RuntimeError. See also the remark in is_sorted documentation: that property is very different from the sort() method.
swap (index1, index2)

Swap two rows at given indices. Each index can be an integer or a gtk.TreeIter. Integers can be negative, but must not underflow. If either integer is out of bounds, IndexError is raised.

Note:This method cannot be used if the store is sorted. If that’s the case, it will raise a RuntimeError.
move_to (from_index, to_index)

Move a row from one position to another. First index must be a valid integer (as in list) or a gtk.TreeIter. Second can be any integer or a gtk.TreeIter — out-of-range integer indices are silently clamped to valid range as in list.insert().

After the move, row that was at from_index will be exactly at to_index. Rows between the two positions (excluding the first) will shift one step up or down.

Note:This method cannot be used if the store is sorted. If that’s the case, it will raise a RuntimeError.
move_before (from_index, before_index)

Move a row so it immediately precedes another row. Both indices must be valid integers (as in list) or gtk.TreeIters. If both indices specify the same row, an IndexError is raised.

After the move, row that was at from_index will be exactly before the row that was at to_index. If from_index == to_index - 1, nothing will be done.

Note:This method cannot be used if the store is sorted. If that’s the case, it will raise a RuntimeError.
move_after (from_index, after_index)

Move a row so it immediately follows another row. Both indices must be valid integers (as in list) or gtk.TreeIters. If both indices specify the same row, an IndexError is raised.

After the move, row that was at from_index will be exactly after the row that was at to_index. If from_index == to_index + 1, nothing will be done.

Note:This method cannot be used if the store is sorted. If that’s the case, it will raise a RuntimeError.

Internal Methods

These methods are meant only for overriding or usage by derived classes. They should be of no interest for users of instances of this class.

_do_insert (index, row_objects, after = False)
FIXME
_do_insert_sorted (row_objects)
FIXME
_do_remove_at (indices)
FIXME
_do_move (from_index, to_index)
FIXME
_get_existing_row_index (index)
FIXME
_get_insertion_point (index, after = False)
FIXME
_create_index_error (index)
FIXME

Subclassing Overview

RowObjectListStore is not abstract, so you don’t need to subclass it for normal use. If you do, however, it is strongly recommended to use model methods like insert() or subscription access when you need to modify the model. Since the class implements gtk.TreeSortable it is very difficult to meddle with its internal attributes properly: order in lists can change in response to user actions.

However, if you absolutely need it, you have access to _values and _indices attributes. If you feel like using them, look at the source code. On the upside, knowing the names you can avoid using them in subclasses.

TreeNode

Nodes are building blocks of trees. Class TreeNode is abstract and defines minimum set of methods and properties needed to implement tree models. For actual use, I recommend DefaultTreeNode or its subclass, LazyDefaultTreeNode. However, many properties and methods defined on this base class are useful in their own right.

Property is_draggable and methods accepts_dragged_child_node() and accepts_parent_node_after_drag() use duck typing to pick up similarly named attributes from node’s row object. In other words, you can choose not to subclass a node class, but instead add these attributes to your row object class as needed.

Interface Reference

Property row_object (read-only)
The row object contained in the node. Most importantly, the row object is used do determine values used for display. It is allowed for the row object to be the node itself, though that’s only useful when use custome node subclass.
Property parent_node (read-only)
A node that serves as a parent of this one. Can be None, in which case this node is a root of its own subtree.
Property next_node (read-only)
The next child of the parent node or None if this node is the last child. By definition, always None if this node doesn’t have a parent node.
Property previous_node (read-only)

The previous child of the parent node or None if this node is the first child. By definition, always None if this node doesn’t have a parent node.

Note that this property is computed and is considerably slower than next_node.

Property has_child_nodes_currently (read-only)
Determine if the node has child nodes at the moment. The node shouldn’t perform any self-modifications. E.g. if it is a lazy node, it shouldn’t load its children. In other words, it is possible that has_child_nodes_currently is False, but becomes True e.g. after evaluating has_child_nodes property.
Property has_child_nodes (read-only)
Determine if the node has child nodes. The node is allowed to perform required self-modifications. E.g. if it is a lazy node, it can load its children, if any. In other words, it is possible that has_child_nodes will return True even when has_child_nodes_currently is False.
Property num_child_nodes (read-only)
Return the number of child nodes this node has. You should use has_child_nodes or has_child_nodes_currently if all you need is to determine whether this number is zero or not.
Property first_child_node (read-only)
Return the first child of this node, or None if there are no children.
get_child_node (index)
Return child node at given index or raise IndexError if the index is too large. In particular, if this node is a leaf, the method will always raise.

get_child_node_index (child)

Determine the position of given child node in this node’s child list. If child is not actually a child of this node, raises ValueError.
get_tree_model ()
Returns the tree model (likely a RowObjectTreeStore) this node or one of its parent is attached to or None if the node is part of a free-standing tree. All nodes in belonging to one tree will return the same value.

compute_path ()

Compute path of the node within its tree. The tree doesn’t need to be attached to a model for this method to work. Path can be recursively determined like this:

  • a root node has an empty tuple as its path: ();
  • path of any other node is path of its parent node extended with its index in parent’s child list.

For instance, second child of a root node will have (1,) as its path.

For trees attached to a model this is the same as GTK+ tree path. The only exception is the root node, but since it is not shown to the user and is ‘virtual‘ in some sense, that doesn’t matter.

delete_child_node (child)

Remove given child from the list of this node children. The child becomes a root node in its own right and can be, for instance, reinserted in another place or even into a different tree. If child doesn’t actually have this node as a parent, raises ValueError.

insert_child_node (child, before = None)

Make given node a child of this node. If before is not specified or is None, make it the last child. Otherwise, insert it right before in the child list. Raises ValueError if child already has a parent or before is specified, but is not a child of this node.
Class method create_tree (data)

Create a node tree from given data. The data should be any iterable. Its first element is taken as row object of new node. All consecutive elements should be in the same format as data itself and will be used to create child nodes for the new node.

It may be easier to understand from a self-explanatory example:

DefaultTreeNode.create_tree (['Root',
                              ['Root child 1',
                               ['Child of the first root child']],
                              ['Root child 2']])

This will create a tree of four nodes. Root node will have two children and its first child will have its own child node. Strings will be used as row objects in the nodes.

Note that row_object property of the node class must be writable for this method to work. DefaultTreeNode qualifies.

hierarchic_copy ()

Create a standalone copy of subtree originating at this node. The new tree will have the same composition, same node types and row objects will be the same as row objects in this subtree. Node objects, however, will be different.

This method is used by RowObjectTreeStore in drag’n’drop code, but you may use it as well, if needed.

Property is_draggable (read-only)

Determine if it is allowed to drag this node.

By default, if node’s row object has a similar named property, the node will return its value. Otherwise, nodes default to be draggable.

accepts_dragged_child_node (child_node)

Determine if child_node is allowed to be dragged into this node.

By default, if node’s row object has a similar named method, node will invoke it pass the returned value as its own. Otherwise, nodes default to accept all dragged in childs.

accepts_parent_node_after_drag (parent_node)

Determine if the node ‘agrees‘ to be dragged into given parent_node.

By default, if node’s row object has a similar named method, node will invoke it pass the returned value as its own. Otherwise, nodes default to accept any parent.

depth_first_order (include_self = True)

Return an iterator that visits all nodes of subtree originating at this node in a depth-first fashion. If the optional argument include_self is False, the node itself is excluded from iteration (this is mostly useful when iterating model trees where the root is ‘virtual‘ anyway).

The returned iterator is lazy in that way it scans the subtree only when another node is requested. I.e. it doesn’t build a full list of returned nodes first.

Refer to the tree traversal section for more information and an example.

Note:Currently, it is not allowed to structuraly modify the subtree in any way while the iterator is being used. This restriction may be alleviated later.
traversal_postorder (include_self = True)
Return an iterator that visits all nodes of subtree originating at this node in a breadth-first fashion. See depth_first_order() documentation for more information.
breadth_first_order (include_self = True)
Return an iterator that traverses all nodes of subtree originating at this node in postorder fashion. See depth_first_order() documentation for more information.

DefaultTreeNode

DefaultTreeNode is the standard implementation of TreeNode. It provides very convenient access to its child nodes: all such nodes are readily available as a Pythonic list. The list can be inspected or altered in any standard way. For instance, to add another child at the first position you can just use the following code:

node.child_nodes.insert (0, new_child)

Interface Reference

DefaultTreeNode (row_object = None, child_nodes = None)
Construct a new node with given initial row object and child nodes (can be any iterable). Note that both arguments are optional and are just shortcuts: you can change the row object as well as modify child node list later.
Property row_object
The row object contained in the node. Unlike the base class, in DefaultTreeNode this property is writable.
Property child_nodes (read-only)

Pythonic list of child nodes of this node. Note that while the list itself cannot be replaced, you can freely modify its contents by adding or removing children. All elements must be instances of TreeNode (e.g. other DefaultTreeNode). To help with debugging, the list will immediately raise a TypeError exception if you try to insert anything else.

The returned list is actually an instance of a subclass of the standard list.

LazyDefaultTreeNode

LazyDefaultTreeNode is a subclass of DefaultTreeNode with support for lazy load of its child nodes (load on demand). Main incentive for such loading is to never build parts of a tree that isn’t going to even be seen by user. If the (fully loaded) tree is very large, one can achieve significant speed improvement this way.

Methods lazy_has_children() and lazy_load() use duck typing to pick up similarly named methods from node’s row object. In other words, you can choose not to subclass LazyDefaultTreeNode, but instead add these methods to your row object class as needed.

Note that the only required method is lazy_load(), which, as stated above, can be implemented in a subclass or row object class.

Interface Reference

LazyDefaultTreeNode (row_object = None, child_nodes = None)
Construct a new node with given initial row object and child nodes (can be any iterable). This is very similar to the constructor of DefaultTreeNode. If child_nodes is not empty, the node will consider itself in loaded state from the start.
lazy_has_children ()

Determine if the node will have children after loading. If this method returns False, node immediately leaves ‘unloaded‘ state. If it returns True, it stays unloaded, but pretends to have children, assuming that it will, after loading.

It is allowed to err with the return value, i.e. return True even if after loading there will be no children. The node will cope with such a situation, though until it is loaded it will pretend to have children (usually by displaying expand icon in gtk.TreeView), leading to somewhat confusing user experience. However, in some cases it is worth it when determining if there actually will be child nodes is slow.

If the method is not overriden or defined on node’s row object, node defaults to loading and just looking at has_child_node property. In that sense, the method is optional.

Default implementation looks at the row object: if it has a similar named method, the node will return its value.

lazy_load ()

Load the node and return actual contents. Returned value should be any iterable (possibly empty) with each element being a TreeNode or any object. Elements that are not instances of TreeNode are wrapped in one as row objects. Depending on whether they have a lazy_load() attribute themselves, they will be wrapped in a lazy node (yes) or just plain DefaultTreeNode (no).

Default implementation looks at the row object: if it has a similar named method, the node will return its value. However, as the method is not optional, default implementation will raise a NotImplementedError if there is no such method in the row object. So, either row object should supply method implementation or you need to subclass LazyDefaultTreeNode and override the method.

TreeViewUtils

A collection of static utility functions aimed at simplifying common gtk.TreeView-related tasks.

The four get_selected_*() functions retrieve current selection in a gtk.TreeView in various forms.

Two enable_*() functions encapsulate standard pieces of code needed to enable drag’n’drop and inline editing in tree views. Both functions are currently simple and provide no additional means to configure this behavior. They might grow additional parameters in future Py-gtktree versions.

Finally, *_viewport_*() functions provide facilities needed to implement lazy value loading.

Interface Reference

get_selected_row (tree_view)

Return a tuple of gtk.TreeIter and row object selected in the given view. If there is no selection, returns a tuple of two None objects.

Note that the model of tree_view must be an instance of RowObjectTreeModel.

get_selected_node (tree_view)

Return the node selected in the given view or None if there is no selection. Unlike the previous function doesn’t return a gtk.TreeIter: it is not needed with nodes.

Note that the model of tree_view must be an instance of RowObjectTreeModel and be node-based (e.g. a RowObjectTreeStore).

get_selected_row_list (tree_view)

Return a possibly empty list of tuples of gtk.TreeIter and row objects selected in the given view.

Note that the model of tree_view must be an instance of RowObjectTreeModel.

get_selected_node_list (tree_view)

Return a possibly empty list of nodes selected in the given view. Unlike the previous function doesn’t return gtk.TreeIters: they are not needed with nodes.

Note that the model of tree_view must be an instance of RowObjectTreeModel and be node-based (e.g. a RowObjectTreeStore).

enable_standard_drags (tree_view)
Enable standard drag’n’drop targets on the tree_view. After the call, tree_view will become a valid target for dragging rows both from and to. Refer to RowObjectTreeModel.is_accepting_drags_from() method documentation for how to enable model-level support for drag’n’drop.

enable_renderer_editing (renderer, model, attribute)

Enable standard inline editing in a gtk.TreeView. First argument should be an instance of gtk.CellRenderer (e.g. a text or toggle renderer) for which editing is to be enabled. Argument model should be an instance of RowObjectTreeModel and attribute — name of an attribute in it. Usually the attribute will be the one bound to the ‘main‘ property of the renderer, but this is not required: the renderer may display one attribute, yet edit another.

An example of using this function could look like this:

renderer = gtk.CellRendererText ()
TreeViewUtils.enable_renderer_editing (renderer, store, 'name')
tree_view.append_column (gtk.TreeViewColumn ('Name', renderer,
                                             text = store.columns ('name')))

More examples can be found in examples/editing_tree.py.

connect_viewport_handler (tree_view, handler, *args)

Connect the handler to various signals on tree_view emitted when the widgets viewport changes. Here viewport is defined as the visible area. When the visible area or rows contained within it change, the handler will be invoked. First argument to the handler will be the tree_view followed by optional args.

Returned object can be later passed to disconnect_viewport_handler() to disconnect from all the different signals at once. However, as with normal handlers, you don’t need to bother with disconnection if it’s fine for the handler to remain connected for the full lifetime of the widget.

disconnect_viewport_handler (tree_view, composite_handler_id)

Disconnect a viewport handler previously connected using connect_viewport_handler(). The second argument must be the return value of a call to that function.

This pair of function can be seen as similar to gobject.GObject.connect() and gobject.GObject.disconnect(). The difference is that the handler is in fact connected to several signals at once.

get_viewport_rows (tree_view)

Return the list of rows currently visible in tree_view. Each row is returned as a tuple of two elements: gtk.TreeIter and row object. It is required that the tree_view has an implementation of RowObjectTreeModel as its model.

Note:Currently only implemented for list models.