Create  Edit  FrontPage  Index  Search  Changes  History  RSS  Login

tut-gtk2-treev-parts

Parts of a Tree View

The Gtk::TreeView widget is used to display data organized as a list or a tree. The data displayed in the view is organized into columns and rows. The user is able to select one or multiple rows within the tree view. All who know Linux are familiar with Nautilus file and directory browser which utilizes Gtk::TreeView widget.

The ease with which users can present and manipulate deeply structured data with Gtk::TreeView widget does not come free, and the brunt of the burden falls on the programmer. With a large variety of features it provides it is not the easiest widget to understand to program. However, time spent learning it is guarantied to be well spent, because with its almost infinite ways of customization, you will be able to apply it to a rather large variety of applications.

What makes Gtk::TreeView unique is that it follows a design concept commonly referred to as model-view-controller (MVC) desgn. MVC is a design method where the information and the way it is rendered are separated, which is vaguely similar to the relationship between Gtk::TextView and Gtk::TextBuffer.

Gtk::TreeModel

Based on the titles of this chapter (Tree View) and this paragraph, as well as its introduction discussions about the Gtk::TreeModel one may conclude that the fundamental data structure of both view and the model classes is that of a tree. This is only partially true, since the simplest form of a tree is a single branch or in computer parlance a list. Due to the fact that a list is also a tree, little or no effort has been made to separate the two, at least in the introductory paragraphs into this topic. Nevertheless, please keep in mind that our first program examples are about the one dimensional lists, and that unfortunately, we can not avoid using the word tree, when it comes to tree view or tree model for that matter. This situation is somewhat alleviated by the fact that GTK+ comes with two built-in data stores or models, namely, Gtk::ListStore and Gtk::TreeStore. As the names imply, Gtk::ListStore is used for simple lists of data items where items have no hierarchical parent-child relationships, and the Gtk::TreeStore which is used for tree-like data structures, where items can spawn children and parent-child relationships can evolve.

The GTK+ provides four types of built-in tree model classes, but in our sessions here we will cover only two - the Gtk::TreeStore and the Gtk::ListStore. The Gtk::TreeModel interface defines a generic tree interface for use by the Gtk::TreeView widget. It is designed to be usable with any appropriate data structure. Data itself is stored in objects whose classes implement the Gtk::TreeModel interface. As is pointed out in Gtk::TreeModel API documentation, these models provide the data structure as well as all appropriate tree interfaces. As a result, implementing drag and drop, sorting, and storing data is trivial.

The Gtk::TreeModel interface provides a standard set of methods for retrieving general information about the data that is stored. For example, it allows you to get the number of rows in the tree and the number of children of a certain row. The Gtk::TreeModel also gives you a way to retrieve the data that is stored in a specific row of the store.

It should be clear, that Gtk::TreeModel itself only provides a way to query the characteristics of a data store and to retrieve existing data, but it does not provide a way to remove or add rows to the store. This is done using the specific store's methods.

Note:
Models, renderers, and columns are referred to as objects rather than widgets, even though they are a part of GTK+ library. This distinction is important because it emphasizes the fact that they are not derived from Gtk::Widget, hence they do not inherit Gtk::Widget's behaviour, properties nor signals.

The model is represented as a hierarchical tree of strongly-typed, columnar data, where the type of data found in a column is defined by a Ruby class (ie. Object, Integer, Float, String, Hash, Gdk::Pixbuf, etc.). Not unlike in database tables, the types are homogeneous per column across all rows (nodes). It is not hard to switch from a database table to a list.

Gtk::ListStore allows you to create a list of elements with multiple columns. Each row is a child of the root node, so only one level of rows is displayed. But as already mentioned Gtk::ListStore is basically a tree structure that has no hierarchy. It is only provided because faster algorithms exist for interacting with the models that do not have any children items.

Gtk::TreeStore provides the same functionality as Gtk::ListStore, except the data can be organized into a multilayered tree.

