11. Tree and List Widgets

A Gtk.TreeView and its associated widgets are an extremely powerful way of displaying data. They are used in conjunction with a Gtk.ListStore or Gtk.TreeStore and provide a way of displaying and manipulating data in many ways, including:

  • Automatically updates when data added, removed or edited
  • Drag and drop support
  • Sorting of data
  • Support embedding widgets such as check boxes, progress bars, etc.
  • Reorderable and resizable columns
  • Filtering of data

With the power and flexibility of a Gtk.TreeView comes complexity. It is often difficult for beginner developers to be able to utilize correctly due to the number of methods which are required.

11.1. The Model

Each Gtk.TreeView has an associated Gtk.TreeModel, which contains the data displayed by the TreeView. Each Gtk.TreeModel can be used by more than one Gtk.TreeView. For instance, this allows the same underlying data to be displayed and edited in 2 different ways at the same time. Or the 2 Views might display different columns from the same Model data, in the same way that 2 SQL queries (or “views”) might show different fields from the same database table.

Although you can theoretically implement your own Model, you will normally use either the Gtk.ListStore or Gtk.TreeStore model classes. Gtk.ListStore contains simple rows of data, and each row has no children, whereas Gtk.TreeStore contains rows of data, and each row may have child rows.

When constructing a model you have to specify the data types for each column the model holds.

store = Gtk.ListStore(str, str, float)

This creates a list store with three columns, two string columns, and a float column.

Adding data to the model is done using Gtk.ListStore.append() or Gtk.TreeStore.append(), depending upon which sort of model was created.

treeiter = store.append(["The Art of Computer Programming",
                         "Donald E. Knuth", 25.46])

Both methods return a Gtk.TreeIter instance, which points to the location of the newly inserted row. You can retrieve a Gtk.TreeIter by calling Gtk.TreeModel.get_iter().

Once, data has been inserted you can retrieve or modify data using the tree iter and column index.

print store[treeiter][2] # Prints value of third column
store[treeiter][2] = 42.15

As with Python’s built-in list object you can use len() to get the number of rows and use slices to retrieve or set values.

# Print number of rows
print len(store)
# Print all but first column
print store[treeiter][1:]
# Print last column
print store[treeiter][-1]
# Set first two columns
store[treeiter][:2] = ["Donald Ervin Knuth", 41.99]

Iterating over all rows of a tree model is very simple as well.

for row in store:
    # Print values of all columns
    print row[:]

Keep in mind, that if you use Gtk.TreeStore, the above code will only iterate over the rows of the top level, but not the children of the nodes. To iterate over all rows and its children, use the print_tree_store function.

def print_tree_store(store):
    rootiter = store.get_iter_first()
    print_rows(store, rootiter, "")

def print_rows(store, treeiter, indent):
    while treeiter != None:
        print indent + str(store[treeiter][:])
        if store.iter_has_child(treeiter):
            childiter = store.iter_children(treeiter)
            print_rows(store, childiter, indent + "\t")
        treeiter = store.iter_next(treeiter)

Apart from accessing values stored in a Gtk.TreeModel with the list-like method mentioned above, it is also possible to either use Gtk.TreeIter or Gtk.TreePath instances. Both reference a particular row in a tree model. One can convert a path to an iterator by calling Gtk.TreeModel.get_iter(). As Gtk.ListStore contains only one level, i.e. nodes do not have any child nodes, a path is essentially the index of the row you want to access.

# Get path pointing to 6th row in list store
path = Gtk.TreePath(5)
treeiter = liststore.get_iter(path)
# Get value at 2nd column
value = liststore.get_value(treeiter, 1)

In the case of Gtk.TreeStore, a path is a list of indexes or a string. The string form is a list of numbers separated by a colon. Each number refers to the offset at that level. Thus, the path “0” refers to the root node and the path “2:4” refers to the fifth child of the third node.

