Date Tags english

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 Maciel

All 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()]

Comments

comments powered by Disqus