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:
CellRendererPixbuf | renders pixbuf images either created by the program or one of the stock items. |
CellRendererText | renders text strings, and numbers that can be converted to a string (including ints, floats, booleans). |
CellRendererToggle | 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_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”:
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.