One of the most challenging tasks for new GTK+ application developers is figuring out how to present and manipulate data in a TreeView widget. The widget itself is not particularly complicated, but the sophisticated architecture behind it can be difficult to grok.

The TreeView and its underlying data store work together in a classic model-view-controller pattern. This approach necessitates a lot of extra boilerplate code, but it's very powerful when used to its full potential. In this article, we are going to briefly look at how to bind a TreeView widget to a TreeStore and then we are going to show you how to bind the same store to a text entry widget with autocompletion.

The underlying data behind a tree view widget is stored in a tree model. The TreeModel defines a high-level interface that describes how view widgets communicate with underlying storage data. There are several standard storage classes that conform with the tree model API. These are the GtkListStore, used for flat data, and the TreeStore, which is used for nested hierarchal data. In this example, we will be using the TreeStore.

The following example in Python demonstrates how to instantiate the TreeStore and populate it with nested data. It uses a simple recursive function to traverse the filesystem and then put the file and directory names into the TreeStore.

import os, stat, gtk # Instantiate the tree store and specify the data types store = gtk.TreeStore(str, gtk.gdk.Pixbuf, int, bool) def dirwalk(path, parent=None): # Iterate over the contents of the specified path for f in os.listdir(path): # Get the absolute path of the item fullname = os.path.join(path, f) # Extract metadata from the item fdata = os.stat(fullname) # Determine if the item is a folder is_folder = stat.S_ISDIR(fdata.st_mode) # Generate an icon from the default icon theme img = gtk.icon_theme_get_default().load_icon( "folder" if is_folder else "document", gtk.ICON_SIZE_MENU, 0) # Append the item to the TreeStore li = store.append(parent, [f, img, fdata.st_size, is_folder]) # If the item is a folder, descend into it if is_folder: dirwalk(fullname, li) dirwalk("/home/username/whatever")

When we instantiate the TreeStore by calling the gtk.TreeStore constructor, we use the parameters to specify the type of data for each of the TreeStore object's columns. In this case, the first column is the filename (a string), the second column is the icon (a Pixbuf), the third column is the file size (an integer), and the fourth column is a boolean value that specifies whether the item is a folder. It's important to note that the TreeStore columns don't necessarily map directly to TreeView columns. The mapping is defined later in the program.

The store.append method is used to add an item to the TreeStore. The first parameter is the parent item, which you need to specify if you want to nest your new item hierarchically inside of an existing one. If you are creating a top-level item in the TreeStore, then the parent will be None . The second parameter of the store.append method is a list of values that you want to put into the TreeStore item's columns. Each value in the list corresponds with one column. You want these to match the types that you specified when you instantiated the TreeStore.

Now that we have created a TreeStore, we want to display it in a TreeView. To do this, we have to create the TreeView widget and then bind values from the model to actual columns in the TreeView. The following example demonstrates how to do this:

# Create a TreeViewColumn col = gtk.TreeViewColumn("File") # Create a column cell to display text col_cell_text = gtk.CellRendererText() # Create a column cell to display an image col_cell_img = gtk.CellRendererPixbuf() # Add the cells to the column col.pack_start(col_cell_img, False) col.pack_start(col_cell_text, True) # Bind the text cell to column 0 of the tree's model col.add_attribute(col_cell_text, "text", 0) # Bind the image cell to column 1 of the tree's model col.add_attribute(col_cell_img, "pixbuf", 1) col2 = gtk.TreeViewColumn("Size") col2_cell_text = gtk.CellRendererText() col2.pack_start(col2_cell_text) col2.add_attribute(col2_cell_text, "text", 2) # Create the TreeView and set our data store as the model tree = gtk.TreeView(store) # Append the columns to the TreeView tree.append_column(col) tree.append_column(col2)

The CellRenderer objects are responsible for rendering the data in the tree. Each column can have any number of cell renderers. In the example above, we want the first column to have the icon and filename. The second column displays the size of the file. The column.add_attribute method is used to connect the cell renderers to the underlying data that is stored in the model. The method takes three parameters. The first is the cell renderer object, the second is the specific attribute of the cell renderer that you want to bind to a value, and the third is the index of the column in the TreeStore that contains the data you want to display in the cell renderer.

When we instantiate the TreeView with the gtk.TreeView constructor method, we pass it our TreeStore object as a parameter. This will make it use our store object as its data model. If you ever want to swap out the model later, you can use the tree.set_model method. Now that we have our TreeView and we have bound it to a TreeStore, we can display it to the user by packing it into a scrolling window:

scroll = gtk.ScrolledWindow() scroll.add(tree) window = gtk.Window() window.connect("destroy", gtk.main_quit) window.add(scroll) window.show_all()