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:
- # 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()
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?

