[pygtk] Custom cell renderers

Osmo Salomaa otsaloma at cc.hut.fi
Sun Apr 10 10:35:45 WST 2005


I didn't manage to subclass CellRendererText but I got the job done the hard 
way with GenericCellRenderer. Below is the code for an editable multiline text 
cell renderer.

If you try it, use shift/ctrl/alt + return/keypad enter for linebreaking while 
editing the text in a cell.

It seems to work well. However, since I'm new to python and pygtk, any 
corrections or suggestions will be apprechiated.

Osmo


#!/usr/bin/env python
# -*- coding: utf8 -*-

import pygtk
import gtk
import gobject
import pango


class CellTextView(gtk.TextView, gtk.CellEditable):


     def set_text(self, text):

         text_buffer = self.get_buffer()
         text_buffer.set_text(text)


     def get_text(self, *args):

         text_buffer = self.get_buffer()
         start = text_buffer.get_start_iter()
         end   = text_buffer.get_end_iter()
         return text_buffer.get_text(start, end, True)


     def do_editing_done(*args):
         pass
     def do_remove_widget(*args):
         pass
     def do_start_editing(*args):
         pass


class CellRendererMultilineText(gtk.GenericCellRenderer):


     __gproperties__ = {
         'text': (gobject.TYPE_STRING, 'text', 'text displayed', '',
                  gobject.PARAM_READWRITE)
     }

     __gsignals__ = {
         'edited': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                    (gobject.TYPE_STRING, gobject.TYPE_STRING))
     }

     property_names = __gproperties__.keys()


     def __init__(self):

         self.__gobject_init__()
         self.set_property('mode', gtk.CELL_RENDERER_MODE_EDITABLE)


     def __getattr__(self, name):

         try:
             return self.get_property(name)
         except TypeError:
             raise AttributeError


     def __setattr__(self, name, value):

         try:
             self.set_property(name, value)
         except TypeError:
             self.__dict__[name] = value


     def do_get_property(self, property):

         if property.name not in self.property_names:
             raise TypeError('No property named %s' % (property.name,))
         return self.__dict__[property.name]


     def do_set_property(self, property, value):

         if property.name not in self.property_names:
             raise TypeError('No property named %s' % (property.name,))
         self.__dict__[property.name] = value


     def on_render(self, window, widget, bg_area, cell_area, exp_area, flags):

         x_offset, y_offset, width, height = self.on_get_size(widget, cell_area)
         layout = self.get_layout(widget)

         # Determine state to get text color right.
         if flags & gtk.CELL_RENDERER_SELECTED:
             if widget.get_property('has-focus'):
                 state = gtk.STATE_SELECTED
             else:
                 state = gtk.STATE_ACTIVE
         else:
             state = gtk.STATE_NORMAL

         widget.style.paint_layout(
             window, state, True, cell_area, widget, 'foo',
             cell_area.x + x_offset, cell_area.y + y_offset, layout
         )


     def get_layout(self, widget):
         '''Gets the Pango layout used in the cell in a TreeView widget.'''

         layout = pango.Layout(widget.get_pango_context())
         layout.set_width(-1)    # Do not wrap text.

         if self.text:
             layout.set_text(self.text)
         else:
             layout.set_text('')

         return layout


     def on_get_size(self, widget, cell_area):

         # The following size calculations have tested so that the TextView
         # will fully fit in the cell when editing and it will be the same
         # size as a CellRendererText cell with same amount or rows.

         xpad = 2
         ypad = 2

         xalign = 0
         yalign = 0.5

         layout = self.get_layout(widget)
         width, height = layout.get_pixel_size()

         x_offset = xpad
         y_offset = ypad

         if cell_area:

             x_offset = xalign * (cell_area.width - width)
             x_offset = max(x_offset, xpad)
             x_offset = int(round(x_offset, 0))

             y_offset = yalign * (cell_area.height - height)
             y_offset = max(y_offset, ypad)
             y_offset = int(round(y_offset, 0))

         width  = width  + (xpad * 2)
         height = height + (ypad * 2)

         return x_offset, y_offset, width, height


     def on_start_editing(self, event, widget, path, bg_area, cell_area, flags):

         editor = CellTextView()
         editor.set_wrap_mode(gtk.WRAP_NONE)
         editor.connect('key-press-event', self.on_key_press_event, path)

         if self.text:
             editor.set_text(self.text)

         editor.grab_focus()
         editor.show()

         return editor


     def on_key_press_event(self, widget, event, path):
         '''Catch pressing Enter keys.

         Shift, Ctrl or Alt combined with Return or Keypad Enter can be used
         for linebreaking. Pressing Return or Keypad Enter alone will finish
         editing.'''

         mask     = event.state
         keyname = gtk.gdk.keyval_name(event.keyval)

         accel_masks    = (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK | \
                           gtk.gdk.MOD1_MASK)
         enter_keynames = ('Return', 'KP_Enter')

         if (keyname in enter_keynames) and not (mask & accel_masks):
             self.emit('edited', path, widget.get_text())
             widget.destroy()


gobject.type_register(CellTextView)
gobject.type_register(CellRendererMultilineText)


def on_edited(widget, path, new_value, store, column):

     iter = store.get_iter(str(path))
     store.set(iter, column, new_value)


if __name__ == '__main__':

     store = gtk.ListStore(str, str)
     tree = gtk.TreeView()
     tree.set_model(store)

     tree.set_size_request(360, 140)
     tree.set_headers_visible(True)

     cr = gtk.CellRendererText()
     cr.connect('edited', on_edited, store, 0)
     cr.set_property('editable', True)
     column = gtk.TreeViewColumn('gtk.CellRendererText', cr, text = 0)
     tree.append_column(column)

     cr = CellRendererMultilineText()
     cr.connect('edited', on_edited, store, 1)
     column = gtk.TreeViewColumn('CellRendererMultilineText', cr, text = 1)
     tree.append_column(column)

     store.append(['Phou flou\nghou fhou.', 'Phou flou\nghou fhou.'])
     store.append(['Bhuu fvou\nghaa neff.', 'Bhuu fvou\nghaa neff.'])

     window = gtk.Window()
     window.set_position(gtk.WIN_POS_CENTER)
     window.connect('delete-event', gtk.main_quit)
     window.add(tree)

     window.show_all()
     gtk.main()




More information about the pygtk mailing list