Sean M Sean M - 2 months ago 7
Python Question

Tkinter mysterious binding issue

I have a bound key combination :

self.parent.bind_all('<Control-n>', self.next_marked)

It is supposed to take me to the next tag in a text widget whose parent is a frame.

def next_marked(self, skip=False):
print (len(self.text.tag_ranges('definition')))
print (self.text.index(INSERT))
next_tag = str(self.text.tag_nextrange('definition', 'insert+1c')[0])
print (self.text.index(INSERT))
spl = next_tag.split('.')
line = int(spl[0])
col = int(spl[1])
self.text.mark_set('insert', '%d.%d' % ( line, col ))

It does this when I do not use the hotkey, however when I do use the hotkey, it always moves the position of the cursor down one line and then performs the function. Is this my operating system at work? (Windows 7) Any recommendation on how to handle this?

I am using Python 2.7 and Tkinter 8.5


The problem seems to be that <Control-n> is already bound to "go to next line" on the Text class, and if there are multiple bindings, they will all be executed, in a specific order:

Tkinter first calls the best binding on the instance level, then the best binding on the toplevel window level, then the best binding on the class level (which is often a standard binding), and finally the best available binding on the application level.

So you could either overwrite the existing class-level binding of <Control-n> for all the Text widgets:

self.parent.bind_class("Text", '<Control-n>', lambda e: None)

Or bind your function to the instance (so it is scheduled before the class-level binding) and make it return "break" to cancel all subsequent bindings:

def next_marked(self, skip=False):
    return "break"

self.text.bind('<Control-n>', self.next_marked)

Also, note that when used as a callback to bind, the first parameter (after self), i.e. skip in your case, will always be the Event.