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?




2 Comments
There is small issue with the code. If you complete the first word by autocomplete then it won’t clear the partially typed chars. For example say you have “tea” “coffee” and “beer” as autocomplete item and if i type te then it will show the “tea” as popup. If i select the “tea” then it fills the entry by “te tea”.
The follow lines can be modified to solve the problem Modify these lines if ” ” in current_text: current_text = ” “.join(current_text.split()[:-1])
to
Have a look at the version in the FAQ. It has changed a lot: http://www.async.com.br/faq/py.....14.024.htp
One Trackback/Pingback
[...] The three the most useful links that I found on this subject were: A song for the lovers, the writing a widget turotial on the PyGTK website, and the widget.py example in the PyGTK cvs. [...]