Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gtk 3 position attribute on insert-text signal from Gtk.Entry is always 0

I am having trouble in managing the insert-text signal emitted by the Gtk.Entry widget. Consider the following example:

from gi.repository import Gtk

def on_insert_text(entry, new_text, new_text_length, position):
    print(position)

entry = Gtk.Entry()
entry.connect('insert-text', on_insert_text)

window = Gtk.Window()
window.connect("destroy", lambda q: Gtk.main_quit())
window.add(entry)
window.show_all()
Gtk.main()

The position attribute I am receiving on the signal handler is always 0. Unless I am misunderstanding this should it not be the position where the next text should be inserted?

In the end what I want to do is to validate the entry of text in the widget to restrict the characters that will be accepted. The way I plan to do this is similar to the example provided in the documentation in which all characters are transformed to uppercase.

like image 597
R. Cruz Avatar asked Aug 07 '16 15:08

R. Cruz


1 Answers

The handler of 'insert-text' is expected to update the value received in the position parameter (which we have seen in incorrect) to reflect the position from which future text should be inserted and return it. This is important so the cursor is changed to the right place after the signal handler returns (this is done by gtk). If you don't update and return then the cursor remains at position 0.

After following the suggestion of using entry.get_position() to obtain the right position value I found out that the update and return of position in my handler was being ignored by pygobject. It behaved as if I was not returning anything (the cursor remained at position 0). Setting the position inside the handler did not help, because gtk would change it back again to 0 after the handler returned.

After some further investigation I learned that the issue lies with the handling of in/out parameters in pygobject which works well in most cases but not with signals (see bug 644927)

If you use connect to attach a handler to the signal and the signal has an in/out parameter you may not receive what you expect in the handler and even if you return a value this value will probably not be handled correctly by pygobject either. Anything that depends on that value will probably not work as expected (e.g. advance the cursor to the new position)

There is a solution though which is to override the associated vfunc (the default handler) instead of connecting with connect(). This solution implies deriving from the base class but it does work.

You can use this method for input validation/transformation on Gtk.Entry. An example handling my use case would be:

import re
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk


class MyEntry(Gtk.Entry, Gtk.Editable):

    def __init__(self):
        super(MyEntry, self).__init__()

    def do_insert_text(self, new_text, length, position):
        regexp = re.compile('^(\d*\.?\d*)$')

        if new_text == '.' and '.' in self.get_text():
            return position
        elif regexp.match(new_text) is not None:
            self.get_buffer().insert_text(position, new_text, length)
            return position + length

        return position

entry = MyEntry()
window = Gtk.Window()
window.connect("destroy", lambda q: Gtk.main_quit())
window.add(entry)
window.show_all()

Gtk.main()

In this case the position parameter is received correctly and the return value is seen and used by pygobject so the cursor is correctly positioned.

Important Note You have to inherit from Gtk.Editable in addition to Gtk.Entry. If you do not do so you will start seeing the validation or whatever you do inside do_insert_text applying to every other Gtk.Entry in your application. If you do not inherit you are overriding the base implementation provided by Gtk.Editable which is called by all other Gtk.Entry widgets in your application. By inheriting from Gtk.Editable you override only the 'local' copy of the base implementation which only applies to your custom class.

like image 55
R. Cruz Avatar answered Nov 03 '22 02:11

R. Cruz