Recently I had to customize a bit the gtk.EntryCompletion widget to match against a list of word. The particularity of the needed customization is to have this widget display its popup everytime the user types in a new word, the words are separated by space.
So I dug around because I thought that the only way was to write a custom widget, but I immediately found a problem: there’s no documentation on writing custom widgets with PyGTK except for this: widget.py (in the pygtk cvs repository). Looking here and there I found myself in the GTK+ cvs repository reading this and this… but there was nothing there for me
Oh good old C-days!
The solution (with the hint of Juhaz@#pygtk on irc.gimp.net) was a lot easier than I thought:
[code lang="python"]
gtkentrymulticompletion.py
author: Lawrence Oluyede
import pygtk pygtk.require(”2.0?) import gobject import gtk from gtk import gdk
class EntryMultiCompletion(gtk.Entry): def init(self): gtk.Entry.init(self) self.completion = gtk.EntryCompletion() # customize the matching function to match multiple space # separated words self.completion.set_match_func(self.match_func, None) # handle the match-selected signal, raised when a completion # is selected from the popup self.completion.connect(”match-selected”, self.on_completion_match) self.set_completion(self.completion)
def match_func(self, completion, key_string, iter, data):
model = self.completion.get_model()
# get the completion strings
modelstr = model[iter][0]
# check if the user has typed in a space char,
# get the last word and check if it matches something
if ” ” in key_string:
last_word = key_string.split()[-1]
return modelstr.startswith(last_word)
# we have only one word typed
return modelstr.startswith(key_string)
def on_completion_match(self, completion, model, iter):
current_text = self.get_text()
# if more than a word has been typed, we throw away the
# last one because we want to replace it with the matching word
# note: the user may have typed only a part of the entire word
# and so this step is necessary
if ” ” in current_text:
current_text = ” “.join(current_text.split()[:-1])
# add the matching word
current_text = “%s %s” % (current_text, model[iter][0])
# set back the whole text
self.set_text(current_text)
# move the cursor at the end
self.set_position(-1)
# stop the event propagation
return True
if name == “main”: # register the class as a Gtk widget gobject.type_register(EntryMultiCompletion)
win = gtk.Window()
win.connect(’delete-event’, gtk.main_quit)
entrycompl = EntryMultiCompletion()
liststore = gtk.ListStore(gobject.TYPE_STRING)
entrycompl.completion.set_model(liststore)
entrycompl.completion.set_text_column(0)
for word in [’abc’, ‘def’, ‘ghi’, ‘jkl’, ‘mno’,
‘pqr’, ’stu’, ‘vwx’, ‘yz’]:
liststore.append([word])
win.add(entrycompl)
win.show_all()
gtk.main()
[/code]
written on Python 2.4.1, PyGTK 2.6.1, GTK+ 2.6.4
It’s a bit hacky but it works!
update: fixed a couple of bugs and avoid the display of popup if the char is a space. Also, the thing it’s in the PyGTK FAQ, look: How do I insert a list of words (space separated) in a GtkEntry with the help of GtkEntryCompletion?

