Python - 如何在 Tkinter 文本小部件中获取光标位置

jev*_*van 1 python tkinter python-2.7

我想获取 Tkinter.Text 插入点的光标位置(行和列),但针对下面的具体情况。

问题:我的文本编辑器项目需要对 Tkinter.Text 进行自定义撤消/重做。我在下面的测试一和测试二中输入了相同的字符串,但由于 Tkinter 给出的 KeyRelease 事件处理程序中的列变量不一致,撤消操作不一致。问题似乎是我对于第二次测试输入太快,这会产生错误的列值。你能帮我找出问题所在吗?

重现错误的两个测试过程:

测试一

    1. 慢慢地输入这个字符串:“一二三”
    1. 按 F1 查看每个单词的撤消。
  • 结果:工作正常。(至少对我来说。强调:慢慢打字。)

测试二

    1. 尽可能快地输入相同的字符串:“一二三”
    1. 按 F1 查看每个单词的撤消。
  • 结果:获取错误的列并且无法正确撤消。(如果您一开始没有看到错误,请重新启动脚本并重复此步骤,有时快速打字可以正常工作。我通常最多尝试 3 到 4 次就可以得到它。)

问题:这是 Tkinter 中的错误,还是我不理解 Tkinter 中的特定内容,这些内容会为我的撤消/重做记录生成一致的列?

from Tkinter import *

class TextView(Text):

    def __init__(self, root):
        Text.__init__(self, root)

        self.history = History(self)
        self.bind("<KeyRelease>", self.keyRelease)

        # used to capture a char at a time in keyRelease.  If space char is pressed it creates a Word object and adds it to undo/redo history.
        self.word = ""

    def keyRelease(self, event):
        if event.keysym == "space":
            self.word += " "
            self.makeWordRecord()
        else:
            self.word += event.char

    def makeWordRecord(self, ):
        if len(self.word):
            index = self.index(INSERT)
            wordEvent = Word(index, self.word)
            self.history.addEvent(wordEvent)
            self.word = ""

    def undo(self, event):
        self.makeWordRecord()
        self.history.undo()

    def redo(self, event):
        self.history.redo()

class History(object):
    def __init__(self, text):
        self.text = text
        self.events = []
        self.index = -1

        # create blank document record, line one, column one, no text
        self.addEvent(Word("1.0", ""))

    def addEvent(self, event):
        if self.index +1 < len(self.events):
            self.events = self.events[:self.index +1]
        self.events.append(event)
        self.index +=1

    def undo(self):
        if self.index > 0:
            self.events[self.index].undo(self.text)
            self.index -=1

    def redo(self):
        if self.index +1  < len(self.events):
            self.index +=1
            self.events[self.index].redo(self.text)

class Word(object):
    def __init__(self, index, word):
        self.index = index
        self.word = word

    def undo(self, text):
        line = self.index.split(".")[0]
        column = int(self.index.split(".")[-1])
        startIndex = line + "." + str(column - len(self.word))
        endIndex = line + "." + str(int(column))
        text.delete(startIndex, endIndex)

    def redo(self, text):
        line = self.index.split(".")[0]
        column = int(self.index.split(".")[-1])
        startIndex = line + "." + str(column - len(self.word))
        text.insert(startIndex, self.word)

if __name__ == "__main__":
    root = Tk()
    root.geometry("400x200+0+0")

    textView = TextView(root)
    textView.pack()
    root.bind("<F1>", textView.undo)
    root.bind("<F2>", textView.redo)

    root.mainloop()
Run Code Online (Sandbox Code Playgroud)

jev*_*van 5

我终于弄清楚是怎么回事了,与Tkinter无关,而是所有Toolkits。我现在可以诚实地说,我可以在最佳编程实践中添加一些内容:

不要通过绑定按键释放方法来处理按键事件,而是通过按键按下方法来处理按键

为什么?

这实际上不是一个编程问题,而是一个硬件问题。当按下某个键时,物理键会下降。有一个弹簧将钥匙推回原位。如果弹簧或键盘上的任何东西导致按键速度慢 200 分之一秒,那么即使每分钟键入 60 个单词也可能会导致按一种顺序键入的按键按另一种顺序返回。发生这种情况的原因可能是弹簧稍厚、较硬、过度使用,甚至粘稠的摩卡咖啡会导致更大的阻力。

大小写也会受到影响。必须同时按下 Shift 键和另一个键才能获得大写字母。但是,如果您在按键释放时处理按键,则 Shift 键的弹起速度可能比您要大写的字符键快,这将导致小写。这也允许字符反转。如果您在键入字符时检查字符的位置,您也可能会因此得到错误的位置。

坚持按键。