After you have created the tree model, the view is used to display the data. By separating the tree view and the model, you are able to display the same set of data in multiple views. These views can be exact copies of each other, or the data can be displayed in varying ways. All views will be updated simultaneously as you make alterations to a model.

Navigating tree model:
As we will see later one can query for the value of a model at a certain node and a certain column on that node. There are two structures used to reference a particular node in a model. They are the Gtk::TreePath and the Gtk::TreeIter. Most of the interface consists of operations on a Gtk::TreeIter.

A tree model column should not be confused with a tree view column

Models are composed of columns that contain the same data type, and rows that hold the entire heterogeneous set of data for a row. On the other hand each model column can hold a single type of data (note that rows contain heterogeneous while columns homogeneous data types). A tree model column should not be confused with a tree view column, which is composed of a single header but may be rendered with data from multiple model columns.

treev-tabs-01.png

For example, a tree column may display a text string that has foreground colour defined by a model column that is not visible to the user as data but simply is displayed as a colour of the text. In the figure here the left table contains two pieces of information: (1) a text string and (2) a RGB colour value, hence each row within a model contains the data to display in view along with the colour parameter for the program, with which to control the colour rendering. The table on the right in the figure represents the tree view, i.e. how the user sees this data. In other words, the two model columns are used to "colour" the corresponding string (colour name) in the tree view column. This example may not make much sense on your first reading, and it is confusing to every starting up GTK+ programmer, until he/she is faced with the task of rendering different rows of a tree view in different styles and colours. We will learn how to implement this later in this tutorial. For now you should just remember that columns in a model do not necessarily map to columns in its corresponding view!


Gtk::TreeViewColumn and Gtk::CellRenderer

The Gtk::TreeViewColumn object is a visible column in a Gtk::TreeView widget. It determines the geometry, type. As mentioned earlier, a tree view displays one or more Gtk::TreeViewColumn objects. Tree columns are composed of a header and the cells of data that are organized as one column. Each tree view column can contain one or more visible columns of data. For instance, in a file browser, a tree view column may contain one column of "icon file images" and one column of "file names". The header of the Gtk::TreeViewColumn object contains a title that describes what data is held in the cells below. If you make the column sortable, the rows will be sorted when a sortable column header is clicked. Gtk::TreeViewColumn objects actually do not render anything to the screen. This is done with an object derived from the Gtk::CellRenderer.

The Gtk::CellRenderer is an abstract base class of a set of objects used for rendering a cell to a Gdk::Drawable. These objects are used primarily by the Gtk::TreeView widget, though they aren't tied to them in any specific way. As already mentioned, it is worth noting that Gtk::CellRenderer is not a Gtk::Widget and cannot be treated as such.

Different types of cell renderers:
There are a number of cell renderers, all derived from the abstract base Gtk::CellRenderer class, from which you must choose when building your tree view columns:

Normally a cell renderer is added to a column at the time column object is initialized. You pass a renderer to the Gtk::TreeViewColumn constructor. But, cell renderers can also be packed into tree view columns similar to how you add widgets into horizontal boxes. However, for this you use Gtk::TreeViewColumn#pack_start and Gtk::TreeViewColumn#pack_end respectively. Each tree view column can contain one or more cell renderers, which are used to render the data. For example, in a file browser, the image column would be rendered with the Gtk::CellRendererPixbuf and the file name with Gtk::CellRendererText.

Later (on the next page in the section called "Multi-item Super Columns"), after we have gained the awareness about some additional features, we first have to get acquainted with and which help us manage cell rendering, we will look at the program which implements multiple cell renderers per column. At this point I would advise you not pay too much attention to the multi-item columns. Please just remember that a simplistic view of renderers and columns as a "one to one" (1:1) proposition is not the whole story, and rest assured, that the promise to revisit this topic when the time is right will be kept.