# Get path pointing to 5th child of 3rd row in tree store
path = Gtk.TreePath([2, 4])
treeiter = treestore.get_iter(path)
# Get value at 2nd column
value = treestore.get_value(treeiter, 1)

Instances of Gtk.TreePath can be accessed like lists, i.e. len(treepath) returns the depth of the item treepath is pointing to, and treepath[i] returns the child’s index on the i-th level.

11.1.1. TreeModel Objects

class Gtk.TreeModel
get_iter(path)

Returns a Gtk.TreeIter instance pointing to path.

path is expected to be a colon separated list of numbers or a tuple. For example, the string “10:4:0” or tuple (10, 4, 0) would create a path of depth 3 pointing to the 11th child of the root node, the 5th child of that 11th child, and the 1st child of that 5th child.

iter_next(treeiter)

Returns a Gtk.TreeIter instance pointing the node following treeiter at the current level or None if there is no next iter.

iter_previous(treeiter)

Returns a Gtk.TreeIter instance pointing the node previous to treeiter at the current level or None if there is no previous iter.

iter_has_child(treeiter)

Returns True if treeiter has children, False otherwise.

iter_children(treeiter)

Returns a Gtk.TreeIter instance pointing to the first child of treeiter or None if treeiter has no children.

get_iter_first()

Returns a Gtk.TreeIter instance pointing to the first iterator in the tree (the one at the path “0”) or None if the tree is empty.

11.1.2. ListStore Objects

class Gtk.ListStore(data_type[, ...])

Creates a new Gtk.ListStore with the specified column data types. Each row added to the list store will have an item in each column.

Supported data types are the standard Python ones and GTK+ types:

  • str, int, float, long, bool, object
  • GObject.GObject
append([row])

Appends a new row to this list store.

row is a list of values for each column, i.e. len(store) == len(row). If row is omitted or None, an empty row will be appended.

Returns a Gtk.TreeIter pointing to the appended row.

11.1.3. TreeStore Objects

class Gtk.TreeStore(data_type[, ...])

Arguments are the same as for the Gtk.ListStore constructor.

append(parent[, row])

Appends a new row to this tree store. parent must be a valid Gtk.TreeIter. If parent is not None, then it will append the new row after the last child of parent, otherwise it will append a row to the top level.

row is a list of values for each column, i.e. len(store) == len(row). If row is omitted or None, an empty row will be appended.

Returns a Gtk.TreeIter pointing to the appended row.

11.1.4. TreePath Objects

class Gtk.TreePath(path)

Construct a Gtk.TreePath pointing to the node specified by path.

If path is a string it is expected to be a colon separated list of numbers. For example, the string “10:4:0” would create a path of depth 3 pointing to the 11th child of the root node, the 5th child of that 11th child, and the 1st child of that 5th child.

If path is a list or a tuple it is expected to contain the indexes of the nodes. Referring to the above mentioned example, the expression Gtk.TreePath("10:4:0") is equivalent to Gtk.TreePath([10, 4, 3]).

11.2. The View

While there are several different models to choose from, there is only one view widget to deal with. It works with either the list or the tree store. Setting up a Gtk.TreeView is not a difficult matter. It needs a Gtk.TreeModel to know where to retrieve its data from, either by passing it to the Gtk.TreeView constructor, or by calling Gtk.TreeView.set_model().

tree = Gtk.TreeView(store)

Once the Gtk.TreeView widget has a model, it will need to know how to display the model. It does this with columns and cell renderers.

Cell renderers are used to draw the data in the tree model in a way. There are a number of cell renderers that come with GTK+, for instance Gtk.CellRendererText, Gtk.CellRendererPixbuf and Gtk.CellRendererToggle. In addition, it is relatively easy to write a custom renderer yourself.

A Gtk.TreeViewColumn is the object that Gtk.TreeView uses to organize the vertical columns in the tree view. It needs to know the name of the column to label for the user, what type of cell renderer to use, and which piece of data to retrieve from the model for a given row.

renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Title", renderer, text=0)
tree.append_column(column)

To render more than one model column in a view column, you need to create a Gtk.TreeViewColumn instance and use Gtk.TreeViewColumn.pack_start() to add the model columns to it.

column = Gtk.TreeViewColumn("Title and Author")

title = Gtk.CellRendererText()
author = Gtk.CellRendererText()

column.pack_start(title, True)
column.pack_start(author, True)

column.add_attribute(title, "text", 0)
column.add_attribute(author, "text", 1)

tree.append_column(column)

11.2.1. TreeView Objects

class Gtk.TreeView([treemodel])

Creates a new Gtk.TreeView widget with the model initialized to treemodel. treemodel must be a class implementing Gtk.TreeModel, such as Gtk.ListStore or Gtk.TreeStore. If treemodel is omitted or None, the model remains unset and you have to call set_model() later.

set_model(model)

Sets the model for this tree view. If this tree view already has a model set, it will remove it before setting the new model. If model is None, then it will unset the old model.

get_model()

Returns the model this tree view is based on. Returns None if the model is unset.

append_column(column)

Appends column to the list of columns.

get_selection()

Gets the Gtk.TreeSelection associated with this tree view.

enable_model_drag_source(start_button_mask, targets, actions)

Arguments are the same as Gtk.Widget.drag_source_set()

enable_model_dest_source(targets, actions)

Arguments are the same as Gtk.Widget.drag_dest_set()

11.2.2. TreeViewColumn Objects

class Gtk.TreeViewColumn(label[, renderer[, **kwargs]])

Creates a new Gtk.TreeViewColumn.

renderer is expected to be a Gtk.CellRenderer instance, and kwargs key-value pairs specifying the default values of renderer‘s properties. This is equivalent to calling pack_start() and add_attribute() for each key-value pair in kwargs.

If renderer is omitted, you have to call pack_start() or pack_end() yourself.

add_attribute(renderer, attribute, value)

Adds an attribute mapping to this column.

attribute is the parameter on renderer to be set from the value. So for example if column 2 of the model contains strings, you could have the “text” attribute of a Gtk.CellRendererText get its values from column 2.

pack_start(renderer, expand)

Packs the renderer into the beginning of this column. If expand is False, then the renderer is allocated no more space than it needs. Any unused space is divided evenly between cells for which expand is True.

pack_end(renderer, expand)

Adds the renderer to end of this column. If expand is False, then the renderer is allocated no more space than it needs. Any unused space is divided evenly between cells for which expand is True.

set_sort_column_id(sort_column_id)

Sets the column of the model by which this column (of the view) should be sorted. This also makes the columnd header clickable.

get_sort_column_id()

Gets the column id set by Gtk.TreeViewColumn.set_sort_column_id()

set_sort_indicator(setting)

Sets whether a little arrow is displayed in the column header to in

setting can either be True (indicator is shown) or False

get_sort_indicator()

Gets the value set by Gtk.TreeViewColumn.set_sort_indicator()

set_sort_order(order)

Changes the order by which the column is sorted.

order can either be Gtk.SortType.ASCENDING or Gtk.SortType.DESCENDING.

get_sort_order()

Gets the sort order set by Gtk.TreeViewColumn.set_sort_order()

11.3. The Selection

Most applications will need to not only deal with displaying data, but also receiving input events from users. To do this, simply get a reference to a selection object and connect to the “changed” signal.

select = tree.get_selection()
select.connect("changed", on_tree_selection_changed)

Then to retrieve data for the row selected:

def on_tree_selection_changed(selection):
    model, treeiter = selection.get_selected()
    if treeiter != None:
        print "You selected", model[treeiter][0]

You can control what selections are allowed by calling Gtk.TreeSelection.set_mode(). Gtk.TreeSelection.get_selected() does not work if the selection mode is set to Gtk.SelectionMode.MULTIPLE, use Gtk.TreeSelection.get_selected_rows() instead.

