Reputation: 4929
I am trying to enhance the Tkinter Text
widget and provide additional capabilities.
One of the key features of my new widget is to hack the arrows/keyboard key strokes (syntax highlighting, auto-complete context menus).
I tried a direct approach of binding the Key and Up/Down keystrokes to methods, but this approach failed as my method handlers were executed before the Text event handlers therefore my methods were executed before the last keystroke handled by the text itself.
class Editor(Frame):
def __init__(self, parent, *args, **kwargs):
Frame.__init__(self, parent) # initialize the base class
# The Main Text Widget
self.text = scrolledtext.ScrolledText(self, *args, **kwargs)
self.text.pack(side='left', fill='both', expand=1)
self.text.bind("<Key>", lambda event: self.editor_key_handler())
self.text.bind("<Up>", lambda event:self.editor_arrow_key_handler('Up'))
self.text.bind("<Down>", lambda event: self.editor_arrow_key_handler('Down'))
I then tried to switch the bindtag order, and make the Class handler run first, then my instance handlers - this indeed fixed the original issue:
bindtag = list()
bindtag.append(self.text.bindtags()[1])
bindtag.append(self.text.bindtags()[0])
bindtag.append(self.text.bindtags()[2])
bindtag.append(self.text.bindtags()[3])
self.text.bindtags(bindtag)
But now as my handlers were running after the Text's... my Up / Down handlers were running after the insert cursor has already moved inside the Text losing the original position where the user clicked the Up/Down arrows.
Being an experienced Perl Tk programmer, I moved along to try and derive the Tkinter Text widget to allow me to hijack the Up/Down default handler and provide my own customised methods..
I didn't find a way to simply derive and override the Text widget (something seems trivial to an OOP system.)
How this can be done?
Upvotes: 1
Views: 260
Reputation: 385970
The best way to add custom event handling to a widget is to leave the bind tags alone, and simply add your own bindings that return the literal string "break". Returning "break" will prevent the default bindings from firing.
In the following example I've added a custom handler for the up arrow, inserting "" rather than doing the default behavior. Notice that the handler returns the string "break":
import tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.text = tk.Text(root)
self.text.pack(fill="both", expand=True)
self.text.bind("<Up>", self.handle_up)
def handle_up(self, event):
self.text.insert("insert", "<up>")
return "break"
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
For a lengthy description of what happens when a key is handled, see this answer: https://stackoverflow.com/a/11542200/7432. The answer is to a question about the Entry
widget, but event processing is the same for all widgets.
Upvotes: 2