The primary use of a Gtk::CellRenderer is for drawing certain graphical elements on a Gdk::Drawable. Typically, one cell renderer is used to draw many cells on the screen (the above advice not to think about multi-item columns was offered precisely because here we do not describe such "multi-columns"). Each cell renderer is responsible for rendering a column of cells, one for every row in the tree view. It begins with the first row, rendering its cells and then proceeding to the next row down until all that was requested or the entire column is processed or completed. Or to put it differently, a cell renderer does not render just one single cell, but is responsible for rendering part or whole of a tree view column for each single row. It basically starts in the first row and renders its part of the column there. Then it proceeds to the next row and renders its part of the column there again. And so on. (We say part of the column because there may be more than one renderer associated with one column.)

Cell renderers are composed of properties that define how each cell of data is rendered to the screen. There are a number of ways to set cell renderer properties, and it isn't expected that a Gtk::CellRenderer objects keep any permanent state around. This, however, is a more complex issue and hardly appropriate for an introductory text to Gtk::CellRenderer. Nevertheless, you have to be aware of one important feature here. Namely, all the properties you can set for a Gtk::CellRenderer apply to entire column. And even this can be vastly incorrect, namely, if that was strictly true, we could not set different rows to different colours and styles. This is why the above paragraph where you find the following statement: "... cell renderer does not render just one single cell, but is responsible for rendering part or whole of a tree view column for each single row. It basically starts in the first row and renders its part of the column there. Then it proceeds to the next row and renders its part of the column there again. And so on"is so important.