11.3.1. TreeSelection Objects

class Gtk.TreeSelection
set_mode(type)

Where type is one of

  • Gtk.SelectionMode.NONE: No selection is possible
  • Gtk.SelectionMode.SINGLE: Zero or one element may be selected
  • Gtk.SelectionMode.BROWSE: Exactly one element is selected. In some circumstances, such as initially or during a search operation, it’s possible for no element to be selected. What is really enforced is that the user can’t deselect a currently selected element except by selecting another element.
  • Gtk.SelectionMode.MULTIPLE: Any number of elements may be selected. Clicks toggle the state of an item. Any number of elements may be selected. The Ctrl key may be used to enlarge the selection, and Shift key to select between the focus and the child pointed to. Some widgets may also allow Click-drag to select a range of elements.
get_selected()

Returns a tuple (model, treeiter), where model is the current model and treeiter a Gtk.TreeIter pointing to the currently selected row. treeiter is None if no rows are selected.

This function will not work if the mode of this selection is Gtk.SelectionMode.MULTIPLE.

get_selected_rows()

Returns a list of Gtk.TreePath instances of all selected rows.

11.4. Sorting

Sorting is an important feature for tree views and is supported by the standard tree models (Gtk.TreeStore and Gtk.ListStore), which implement the Gtk.TreeSortable interface.

11.4.1. Sorting by clicking on columns

A column of a Gtk.TreeView can easily made sortable with a call to Gtk.TreeViewColumn.set_sort_column_id(). Afterwards the column can be sorted by clicking on its header.

First we need a simple Gtk.TreeView and a Gtk.ListStore as a model.

model = Gtk.ListStore(str)
model.append(["Benjamin"])
model.append(["Charles"])
model.append(["alfred"])
model.append(["Alfred"])
model.append(["David"])
model.append(["charles"])
model.append(["david"])
model.append(["benjamin"])

treeView = Gtk.TreeView(model)

cellRenderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Title", renderer, text=0)

The next step is to enable sorting. Note that the column_id (0 in the example) refers to the column of the model and not to the TreeView’s column.

column.set_sort_column_id(0)

11.4.2. Setting a custom sort function

It is also possible to set a custom comparison function in order to change the sorting behaviour. As an example we will create a comparison function that sorts case-sensitive. In the example above the sorted list looked like:

alfred
Alfred
benjamin
Benjamin
charles
Charles
david
David

The case-sensitive sorted list will look like:

Alfred
Benjamin
Charles
David
alfred
benjamin
charles
david

First of all a comparison function is needed. This function gets two rows and has to return a negative integer if the first one should come before the second one, zero if they are equal and a positive integer if the second one should come before the second one.

def compare(model, row1, row2, user_data):
    sort_column, _ = model.get_sort_column_id()
    value1 = model.get_value(row1, sort_column)
    value2 = model.get_value(row2, sort_column)
    if value1 < value2:
        return -1
    elif value1 == value2:
        return 0
    else:
        return 1

Then the sort function has to be set by Gtk.TreeSortable.set_sort_func().

model.set_sort_func(0, compare, None)

11.4.3. TreeSortable objects

class Gtk.TreeSortable
set_sort_column_id(sort_column_id, order)

Sets the current sort column to sort_column_id.

order can either be Gtk.SortType.ASCENDING or Gtk.SortType.DESCENDING.

get_sort_column_id()

Returns a tuple consisting of the current sort column and order.

set_sort_func(sort_column_id, sort_func, user_data)

Sets the comparison function used when sorting by the colum sort_column_id.

user_data gets passed to sort_func.

sort_func is a function with the signature sort_func(model, iter1, iter2, user_data) and should return a negative integer if iter1 sorts before iter2, zero if they are equal and a positive integer if iter2 sorts before iter1.

set_default_sort_func(sort_func, user_data)

See Gtk.TreeSortable.set_sort_func(). This sets the comparison function that is used when sorting by the default sort column