TreeViewColumns and
CellRenderers work together to display a column of
data in a TreeView. The
TreeViewColumn provides the column title and a
vertical space for the CellRenderers to render a
portion of the data from the TreeView data store. A
CellRenderer handles the rendering of each row and
column data within the confines of the
TreeViewColumn. A
TreeViewColumn can contain more than one
CellRenderer to provide a row display similar to an
HBox. A common use of multiple
CellRenderers is to combine a
CellRendererPixbuf and a
CellRendererText in one column.
An example illustrating the layout of two
TreeViewColumns: one with two
CellRenderers and one with one
CellRenderer is shown in Figure 14.2, “TreeViewColumns with CellRenderers”:
The application of each CellRenderer is
indicated with a different background color: yellow for the
CellRendererPixbuf, cyan for one
CellRendererText, and pink for the other
CellRendererText. Note that the
CellRendererPixbuf and the first
CellRendererText are in the same column headed by the
"Pixbuf and Text" header. The background color of the
CellRendererText rendering "Print File" is the
default color to show the application area in a single row.
Figure 14.2, “TreeViewColumns with CellRenderers” was created by the treeviewcolumn.py program.
The type of CellRenderer needed is
determined by the type of tree model data display required; PyGTK has three
pre-defined CellRenderers:
| renders pixbuf images either created by the program or one of the stock items. |
| renders text strings, and numbers that can be converted to a string (including ints, floats, booleans). |
| renders a boolean value as a toggle button or a radio button |
The properties of a CellRenderer determine
how the data will be rendered:
| "mode" | Read-Write | The editable mode of the
CellRenderer. One of:
gtk.CELL_RENDERER_MODE_INERT,
gtk.CELL_RENDERER_MODE_ACTIVATABLE or
gtk.CELL_RENDERER_MODE_EDITABLE |
| "visible" | Read-Write | If TRUE the cell is displayed |
| "xalign" | Read-Write | The fraction of free space to the left of the cell in the range 0.0 to 1.0. |
| "yalign" | Read-Write | The fraction of free space above the cell in the range 0.0 to 1.0. |
| "xpad" | Read-Write | The amount of padding to the left and right of the cell. |
| "ypad" | Read-Write | The amount of padding above and below cell. |
| "width" | Read-Write | The fixed width of the cell. |
| "height" | Read-Write | The fixed height of the cell. |
| "is-expander" | Read-Write | If TRUE the row has children |
| "is-expanded" | Read-Write | If TRUE the row has children and it is
expanded to show the children. |
| "cell-background" | Write | The background color of the cell as a string. |
| "cell-background-gdk" | Read-Write | The background color of the cell as a
gtk.gdk.Color. |
| "cell-background-set" | Read-Write | If TRUE the cell background color
is set by this cellrenderer |
The above properties are available for all
CellRenderer subclasses. The individual
CellRenderer types also have their own
properties.
The CellRendererPixbuf has these
properties:
| "pixbuf" | Read-Write | The pixbuf to render - overridden by "stock-id" |
| "pixbuf-expander-open" | Read-Write | Pixbuf for open expander. |
| "pixbuf-expander-closed" | Read-Write | Pixbuf for closed expander. |
| "stock-id" | Read-Write | The stock ID of the stock icon to render |
| "stock-size" | Read-Write | The size of the rendered icon |
| "stock-detail" | Read-Write | Render detail to pass to the theme engine |
The CellRendererText has a large number of
properties mostly dealing with style specification:
| "text" | Read-Write | Text to render |
| "markup" | Read-Write | Marked up text to render. |
| "attributes" | Read-Write | A list of style attributes to apply to the text of the renderer. |
| "background" | Write | Background color as a string |
| "foreground" | Write | Foreground color as a string |
| "background-gdk" | Read-Write | Background color as a
gtk.gdk.Color |
| "foreground-gdk" | Read-Write | Foreground color as a
gtk.gdk.Color |
| "font" | Read-Write | Font description as a string |
| "font-desc" | Read-Write | Font description as a
pango.FontDescription |
| "family" | Read-Write | Name of the font family, e.g. Sans, Helvetica, Times, Monospace |
| "style" | Read-Write | Font style |
| "variant" | Read-Write | Font variant |
| "weight" | Read-Write | Font weight |
| "stretch" | Read-Write | Font stretch |
| "size" | Read-Write | Font size |
| "size-points" | Read-Write | Font size in points |
| "scale" | Read-Write | Font scaling factor |
| "editable" | Read-Write | If TRUE the text can be modified by the
user |
| "strikethrough" | Read-Write | If TRUE strike through the text |
| "underline" | Read-Write | Style of underline for this text |
| "rise" | Read-Write | Offset of text above the baseline (below the baseline if rise is negative) |
| "language" | Read-Write | The language this text is in, as an ISO code. Pango can use this as a hint when rendering the text. If you don't understand this parameter, you probably don't need it. GTK+ 2.4 and above. |
| "single-paragraph-mode" | Read-Write | If TRUE, keep all text in a single
paragraph. GTK+ 2.4 and above. |
| "background-set" | Read-Write | If TRUE apply the background
color |
| "foreground-set" | Read-Write | If TRUE apply the foreground
color |
| "family-set" | Read-Write | If TRUE apply the font
family |
| "style-set" | Read-Write | If TRUE apply the font
style |
| "variant-set" | Read-Write | If TRUE apply the font
variant |
| "weight-set" | Read-Write | If TRUE apply the font
weight |
| "stretch-set" | Read-Write | If TRUE apply the font
stretch |
| "size-set" | Read-Write | If TRUE apply the font
size |
| "scale-set" | Read-Write | If TRUE scale the font |
| "editable-set" | Read-Write | If TRUE apply the text
editability |
| "strikethrough-set" | Read-Write | If TRUE apply the
strikethrough |
| "underline-set" | Read-Write | If TRUE apply the text
underlining |
| "rise-set" | Read-Write | If TRUE apply the
rise |
| "language-set" | Read-Write | If TRUE apply the
language used to render the text. GTK+ 2.4 and above. |
Almost every CellRendererText property has
an associated boolean property (with the "-set" suffix) that indicates if
the property is to be applied. This allows you to set a property globally
and selectively enable and disable its application.
The CellRendererToggle has the following
properties:
| "activatable" | Read-Write | If TRUE, the toggle button can be
activated |
| "active" | Read-Write | If TRUE, the button is active. |
| "radio" | Read-Write | If TRUE, draw the toggle button as a
radio button |
| "inconsistent" | Read-Write | If TRUE, the button is in an
inconsistent state. GTK+ 2.2 and above. |
The properties can be set for all rows by using the
gobject.set_property() method. See the treeviewcolumn.py
program for an example using this method.
An attribute associates a tree model column with a
CellRenderer property; the
CellRenderer sets the property from the row's column
value before rendering the cell. This allows you to customize the cell
display using tree model data. An attribute can be added to the current set
by using:
treeviewcolumn.add_attribute(cell_renderer,attribute,column)
where the property specified by attribute
is set for the cell_renderer from
column. For example:
treeviewcolumn.add_attribute(cell, "cell-background", 1)
sets the CellRenderer background to the
color specified by the string in the second column of the data store.
To clear all attributes and set several new attributes at once use:
treeviewcolumn.set_attributes(cell_renderer, ...)
where the attributes of cell_renderer are
set by key-value pairs: property=column. For example, for a
CellRendererText:
treeviewcolumn.set_attributes(cell, text=0, cell_background=1, xpad=3)
sets, for each row, the text from the first column, the background color from the second column and the horizontal padding from the fourth column. See the treeviewcolumn.py program for an example using these methods.
The attributes of a CellRenderer can be
cleared using:
treeviewcolumn.clear_attributes(cell_renderer)
If setting attributes is not sufficient for your needs you can
set a function to be called for each row to set the properties for that
CellRenderer using:
treeviewcolumn.set_cell_data_func(cell_renderer,func,data=None)
where func has the signature:
def func(column, cell_renderer, tree_model, iter, user_data)
where column is the
TreeViewColumn containing
cell_renderer, tree_model is
the data store and iter is a
TreeIter pointing at a row in
tree_model. user_data is the
value of data that was passed to
set_cell_data_func().
In func you set whatever properties you
want on cell_renderer. For example the following code
fragment sets the text property to display PyGTK objects as an ID
string.
...
def obj_id_str(treeviewcolumn, cell, model, iter):
pyobj = model.get_value(iter, 0)
cell.set_property('text', str(pyobj))
return
...
treestore = gtk.TreeStore(object)
win = gtk.Window()
treeview = gtk.TreeView(treestore)
win.add(treeview)
cell = CellRendererText()
tvcolumn = gtk TreeViewColumn('Object ID', cell)
tvcolumn.set_cell_data_func(cell, obj_id_str)
treeview.append_column(tvcolumn)
iter = treestore.append(None, [win])
iter = treestore.append(iter, [treeview])
iter = treestore.append(iter, [tvcolumn])
iter = treestore.append(iter, [cell])
iter = treestore.append(None, [treestore])
...
The resulting display should be something like Figure 14.3, “CellRenderer Data Function”:
Another use of a cell data function is to control the formatting
of a numerical text display e.g. a float value. A
CellRendererText will display and automatically
convert a float to a string but with a default format "%f".
With cell data functions you can even generate the cell data for
the columns from external data. For example the filelisting.py
program uses a ListStore with just one column that
holds a list of file names. The TreeView displays
columns that include a pixbuf, the file name and the file's size, mode and
time of last change. The data is generated by the following cell data
functions:
def file_pixbuf(self, column, cell, model, iter):
filename = os.path.join(self.dirname, model.get_value(iter, 0))
filestat = statcache.stat(filename)
if stat.S_ISDIR(filestat.st_mode):
pb = folderpb
else:
pb = filepb
cell.set_property('pixbuf', pb)
return
def file_name(self, column, cell, model, iter):
cell.set_property('text', model.get_value(iter, 0))
return
def file_size(self, column, cell, model, iter):
filename = os.path.join(self.dirname, model.get_value(iter, 0))
filestat = statcache.stat(filename)
cell.set_property('text', filestat.st_size)
return
def file_mode(self, column, cell, model, iter):
filename = os.path.join(self.dirname, model.get_value(iter, 0))
filestat = statcache.stat(filename)
cell.set_property('text', oct(stat.S_IMODE(filestat.st_mode)))
return
def file_last_changed(self, column, cell, model, iter):
filename = os.path.join(self.dirname, model.get_value(iter, 0))
filestat = statcache.stat(filename)
cell.set_property('text', time.ctime(filestat.st_mtime))
return
These cell data functions retrieve the file information using the name, extract the needed data and set the cell 'text' or 'pixbuf' property with the data. Figure 14.4, “File Listing Example Using Cell Data Functions” shows the example program in action:
A CellRendererText can use Pango markup (by
setting the "markup" property) instead of a plain text string to encode
various text attributes and provide a rich text display with multiple font
style changes. See the Pango
Markup reference in the PyGTK Reference Manual
for details on the Pango markup language.
The following code fragment illustrates the use of the "markup" property:
...
liststore = gtk.ListStore(str)
cell = gtk.CellRendererText()
tvcolumn = gtk.TreeViewColumn('Pango Markup', cell, markup=0)
...
liststore.append(['<span foreground="blue"><b>Pango</b></span> markup can'
' change\n<i>style</i> <big>size</big>, <u>underline,'
<s>strikethrough</s></u>,\n'
'and <span font_family="URW Chancery L"><big>font family '
'e.g. URW Chancery L</big></span>\n<span foreground="red">red'
' foreground and <span background="cyan">cyan background</span></span>'])
...
produces a display similar to Figure 14.5, “CellRendererText Markup”:
If you create pango markup on the fly you have to be careful to
replace the characters that are special to the markup language: "<",
">", "&". The Python library function
cgi.escape() can do these basic conversions.
CellRendererText cells can be made editable
to allow a user to edit the contents of the cell that is selected by
clicking it or pressing one of the Return,
Enter, Space or Shift+Space
keys. A CellRendererText is made editable for all
rows by setting its "editable" property to TRUE as
follows:
cellrenderertext.set_property('editable', True)
Individual cells can be set editable by adding an attribute to the
TreeViewColumn using the
CellRendererText similar to:
treeviewcolumn.add_attribute(cellrenderertext, "editable", 2)
which sets the "editable" property to the value contained in the third column of the data store.
Once the cell editing completes, your application should handle the "edited" signal to retrieve the new text and set the associated data store value. Otherwise the cell value reverts to its original value. The signature of the "edited" signal handler is:
def edited_cb(cell,path,new_text,user_data)
where cell is the
CellRendererText, path is the
tree path (as a string) to the row containing the edited cell,
new_text is the edited text and
user_data is context data. Since the
TreeModel is needed to use
path to set new_text in the
data store you probably want to pass the TreeModel as
user_data in the connect()
method:
cellrenderertext.connect('edited', edited_cb, model)
If you have two or more editable cells in a row, you could pass
the TreeModel column number as part of
user_data as well as the
TreeModel:
cellrenderertext.connect('edited', edited_cb, (model, col_num))
Then you can set the new text in the "edited" handler similar to
this example using a ListStore:
def edited_cb(cell, path, new_text, user_data):
liststore, column = user_data
liststore[path][column] = new_text
return
CellRendererToggle buttons can be made
activatable by setting the "activatable" property to
TRUE. Similar to editable
CellRendererText cells the "activatable" property can
be set for the entire CellRendererToggle set of cells
using the set_property() method or for individual
cells by adding an attribute to the TreeViewColumn
containing the CellRendererToggle.
cellrenderertoggle.set_property('activatable', True)
treeviewcolumn.add_attribute(cellrenderertoggle, "activatable", 1)
The setting of the individual toggle buttons can be derived from
the values in a TreeModel column by adding an
attribute, for example:
treeviewcolumn.add_attribute(cellrenderertoggle, "active", 2)
You should connect to the "toggled" signal to get notification of user clicks on the toggle buttons so that your application can change the value in the data store. For example:
cellrenderertoggle.connect("toggled", toggled_cb, (model, column))
The callback has the signature:
def toggled_cb(cellrenderertoggle,path,user_data)
where path is the tree path, as a string,
pointing to the row containing the toggle that was clicked. You should pass
the TreeModel and possibly the column index as part
of user_data to provide the necessary context for
setting the data store values. For example, your application can toggle the
data store value as follows:
def toggled_cb(cell, path, user_data):
model, column = user_data
model[path][column] = not model[path][column]
return
If your application wants to display the toggle buttons as radio buttons and have only one be set, it will have to scan the data store to deactivate the active radio button and then set the toggled button. For example:
def toggled_cb(cell, path, user_data):
model, column = user_data
for row in model:
row[column] = False
model[path][column] = True
return
takes the lazy approach of setting all data store values to
FALSE before setting the value to TRUE
for the row specified by path.
The cellrenderer.py
program illustrates the application of editable
CellRendererText and activatable
CellRendererToggle cells in a
TreeStore.
1 #!/usr/bin/env python
2 # vim: ts=4:sw=4:tw=78:nowrap
3 """ Demonstration using editable and activatable CellRenderers """
4 import pygtk
5 pygtk.require("2.0")
6 import gtk, gobject
7
8 tasks = {
9 "Buy groceries": "Go to Asda after work",
10 "Do some programming": "Remember to update your software",
11 "Power up systems": "Turn on the client but leave the server",
12 "Watch some tv": "Remember to catch ER"
13 }
14
15 class GUI_Controller:
16 """ The GUI class is the controller for our application """
17 def __init__(self):
18 # setup the main window
19 self.root = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
20 self.root.set_title("CellRenderer Example")
21 self.root.connect("destroy", self.destroy_cb)
22 # Get the model and attach it to the view
23 self.mdl = Store.get_model()
24 self.view = Display.make_view( self.mdl )
25 # Add our view into the main window
26 self.root.add(self.view)
27 self.root.show_all()
28 return
29 def destroy_cb(self, *kw):
30 """ Destroy callback to shutdown the app """
31 gtk.main_quit()
32 return
33 def run(self):
34 """ run is called to set off the GTK mainloop """
35 gtk.main()
36 return
37
38 class InfoModel:
39 """ The model class holds the information we want to display """
40 def __init__(self):
41 """ Sets up and populates our gtk.TreeStore """
42 self.tree_store = gtk.TreeStore( gobject.TYPE_STRING,
43 gobject.TYPE_BOOLEAN )
44 # places the global people data into the list
45 # we form a simple tree.
46 for item in tasks.keys():
47 parent = self.tree_store.append( None, (item, None) )
48 self.tree_store.append( parent, (tasks[item],None) )
49 return
50 def get_model(self):
51 """ Returns the model """
52 if self.tree_store:
53 return self.tree_store
54 else:
55 return None
56
57 class DisplayModel:
58 """ Displays the Info_Model model in a view """
59 def make_view( self, model ):
60 """ Form a view for the Tree Model """
61 self.view = gtk.TreeView( model )
62 # setup the text cell renderer and allows these
63 # cells to be edited.
64 self.renderer = gtk.CellRendererText()
65 self.renderer.set_property( 'editable', True )
66 self.renderer.connect( 'edited', self.col0_edited_cb, model )
67
68 # The toggle cellrenderer is setup and we allow it to be
69 # changed (toggled) by the user.
70 self.renderer1 = gtk.CellRendererToggle()
71 self.renderer1.set_property('activatable', True)
72 self.renderer1.connect( 'toggled', self.col1_toggled_cb, model ) 73
74 # Connect column0 of the display with column 0 in our list model 75 # The renderer will then display whatever is in column 0 of
76 # our model .
77 self.column0 = gtk.TreeViewColumn("Name", self.renderer, text=0) 78
79 # The columns active state is attached to the second column
80 # in the model. So when the model says True then the button
81 # will show as active e.g on.
82 self.column1 = gtk.TreeViewColumn("Complete", self.renderer1 )
83 self.column1.add_attribute( self.renderer1, "active", 1)
84 self.view.append_column( self.column0 )
85 self.view.append_column( self.column1 )
86 return self.view
87 def col0_edited_cb( self, cell, path, new_text, model ):
88 """
89 Called when a text cell is edited. It puts the new text
90 in the model so that it is displayed properly.
91 """
92 print "Change '%s' to '%s'" % (model[path][0], new_text)
93 model[path][0] = new_text
94 return
95 def col1_toggled_cb( self, cell, path, model ):
96 """
97 Sets the toggled state on the toggle button to true or false.
98 """
99 model[path][1] = not model[path][1]
100 print "Toggle '%s' to: %s" % (model[path][0], model[path][1],)
101 return
102
103 if __name__ == '__main__':
104 Store = InfoModel()
105 Display = DisplayModel()
106 myGUI = GUI_Controller()
107 myGUI.run()
The program provides editable cells in the first column and
activatable cells in the second column. Lines 64-66 create an editable
CellRendererText and connect the "edited" signal to
the col0_edited_cb() callback (lines 87-94) that
changes the appropriate row column value in the
TreeStore. Likewise lines 70-72 create an activatable
CellRendererToggle and connect the "toggled" signal
to the col1_toggled_cb() callback (lines 95-101) to
change the appropriate row value. When an editable or activatable cell is
changed, a message is printed to indicate what the change was.
Figure 14.6, “Editable and Activatable Cells” illustrates the cellrenderer.py program in operation.