3.1 ObjectList

There is a special gtk.TreeView wrapper that accepts a columned list, each row of the list representing an instance. This is the first high-level class of the framework being described; high-level in the sense that it goes beyond simply organizing the UI and handler methods, offering functionality directed towards object-oriented development in Python.

The ObjectList was designed for the very common task of presenting a list of instance data in a gtk.TreeView. Elaborating an example: lets say we have a set of Person instances stored somewhere, each of these instances possessing the attributes name, address and telephone. A common problem is displaying a gtk.TreeView with this data and performing an action when a person is selected. This is exactly the use case which ObjectList supports.

Before showing an example, it's important to explain how the ObjectList's constructor works, since it is rather special.

    class ObjectList:
        def __init__(self, columns, instance_list=None,
                     mode=gtk.SELECTION_BROWSE)

The ObjectList takes a columns parameter, and understanding it is essential to using it correctly. columns should be a list of Column instances, each of these instances representing a column in the list (and therefore, an attribute in the instance). Let's have a look at the constructor for Column:

    class Column:
        __init__(self, attribute, title=None, data_type=True,
                 **kwargs)

Each Column instance determines the properties of the ObjectList's column; most important of these is attribute, which specifies the name of the instance attribute the column should hold. Note that all of these arguments but attribute are optional and can be safely be left out. To create a ObjectList for our example above, with columns for "name", "address" and "telephone", we could have something like:

    my_columns = [
        Column("name", sorted=True),
        Column("address"),
        Column("telephone", title="Phone")
    ]

    cd = ObjectList(my_columns)

This section of code would create a new ObjectList with 3 columns, being sorted by the first one, and using "Name", "Address" and "Phone" as column titles. See the API reference for ObjectList and Column to find out all the details on them; there are many features, including sort ordering, tooltips, format strings and more.

Note that you can produce a complete column specification by running the delegate once with a simple set of columns, manipulating them in runtime (changing sort order and widths, hiding columns) and and then calling dump_columns() on the delegate; this method creates a list of Column instances corresponding to the current state of the ObjectList. This list can be reused in the ObjectList constructor, making it easy to save and restore the ObjectList's state even between runs.

The instance_list parameter should provide a list of instances to be inserted into the clist, and the handler is an optional callback that will be run when items are selected in the clist. The instances in the list must offer either an accessor to the attribute specified - the accessor name must be in the format get_attribute_name() - or an attribute with the name specified (the delegate will do a getattr() for it). Note that you can instantiate a ObjectList without a list and add it later by calling new_list(instance_list).

To exemplify the ObjectList, I'm going to build upon the News Browser example we saw a while back. I'm going to need to change the news data structures from tuples to instances, and create a ObjectList for it (see news/news2.py):


#!/usr/bin/env python
import gtk

from kiwi.ui.objectlist import ObjectList, Column

class NewsItem:
    """An instance that holds information about a news article."""
    def __init__(self, title, author, url):
        self.title, self.author, self.url = title, author, url

# Assemble friendly Pigdog.org news into NewsItem instances so they can
# be used in the ObjectList
news = [
 NewsItem("Smallpox Vaccinations for EVERYONE", "JRoyale",
          "http://www.pigdog.org/auto/Power_Corrupts/link/2700.html"),
 NewsItem("Is that uranium in your pocket or are you just happy to see me?",
          "Baron Earl",
          "http://www.pigdog.org/auto/bad_people/link/2699.html"),
 NewsItem("Cut 'n Paste", "Baron Earl",
          "http://www.pigdog.org/auto/ArtFux/link/2690.html"),
 NewsItem("A Slippery Exit", "Reverend CyberSatan",
          "http://www.pigdog.org/auto/TheCorporateFuck/link/2683.html"),
 NewsItem("Those Crazy Dutch Have Resurrected Elvis", "Miss Conduct",
          "http://www.pigdog.org/auto/viva_la_musica/link/2678.html")
]

# Specify the columns: one for each attribute of NewsItem, the URL
# column invisible by default
my_columns = [Column("title", sorted=True),
              Column("author", justify=gtk.JUSTIFY_RIGHT),
              Column("url", title="URL", visible=False)]

objectlist = ObjectList(my_columns)
objectlist.add_list(news)

w = gtk.Window()
w.connect('delete-event', gtk.main_quit)
w.set_size_request(600, 250)

w.add(objectlist)

w.show_all()
gtk.main()

Wow! Assuming you have data in the correct format, in some 25 lines you defined the data for your applications, and in about 5 lines you set up and created the ObjectList. This example, though, doesn't do anything (try running it - it doesn't even flash a window open, which is what normally happens if you create a window but don't run the mainloop) because a SlaveView doesn't have a toplevel window associated to it. To be able to use the ObjectList, we need to perform UI composition: attach it to a parent view. I'll show for now a screenshot of the generated widget inside a single window just so you know how it looks:

\includegraphics[scale=0.88]{images/news2.eps}

If you look at this image, you might notice some important details:

  1. There is a little arrow in the right corner of the Title column. What is that? A sort indicator! The titles in the first column are sorted ascending. The ObjectList is set up so that you can order it's columns by clicking on the headers, and it even sorts numeric data properly. By default the sorted column is the left-most one, but you can change that by using the columns spec.

  2. There is a popup menu in the right corner of the image. What is it? It's a list of the columns, allowing you to select which column is displayed and which is hidden. As you can see, the URL column is hidden (remember the False we passed in the my_columns list?) and Title and Author are displayed.

    Note that both features are built in to the Kiwi ObjectList, one of the enhanced widgets we provide.

  3. (The gtk.TreeView has no window border around it. That's because I had to do some magic to get this screenshot to display correctly. Don't worry about it.)