Skip to content

PyGTK – EntryMultiCompletion

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! :-D 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

  1. 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])

        current_text = “%s %s” % (current_text, model[iter][0])
    

    to

    if " " in current_text:
        current_text = " ".join(current_text.split()[:-1])
        current_text = "%s %s" % (current_text, model[iter][0])
    else:
        current_text = model[iter][0]
    

    Friday, January 12, 2007 at 10:10 am | Permalink
  2. Lawrence wrote:

    Have a look at the version in the FAQ. It has changed a lot: http://www.async.com.br/faq/py.....14.024.htp

    Friday, January 12, 2007 at 11:19 am | Permalink

One Trackback/Pingback

  1. [...] 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. [...]

Additional comments powered by BackType