Search code examples
pythonpygtkgtkentry

"preedit-changed" event in python gtk.Entry does not work


I'm trying to make my gtk.Entry accept only numbers. At first I tried to print something in my stdout as the event occurs, but as I ran my script nothing occurred when I entered something in my gtk.Entry (pygtk 2.24, python 2.7.2, Windows XP, also I ran the same code in Linux, results in the same). What can be wrong with the code?

import gtk

class UI:
    def delete_event(self, widget, event, data=None):
        return False

    def destroy(self, widget, data=None):
        gtk.main_quit()

    def preedit_changed(self, widget, preedit, data=None):
        print "preedit_changed"
        return True

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.delete_event)
        self.window.connect("destroy", self.destroy)
        self.window.set_border_width(5)
        self.entry = gtk.Entry()
        self.entry.connect("preedit-changed", self.preedit_changed)
        self.window.add(self.entry)
        self.window.show_all()
        gtk.main()

if __name__ == "__main__":
    ui = UI()

Solution

  • I wasn't aware of that signal (I tend to refer to somewhat older GTK+ docs on my HD), but it appears that it doesn't do what you think it does.

    After delving through the Gnome docs I discovered that "preedit-changed" is used for handling characters that are entered using multiple keypresses, eg accented letters on most keyboards, and things like ™, which on Linux is entered using the Compose key: Compose-t-m. So if you enter one of those multi-key chars into your Entry box you will get a "preedit_changed" signal. OTOH, in my experiments the preedit returned is always an empty string...

    But you can do what you want using the plain old "changed" signal. I've modified your code with a simple example that only handles digits, but it'd be pretty easy to extend it to cope with +-. as well; handling numbers in scientific notation would require a different technique.

    #! /usr/bin/env python
    
    import pygtk
    pygtk.require('2.0')
    import gtk
    
    class UI:
        def delete_event(self, widget, event):
            return False
    
        def destroy(self, widget):
            gtk.main_quit()
    
        def preedit_changed(self, widget, preedit):
            value = widget.get_text()
            print "preedit_changed: '%s', preedit: '%s'" % (value, preedit)
            return True
    
        def activated(self, widget):
            value = widget.get_text()
            print "activated: '%s'" % value
            return True
    
        def changed(self, widget):
            value = widget.get_text()
            print "changed: '%s'" % value
            return True
    
        def digits_only(self, widget):
            value = widget.get_text()
            #Remove non-digits from string
            value = ''.join([c for c in value if c.isdigit()]) 
            widget.set_text(value)
            return True
    
        def __init__(self):
            self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
            self.window.connect("delete_event", self.delete_event)
            self.window.connect("destroy", self.destroy)
            self.window.set_border_width(5)
    
            self.entry = gtk.Entry()
            self.entry.connect("activate", self.activated)
            #self.entry.connect("changed", self.changed)
            self.entry.connect("changed", self.digits_only)
            self.entry.connect("preedit_changed", self.preedit_changed)
            self.window.add(self.entry)
            self.window.show_all()
            gtk.main()
    
    if __name__ == "__main__":
        ui = UI()