如何在Tkinter中更改当前插入位置时实现回调?

0 投票
1 回答
1022 浏览
提问于 2025-04-18 06:01

你好,我正在用Python的Tkinter做一个协同编辑的项目。我需要捕捉用户改变插入光标位置的事件。我使用的是Tkinter.Text这个控件。目前我只能捕捉到<<modified>>这个虚拟事件,但我不知道怎么才能检测到用户在不修改内容的情况下改变了编辑位置。谢谢!

1 个回答

3

有点出乎意料的是,Tkinter里面并没有直接的功能来解决这个问题。不过,还是有办法实现你想要的效果。关键在于拦截所有与文本小部件相关的底层操作,然后生成一些事件,让你的应用程序可以绑定这些事件。

举个例子,当插入光标移动时,你需要在每次插入或删除内容,或者插入光标变化时(通过 mark set insert 命令)生成一个事件,这样才能收到通知。

我们可以设置一个代理来拦截所有这些命令。这个代理会把命令转发给原来的小部件对象,当它检测到会改变插入点的命令时,就会生成一个事件,然后再返回原命令的结果。

下面是一个可用的示例:

# 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()

撰写回答