Namely, considering what was just repeated in the highlighted text, we can subsume that while any column is processed from first row to the last, during this process, each cell is capable of triggering certain actions in which according to some model-data for that particular row renderer's property can be modified. This mechanism is calledCell Data Functionsand its syntax is covered in API for Gtk::TreeViewColumn#set_cell_data_func, and about which we will talk latter in this tutorial. For now you should just know, that while a renderer is processing (ie. rendering) cells each cell is capable of modifying renderer's properties according to some criteria that is stored in the model along with our data to be displayed. (Whatever sets certain renderer's property last, that property remains set until at some later point in another row the same property is changed again by the call to "cell data function".)

Note:
If you are just starting to learn Gtk+, the sophistication in the last paragraph can be completely ignored without the fear of adversely affecting your understanding of the tutorial. You will have a chance to gradually understand what is at stake here, when you see how this works in example programs I provide later on in this article as well as on the following pages in this chapter (starting with the "Grocery List" program called liststore.rb).

There are a number of rules that must be followed when writing a new Gtk::CellRenderer. First and foremost, it's important that a certain set of properties will always yield a cell renderer of the same size, barring a Gtk::Style change. The Gtk::CellRenderer also has a number of generic properties that are expected to be honoured by all children.

Beyond merely rendering a cell, cell renderers can optionally provide active user interface elements. A cell renderer can be set active or inactive like Gtk::CellRendererToggle, which toggles when it gets activated by a mouse click, or it can be editable like Gtk::CellRendererText, which allows the user to edit the text using a Gtk::Entry. To make a cell renderer activatable or editable, you have to implement the activate or start_editing virtual methods, respectively.

You can control the look of the tree view such as hiding or showing column headers with the Gtk::TreeView#headers_visible= method, and set them clickable with Gtk::TreeView#headers_clickable= (which incidentally will be done automatically should you enable sorting). The expander column can be set with Gtk::TreeView#expander_column=. This feature is manifested by indenting child rows. Parent rows contain the "expander arrow" which if clicked will hide (collapse) or show (expand) a node's children. By default, this is the first column in tree view.

Using Gtk::ListStore

The Gtk::ListStore object is a list model for use with a Gtk::TreeView widget. It implements the Gtk::TreeModel interface, and can consequently use all of the methods available there. It also implements the Gtk::TreeSortable interface so you cansortthe list using the view (sorting features of tree view are explained later in a variant of "liststore.rb" (Grocery List) program called "liststore-sorting.rb").

treev-parts-n-01.png

Gtk::ListStore is used to create lists of data that have no hierarchical relationships among rows. Following is an example "Grocery List" application, which contains three columns, all of which are built using Gtk::CellRendererText. The first column is a Boolean value displayed as true or false, that define whether or not the product should be purchased. (Later we will learn how to use Gtk::CellRendererToggle in the place of spelling out true or false. The second column is an integer, representing the quantity to purchase and the third is a text string describing the product.

Before we look at the Grocery List example, I wish to point out how tree view and a model are connected. The order in which we create either the view or the model is not important, though depending on your style of programming you may have your preference. For instance, I prefer to create model before view, because I like to connect the two at the time when view is created. Let us look at the different ways you can accomplish this association for a model with two columns defined as String and Integer:

# Create a view
view = Gtk::TreeView.new

# Create empty data store
treestore = Gtk::TreeStore.new(String, Integer)

# Associate model with view utilizing Gtk::TreeView#model= 
view.model = treestore

I prefer the following, shorter version:

# Create empty data store
treestore = Gtk::TreeStore.new(String, Integer)

# Create view and associate model with it. 
view = Gtk::TreeView.new(treestore)

Even more terse though arguably less intelligible style is:

view = Gtk::TreeView.new(treestore = Gtk::TreeStore.new(String, Integer))


Lets look at our example program now. As promised, in this program we implement the easiest of column/cell style and colour rendering. Pay attention to how the colour of entire column is set to be red.


liststore.rb

#!/usr/bin/env ruby
require 'gtk2'

# Add three columns to the GtkTreeView. All three of the
# columns will be displayed as text, although one is a boolean
# value and another is an integer.
def setup_tree_view(treeview)
  # Create a new GtkCellRendererText, add it to the tree
  # view column and append the column to the tree view.
  renderer = Gtk::CellRendererText.new

  # render's propery will effect entire column
  renderer.foreground = "#ff0000"
  column   = Gtk::TreeViewColumn.new("Buy", renderer,  :text => BUY_IT)
  treeview.append_column(column)
  renderer = Gtk::CellRendererText.new
  column   = Gtk::TreeViewColumn.new("Count", renderer, :text => QUANTITY)
  treeview.append_column(column)
  renderer = Gtk::CellRendererText.new
  renderer = Gtk::CellRendererText.new
  column   = Gtk::TreeViewColumn.new("Product", renderer, :text => PRODUCT)
  treeview.append_column(column)
end

window = Gtk::Window.new("Grocery List")
window.resizable = true
window.border_width = 10

window.signal_connect('destroy') { Gtk.main_quit }
window.set_size_request(250, 165)

class GroceryItem
  attr_accessor :buy, :quantity, :product
  def initialize(b, q, p); @buy, @quantity, @product = b, q, p; end
end
BUY_IT = 0; QUANTITY = 1; PRODUCT  = 2

list = Array.new
list[0] = GroceryItem.new(true,  1, "Paper Towels") 
list[1] = GroceryItem.new(true,  2, "Bread")
list[2] = GroceryItem.new(false, 1, "Butter")
list[3] = GroceryItem.new(true,  1, "Milk")
list[4] = GroceryItem.new(false, 3, "Chips")
list[5] = GroceryItem.new(true,  4, "Soda") 

treeview = Gtk::TreeView.new
setup_tree_view(treeview)

# Create a new tree model with three columns, as Boolean,
# integer and string.
store = Gtk::ListStore.new(TrueClass, Integer, String)

# Add all of the products to the GtkListStore.
list.each_with_index do |e, i|
  iter = store.append

  iter[BUY_IT]   = list[i].buy       # same as: >>> # store.set_value(iter, BUY_IT,   list[i].buy)
  iter[QUANTITY] = list[i].quantity  # same as: >>> # store.set_value(iter, QUANTITY, list[i].quantity)
  iter[PRODUCT]  = list[i].product   # same as: >>> # store.set_value(iter, PRODUCT,  list[i].product)
end

# Add the tree model to the tree view
treeview.model = store

scrolled_win = Gtk::ScrolledWindow.new
scrolled_win.add(treeview)
scrolled_win.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)

window.add(scrolled_win)
window.show_all
Gtk.main

Creating the Tree View

Creating the tree view with Gtk::TreeView.new(model = nil) is the easiest part. Next order of business is to set up the tree view columns. In our example program we do this in our own helper method setup_tree_view(treeview), which deserves some explanations. Our tree view contains three columns and each has to be set up. All three columns will display text, hence we will use the Gtk::CellRendererText cell renderer for all of them.

renderer = Gtk::CellRendererText.new

Renderers and Columns

As we have seen, after creating the Gtk::TreeView, you have to add one or more columns to the view for it to be of any use. Each tree view column is composed of a header displaying a short description of its content, and at least one cell renderer.

Cell Renderers:

All cell renderers are derived from the Gtk::CellRenderer class. Each cell renderer contains a number of properties that determine how data will be drawn within a cell. The Gtk::CellRenderer class provides common properties to all derived renderer classes including: background colour and size parameters, alignment, mode, visibility, sensitivity, padding, etc. You should check the API documentation for full list. It also provides editing_canceled and editing-started signals, the former emitted when the user cancels the process of editing a cell, and the latter emitted when a cell is started to be edited.

We have introduced theforefroundcolour attribute in our program here:

renderer = Gtk::CellRendererText.new
renderer.foreground = "#ff0000"                   # or, you could also use colour name ("red") instead.

It is important to understand, that this attribute sets the colour for the entire column.

Our example program introduces Gtk::CellRendererText class. Gtk::CellRenderer hierarchy of classes provides a plethora of additional properties and instance methods which you can use to alter the appearance of the display. So you will have an idea what instance methods are available to Gtk::CellRenderer and Gtk::TreeViewColumn objects to alter and manage their properties, I will for your convenience list some of them at the end of this session.

Columns:

Columns are directly associated with cell renderer as well as with tree view. These relationships can be seen in the following code segments:

column = Gtk::TreeViewColumn.new("Buy", renderer, {:text => BUY_IT})
treeview.append_column(column)

Note that curly braces are not needed in the above constructor, I just want to underscore, that we have a hash here. The following is a more verbose equivalent of the above:

renderer = Gtk::CellRendererText.new
column   = Gtk::TreeViewColumn.new
column.title = "Buy"
column.pack_start(renderer, false)
#column.add_attribute(renderer, :text, BUY_IT)
column.set_attributes(renderer, {:text => BUY_IT})  # curly brackets indicate that they surround a hash
treeview.append_column(column)

Following code segment shows you yet another way of initializing a tree view column with the identical results as obtained by the code in the above two code snippets:

renderer = Gtk::CellRendererText.new
col_offset = treeview.insert_column(-1, 'Buy', renderer, {'text' => BUY_IT}) # curly b. ==> the use of hash
column = treeview.get_column(col_offset - 1)
#treeview.append_column(column)      ## <--- Note that this line must not be run in this 3rd code segment!

Note, that in the second code segment above under subtitle "Columns", you could use either of Gtk::TreeViewColumn#set_attributes or Gtk::TreeViewColumn#add_attribute instance methods to associate the cell renderer with model column number. I should warn you that there are times when you will need to use the longer more verbose (as in second code snippet above) way of creating tree view columns. We will talk about this after we learn the basics (if you wish you can peek ahead to the next page into the section called "Multi-item Super Columns".)

Dissecting the column creation

When defining a tree view column we need to provide (1) the text title for the header ("Buy" in our case here), (2) the renderer to be used to render all the cells for this column in every row of this list store, and (3) the value (BUY_IT) either as a parameter (if Gtk::TreeViewColumn#add_attribute is used), or in the event that a statement uses a hash, such as in Gtk::TreeViewColumn.new and in Gtk::TreeViewColumn#set_attributes, any number of attribute name/value pairs in the hash, where the name represents the attribute/property name (:text) and the value (BUY_IT) a column number in the model. The use of word "value" in all these cases is rather mis-fortunate and a bit of a misnomer -for any tree column view the value in the name/value pair here represents the column number, not the attribute's value, and should always be an integer!It is very important to realize that you can not set property values here. For instance one can not set:foregroundattribute's value to a desired colour within Gtk::TreeViewColumn.new code as is shown in the following erroneous code segment:

# The following is WRONG:
column = Gtk::TreeViewColumn.new("Buy", renderer, "text" => BUY_IT, :foreground => "#ff0000")  # ERROR !!!

Providing you have the renderer object you could set these attributes for it by writing:

renderer.text = "Yes, buy this product"
renderer.foreground = "#ff0000"

However, this would only change the tree view but not the model. Also you must know where you can and can not change a renderer's attributes. For instance placing the line"renderer.text = 'Yes, buy this product'"in "setup_tree_view" method may have no effect, however setting the value of "foreground" attribute there would work. The best place to change renderer's attributes is in the "Cell Data Function" which you do not learn as one of the basic tools here and we only explain it in the next chapter since it is one of more exotic features of tree view management mechanisms but nevertheless used frequently.

To understand why the initialization of the column constructor above in the first of the two code snippets is wrong you must know that the :text and :foreground properties are the render's attributes. As pointed out in the discussion under the title"Dissecting the column creation"above in this section the values in the constructor's hash parameter should always be integers since they represent the column numbers for the actual values of these attributes in the model. If you recall in the paragraph above entitled"Navigating tree model"we mentioned that most of the tree model interface consists of operations on a Gtk::TreeIter. If you look at the "liststore.rb" program listing above you will notice that these iterators are used to access the model as if it were an array:

iter[BUY_IT]   = list[i].buy
iter[QUANTITY] = list[i].quantity
iter[PRODUCT]  = list[i].product

However, unlike the previous code example where we set the "text" and "foreground" attributes directly on the renderer, this code here is using Gtk::TreeIter's rather than direct access to renders's properties (attributes). More importantly the code using iters does two things - it sets both: (1) the columns in the model and also (2) the columns in the view.


We are not quite yet done with our example program "liststore.rb". There are a few more marginal issues to discuss. The one that calls for attention is the way the model is initialized, or as we would say in old computer parlance "the way we load data into the model (list in this case)". So lets now return back to it.

Creating the Gtk::ListStore

After the tree view columns are set up with the desired cell renderers, it is time to create our list model that will interface between the renderers and the tree view. As you already know, we used the Gtk::ListStore so that the items would be shown as a list of elements. The fact that our program constants BUY_IT, QUANTITY, and PRODUCT refer to model column numbers, and that the respective data types in the model are Boolean, Integer and String is formalized by the list store (model) constructor:

# Create a new tree model with three columns, as Boolean,
# integer and string.
store = Gtk::ListStore.new(TrueClass, Integer, String)

In our program, we invoke this constructor to create the list and at the same time define the data types for each column within a row, just before we enter the loop, by iterating through our array of values to append them to the list. Indeed the important feature in this process is the iterator, i.e. the Gtk::TreeIter object, which is manipulated as an index in our list, where the Gtk::ListStore#append does all the work for us and returns the iterator for each subsequently created empty row as it creates a new empty list entry (record or row) and points the iterator (index) to it. We then proceed to assign values from our array of GroceryItem objects to individual columns with Gtk::TreeIter#[], or alternatively with Gtk::ListStore#set_value:

store = Gtk::ListStore.new(TrueClass, Integer, String)

# Add all of the products to the Gtk::ListStore.
list.each_with_index do |e, i|
    iter = store.append

    iter[BUY_IT]   = list[i].buy       # same as: >>> # store.set_value(iter, BUY_IT,   list[i].buy)
    iter[QUANTITY] = list[i].quantity  # same as: >>> # store.set_value(iter, QUANTITY, list[i].quantity)
    iter[PRODUCT]  = list[i].product   # same as: >>> # store.set_value(iter, PRODUCT,  list[i].product)
end

Following is the pertinent API for the methods we used when dealing with the Gtk::ListStore object in the example above:

Gtk::ListStore.new(type1, type2, type3, ...)
Creates a new tree store as with columns each of the types passed in. As an example, Gtk::ListStore.new(Integer, String, Gdk::Pixbuf) will create a new Gtk::ListStore with three columns, of type Integer, String and Gdk::Pixbuf respectively.
  • type1, type2, type3, ... : Object.class value
  • Returns: A new Gtk::ListStore
append
Appends a new row to list_store. iter will be changed to point to this new row. The new row will be empty after this method is called. To fill in values, you need to call Gtk::TreeIter#set_value or Gtk::ListStore#set_value.
set_value(iter, column, value)
Sets the data in the cell specified by iter and column. The type of value must be convertible to the type of the column. Use Gtk::TreeIter#set_value instead.
  • iter : A valid Gtk::TreeIter for the row being modified
  • column : A column number to modify
  • value : A new value for the cell
  • Returns: self


As you see there are many issues to consider when dealing with tree model and tree view. It is not easy to learn all the aspects of this mechanism up front before you start to experiment with the programs. The best place to get your feet wet in practice and begin investigating and learning the ropes of the tree view and tree model MVC pattern in GTK+ is a program like our "liststore.rb" at the place where the tree view is initialized. In our example program this is the setup_tree_view method. In it, we are defining three individual single columns, where we have a single name/value pair for each attribute. Finally we have to add (append) our newly defined column to the tree view. Copy the program to your system and start playing with it. The above discussions should provide you with plenty of material to try different alternatives.



To avoid unnecessary lengthy learning curves each time a new feature is introduced in a new program example we will use the above "liststore.rb" program as the base for demonstrating the additional tree view/model features us much as possible. Such is the case with our first addition, namely sorting the tree view.

Sorting Tree View

liststore-sorting.png

Sorting in GTK+ is defined in Gtk::TreeSortable module, or as GTK+ programmers like to say, in Gtk::TreeSortable interface. This interface is implemented by all model classes, hence views can demand that model data be sorted and as such also displayed in the view. This is accomplished by setting Gtk::ListStore#sort_column_id or Gtk::TreeStore#sort_column_id. However, in order to equip column headers with sorting visual indicators and triggers to initialize sorting processes, view also needs to be aware those columns. To give this information to tree view we employ Gtk::TreeViewColumn#sort_column_id=(sort_column_number). But if you follow the code in our example you will notice that it is sufficient to set the model's sort_column_id and that you can ignore the latter, namely Gtk::TreeViewColumn#set_sort_column_id. Also important feature for controlling sort order is set the "clicked" signal emitted by the column header when clicked. In it you can toggle the sort indicator arrow, and indeed, the sort order on the display, by manipulating the Gtk::TreeViewColumn#sort_order flag with Gtk::SORT_ASCENDING and Gtk::SORT_DESCENDING constants.

Note:
Here,sort_column_numberis the column number in the model or store and not the view column number, though, often they do coincide, just like in this program. However, one thing to remember is that you never directly refer to a view column number. View column numbers are implicitly defined when Gtk::TreeView#append_column(column) is executed, and where the argument, here named "column", had been assigned the proper model column numbers for all possible renderers associated with the particular view column that needs to be sorted.

As promised, following is the same program as the above "liststore.rb" but here with added sorting features. Please read the comments explaining sorting features to set visual and clickable sort items in the tree view column header.

liststore-sorting.rb

#!/usr/bin/env ruby
require 'gtk2'

# Add three columns to the GtkTreeView. All three of the
# columns will be displayed as text, although one is a boolean
# value and another is an integer.
#
# NOTE:
#   sorting requires model parameter  <----                       ------ (sorting) --------
#   --------------------------------
def setup_tree_view(treeview, model)
  # Create a new GtkCellRendererText, add it to the tree
  # view column and append the column to the tree view.
  renderer = Gtk::CellRendererText.new
  renderer.foreground = "#ff0000"
  column   = Gtk::TreeViewColumn.new("Buy", renderer,  :text => BUY_IT)
  treeview.append_column(column)
  renderer = Gtk::CellRendererText.new
  column   = Gtk::TreeViewColumn.new("Count", renderer, :text => QUANTITY)
  treeview.append_column(column)
  renderer = Gtk::CellRendererText.new

  column   = Gtk::TreeViewColumn.new("Product", renderer, :text => PRODUCT)

  # Set up attributes and signals to sort this list by Product column 
  # ----------------------------------------------------------------------(sorting) -s- (1/2) --- 
  column.sort_indicator=true
  column.sort_column_id = PRODUCT  # Note: PRODUCT maps to model column number
                                   #       and not to view column number!
  # column.clickable = true    # You'd need this only, if you didn't set column.sort_column_id

  column.signal_connect('clicked') do |w|
    w.sort_order = 
      w.sort_order == Gtk::SORT_ASCENDING ? Gtk::SORT_DESCENDING : Gtk::SORT_ASCENDING
  end
  # ----------------------------------------------------------------------(sorting) -s- (1/2) ---  
  treeview.append_column(column)
end

window = Gtk::Window.new("Grocery List")
window.resizable = true
window.border_width = 10

window.signal_connect('destroy') { Gtk.main_quit }
window.set_size_request(250, 165)

class GroceryItem
  attr_accessor :buy, :quantity, :product
  def initialize(b, q, p); @buy, @quantity, @product = b, q, p; end
end
BUY_IT = 0; QUANTITY = 1; PRODUCT  = 2

list = Array.new
list[0] = GroceryItem.new(true,  1, "Paper Towels") 
list[1] = GroceryItem.new(true,  2, "Bread")
list[2] = GroceryItem.new(false, 1, "Butter")
list[3] = GroceryItem.new(true,  1, "Milk")
list[4] = GroceryItem.new(false, 3, "Chips")
list[5] = GroceryItem.new(true,  4, "Soda") 

# Create a new tree model with three columns, as Boolean,
# integer and string.
store = Gtk::ListStore.new(TrueClass, Integer, String)

treeview = Gtk::TreeView.new(store)

# -------------------------------------------------------------------- (sorting) -s- (2/2) ----
setup_tree_view(treeview, store)  # NOTE: sorting requires model arrgument too

# You could set also this Gtk::TreeSortable#set_sort_column_id here, however it is redundant 
# ------------------------------------------------------------------------------------------
# store.set_sort_column_id(sort_column_id=PRODUCT, order = Gtk::SORT_ASCENDING)  # redundant

# The following signal could have also been coded in the {{ setup_tree_view() }}
store.signal_connect('sort-column-changed') { puts "Sort order has bsen reversed " }
# -------------------------------------------------------------------- (sorting) -e- (2/2) ----

# Add all of the products to the GtkListStore.
list.each_with_index do |e, i|
    iter = store.append

    iter[BUY_IT]   = list[i].buy       # same as: >>> # store.set_value(iter, BUY_IT,   list[i].buy)
    iter[QUANTITY] = list[i].quantity  # same as: >>> # store.set_value(iter, QUANTITY, list[i].quantity)
    iter[PRODUCT]  = list[i].product   # same as: >>> # store.set_value(iter, PRODUCT,  list[i].product)
end

scrolled_win = Gtk::ScrolledWindow.new
scrolled_win.add(treeview)
scrolled_win.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)

window.add(scrolled_win)
window.show_all
Gtk.main


Finally following is the promised list of Gtk::CellRendererText classes' instance methods. They are listed here without the descriptions and argument specifications, so you will appreciate the sheer volume of the material at hand. Also it may give you some feeling for how things are organized and how to search for info when you need it.

Instance Methods:
The following are over 130 instance methods defined in Gtk::CellRendererText class and over 40 methods inherited from the Gtk::CellRenderer, and 77 methods belonging to Gtk::TreeViewColumn class:
Gtk::TreeViewColumn Instance Methods:
Last modified:2012/10/06 09:57:46
Keyword(s):
References:[tut-gtk2-treev-trees] [tut-gtk2-treev] [tut-gtk2-treev-crs] [tut-gtk]