Quite a few people have asked me to publish my attempt at simplifying the process of creating and using a Gtk.TreeView (see this post). The truth is that life has been a bit hectic these last 5-6 weeks and I didn’t get to do any more development on it. But I think it is still very usefull, even if only as a learning aid. The code will be published to a few repositories very soon, but in the meantime, you can copy it inline right here (read the rest of this post). I’m also including a sample class that makes use of this generic class.
genericlistview.py
""" Copyright (c) 2007, Og MacielAll rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Og Maciel nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ # -*- coding: utf-8 -*- import gtk import gobject class GenericListView(gtk.TreeView): """ This class represent a generic ListView """ def __init__ (self, columns=None): """ Constructor @type columns: dictionary @param columns: Dictionary containing ordered list of the columns to be displayed, with the index as its key, followed by a list containing the column title, and gtk.CellRenderer type. Ex: columns = {0: [None, gtk.CellRendererToggle()]} @rtype: None @return: None. """ # Call the super class gtk.TreeView.__init__ (self) # If columns were passed, use them. Else, columns from derived class. if columns: self.columns = columns #self.set_fixed_height_mode(True) # Setup the list self._setupListView(self.columns) def _setupListView(self, columns): """ Initializes the ListView object by dynamically creating a ListStore and adding necessary columns based on the @columns parameter. @type columns: dictionary @param columns: Dictionary containing ordered list of the columns to be displayed, with the index as its key, followed by a list containing the column title, gtk.CellRenderer type, and custom function. Ex: columns = {0: [None, gtk.CellRendererToggle(), myfunction]} @rtype: None @return: None. """ # Temporary containers for datatypes and treeviewcolumns dataTypes = [] treeViewColumns = [] # Extract information from dictionary value for key, value in columns.items(): # Column title title = value[0] # Column gtk.CellRenderer cellrenderer = value[1] # Get data type dataType = self._getColumnInfo(cellrenderer) # Get gtk.TreeViewColumn tvColumn = self._getTreeViewColumn(title, cellrenderer, key) # Add to temporary datatype and treeviewcolumns containers dataTypes.append(dataType) treeViewColumns.append(tvColumn) # Defines the TreeStore #self.listStore = gtk.ListStore(*dataTypes) self.listStore = gtk.TreeStore(*dataTypes) # Associates the listStore to the ListView object self.set_model(self.listStore) # Add columns to the List for column in treeViewColumns: self.append_column(column) # If TRUE, hint to the theme engine to draw rows in alternating colors. self.set_rules_hint(True) def _getTreeViewColumn(self, title, type, index): """ Returns a gtk.TreeViewColumn with the title, gtk.CellRenderer, and attributes based on the title and CellRendererType passed. @type title: string @param title: String containing text to display as column header. @type type: gtk.CellRenderer @param type: gtk.CellRenderer to associate with column. @type index: integer @param index: This is the location where the new gtk.TreeViewColumn will obtain its data. @rtype: gtk.TreeViewColumn @return: Returns a gtk.TreeViewColumn with the title, gtk.CellRenderer, and attributes based on the title and CellRendererType passed. """ if isinstance(type, gtk.CellRendererToggle): tvcolumn = gtk.TreeViewColumn(title, type, active=index) elif isinstance(type, gtk.CellRendererPixbuf): tvcolumn = gtk.TreeViewColumn(title, type, pixbuf=index) else: tvcolumn = gtk.TreeViewColumn(title, type, text=index) tvcolumn.set_resizable(True) tvcolumn.set_clickable(True) return tvcolumn def _getColumnInfo(self, value): """ Return the data type to be used when building a gtk.ListStore. @type value: gtk.CellRenderer @param value: The gtk.CellRenderer associated with column. @rtype: GObject @return: Returns a tuple containing the data type (GObject). """ if isinstance(value, gtk.CellRendererToggle): value.connect ("toggled", self.toggled_item) type = gobject.TYPE_BOOLEAN elif isinstance(value, gtk.CellRendererText): type = gobject.TYPE_STRING elif isinstance(value, gtk.CellRendererPixbuf): type = gtk.gdk.Pixbuf else: type = gobject.TYPE_NONE return type # Events # TODO: Remove this from here and move it to the inherited class. def toggled_item(self, cell, path): """ Toggles CellRendererToggle on/off. @type : integer @param : . @rtype: list @return: . """ self.listStore[path][0] = not self.listStore[path][0] # Control Procedures def add(self, value, parent=None): """ Add an item to the internal liststore @type value: list @param value: List containing the data to add to the internal liststore. @type parent: gtk.TreeIter @param parent: Parent node or None. @rtype: gtk.TreeIter @return: A gtk.TreeIter pointing at the new row. """ return self.listStore.append(parent, value) def addList(self, values, parent=None): """ Add multiple itens to the internal liststore @type values: list @param values: List of lists containing the data to add to the internal liststore. @type parent: gtk.TreeIter @param parent: Parent node or None. @rtype: None @return: None. """ # Removes the model so the addition is quicker self.set_model(None) # Freezes list so to cancel refresh event self.freeze_child_notify() for value in values: self.listStore.append(parent, value) # set model back self.set_model(self.listStore) # Unfreeze the list self.thaw_child_notify() def remove(self): """ Remove the selected row @rtype: None @return: None. """ #http://eccentric.cx/misc/pygtk/pygtkfaq.html#13.8 selection = self.get_selection() model, iter = selection.get_selected() if iter: path = model.get_path(iter) model.remove(iter) # now that we removed the selection, play nice with # the user and select the next item selection.select_path(path) # well, if there was no selection that meant the user # removed the last entry, so we try to select the # last item if not selection.path_is_selected(path): row = path[0]-1 # test case for empty lists if row >= 0: selection.select_path((row,)) def getSelectedRow(self): """ Get the entire selected row. @rtype: list @return: Returns a list containing the data from the selected row. """ selection = self.get_selection() model, paths = selection.get_selected_rows() # Returns first selected row return paths[0] def getSelectedItem(self, index): """ Return the indexed item from the selected row. @type index: integer @param index: Index of the column item to return. @rtype: str @return: Returns the value for the cell in the selected row. """ selection = self.get_selection() model, iter, = selection.get_selected() return self.store.get_value(iter, index) def getCount(self): """ Returns the number of itens in the list. @rtype: int @return: Returns the number of itens in the list. """ return len(self.listStore)
And here is the derived class:
derivedclass.py
# -*- coding: utf-8 -*-
import gtk
from genericlistview import GenericListView
class BillListView(GenericListView):
"""
This class represents a ListView for bills.
"""
# This dictionary represents the columns displayed by the listview.
# It is indexed by the order you want them to be displayed, followed
# by the column title and cellrenderer type.
columns = {
0: [None, gtk.CellRendererPixbuf()],
1: ['Payee', gtk.CellRendererText()],
2: ['Amount Due', gtk.CellRendererText()],
3: ['Due Date', gtk.CellRendererText()]








Thanks for the program, I can’t wait to try it out.
I wonder… would you be willing to add me to your messenger? encompass for GTalk/Jabber. I would love to have another hand with my GSoC project.
http://launchpad.net/pystart
Thanks for the code!
Hey Jason,
I just added you to my GTalk to list. I’d be very interested in being of help to your project for sure!
Ping me (ogmaciel) when you’re online.
Cheers,
Og
This bytes working amazing, thanks a lot. If you want can contact with me when you want.
A really helpful addition would be to manage d’n'd with multiple selections enabled. That’s a real pain to manage since GTK is not able to do that correctly by itself.
@Athropos, that is a pretty good idea!!!
Hi,
Since I really need the DnD with multiple selections, I created a ListView component that can manage it (among other things, like automatic sorting). Here’s a link:
http://fingelrest.silent-blade.org/index.php?n=Hobbies.ExtListView
What do you think?