Question

Hi I am doing a project of co-editor with Python Tkinter. I need to catch the event when the user change the position of insert index. I use Tkinter.Text as the control. Now I can only catch the <<modified>> virtual event, but how do I detect if the user changed the editing position without modifying the content? Thanks in advance

Was it helpful?

Solution

Somewhat surprisingly, there's nothing directly built-in to Tkinter to solve this. However, there's still a way to do what you want. The trick is to intercept all of the low level activity with the text widget, and then generate events which your app can then bind to.

For example, to get notified when the insertion cursor moves you need to generate an event whenever something is inserted or deleted, or the insertion cursor changes (via the mark set insert command).

We can set up a proxy to intercept all of these commands. The proxy will forward the command to the original widget object, generate an event when it detects a command that would change the insertion point, and then return the result of the original command.

Here is a working example:

# for python3, use 'tkinter' instead of 'Tkinter'
import Tkinter as tk  

class Example(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        self.text = CustomText(self, background="white")
        self.status = tk.Label(self, bd=1, relief='sunken', text="", anchor="w")
        self.status.pack(side="bottom", fill="x")
        self.text.pack(side="right", fill="both", expand=True)

        self.text.bind("<<CursorChange>>", self._on_change)

        self.text.insert("end", "one\ntwo\nthree\n")
        self.text.insert("end", "four\n",("bigfont",))
        self.text.insert("end", "five\n")

    def _on_change(self, event):
        line, char = self.text.index("insert").split(".")
        message = "Line: %s character: %s" % (line, char)
        self.status.configure(text=message)


class CustomText(tk.Text):
    def __init__(self, *args, **kwargs):
        tk.Text.__init__(self, *args, **kwargs)

        # create a proxy for the underlying widget
        self._orig = self._w + "_orig"
        self.tk.call("rename", self._w, self._orig)
        self.tk.createcommand(self._w, self._proxy)

    def _proxy(self, *args):
        cmd = (self._orig,) + args
        result = self.tk.call(cmd)

        # generate an event if something was added or deleted,
        # or the cursor position changed
        if (args[0] in ("insert", "delete") or 
            args[0:3] == ("mark", "set", "insert")):
            self.event_generate("<<CursorChange>>", when="tail")

        return result        


if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True);
    root.mainloop()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top