14.4. CellRenderers

14.4.1. Overview

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”:

Figure 14.2. TreeViewColumns with CellRenderers

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.

14.4.2. CellRenderer Types

The type of CellRenderer needed is determined by the type of tree model data display required; PyGTK has three pre-defined CellRenderers:

CellRendererPixbufrenders pixbuf images either created by the program or one of the stock items.
CellRendererTextrenders text strings, and numbers that can be converted to a string (including ints, floats, booleans).
CellRendererTogglerenders a boolean value as a toggle button or a radio button

14.4.3. CellRenderer Properties

The properties of a CellRenderer determine how the data will be rendered:

"mode"Read-WriteThe 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-WriteIf TRUE the cell is displayed
"xalign"Read-WriteThe fraction of free space to the left of the cell in the range 0.0 to 1.0.
"yalign"Read-WriteThe fraction of free space above the cell in the range 0.0 to 1.0.
"xpad"Read-WriteThe amount of padding to the left and right of the cell.
"ypad"Read-WriteThe amount of padding above and below cell.
"width"Read-WriteThe fixed width of the cell.
"height"Read-WriteThe fixed height of the cell.
"is-expander"Read-WriteIf TRUE the row has children
"is-expanded"Read-WriteIf TRUE the row has children and it is expanded to show the children.
"cell-background"WriteThe background color of the cell as a string.
"cell-background-gdk"Read-WriteThe background color of the cell as a gtk.gdk.Color.
"cell-background-set"Read-WriteIf 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-WriteThe pixbuf to render - overridden by "stock-id"
"pixbuf-expander-open"Read-WritePixbuf for open expander.
"pixbuf-expander-closed"Read-WritePixbuf for closed expander.
"stock-id"Read-WriteThe stock ID of the stock icon to render
"stock-size"Read-WriteThe size of the rendered icon
"stock-detail"Read-WriteRender detail to pass to the theme engine

The CellRendererText has a large number of properties mostly dealing with style specification:

"text"Read-WriteText to render
"markup"Read-WriteMarked up text to render.
"attributes"Read-WriteA list of style attributes to apply to the text of the renderer.
"background"WriteBackground color as a string
"foreground"WriteForeground color as a string
"background-gdk"Read-WriteBackground color as a gtk.gdk.Color
"foreground-gdk"Read-WriteForeground color as a gtk.gdk.Color
"font"Read-WriteFont description as a string
"font-desc"Read-WriteFont description as a pango.FontDescription
"family"Read-WriteName of the font family, e.g. Sans, Helvetica, Times, Monospace
"style"Read-WriteFont style
"variant"Read-WriteFont variant
"weight"Read-WriteFont weight
"stretch"Read-WriteFont stretch
"size"Read-WriteFont size
"size-points"Read-WriteFont size in points
"scale"Read-WriteFont scaling factor
"editable"Read-WriteIf TRUE the text can be modified by the user
"strikethrough"Read-WriteIf TRUE strike through the text
"underline"Read-WriteStyle of underline for this text
"rise"Read-WriteOffset of text above the baseline (below the baseline if rise is negative)
"language"Read-WriteThe 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-WriteIf TRUE, keep all text in a single paragraph. GTK+ 2.4 and above.
"background-set"Read-WriteIf TRUE apply the background color
"foreground-set"Read-WriteIf TRUE apply the foreground color
"family-set"Read-WriteIf TRUE apply the font family
"style-set"Read-WriteIf TRUE apply the font style
"variant-set"Read-WriteIf TRUE apply the font variant
"weight-set"Read-WriteIf TRUE apply the font weight
"stretch-set"Read-WriteIf TRUE apply the font stretch
"size-set"Read-WriteIf TRUE apply the font size
"scale-set"Read-WriteIf TRUE scale the font
"editable-set"Read-WriteIf TRUE apply the text editability
"strikethrough-set"Read-WriteIf TRUE apply the strikethrough
"underline-set"Read-WriteIf TRUE apply the text underlining
"rise-set"Read-WriteIf TRUE apply the rise
"language-set"Read-WriteIf 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-WriteIf TRUE, the toggle button can be activated
"active"Read-WriteIf TRUE, the button is active.
"radio"Read-WriteIf TRUE, draw the toggle button as a radio button
"inconsistent"Read-WriteIf 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.

14.4.4. CellRenderer Attributes

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)

14.4.5. Cell Data Function

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_renderer, 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)
  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”:

Figure 14.3. CellRenderer Data Function

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:

Figure 14.4. File Listing Example Using Cell Data Functions

File Listing Example Using Cell Data Functions

14.4.6. CellRendererText Markup

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”:

Figure 14.5. CellRendererText Markup

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.

14.4.7. Editable Text Cells

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

14.4.8. Activatable Toggle Cells

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.

14.4.9. Editable and Activatable Cell Example Program

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.

Figure 14.6. Editable and Activatable Cells

Editable and Activatable Cells