Reputation: 71
I want to get the cursor position (line and column) of the insertion point of a Tkinter.Text, but for the specific situation below.
PROBLEM: My text editor project requires a custom undo/redo for Tkinter.Text. I put in the same string for both Test One and Test Two below, but undo does not act consistently due to a inconsistent column variable in KeyRelease event handler given by Tkinter. The problem seems to be that I type too fast for second test which produces a bad column value. Can you help me find the problem?
TWO TEST PROCESS TO REPRODUCE THE ERROR:
TEST ONE
Result: Works fine. (For me atleast. Ephasis: type slowly.)
TEST TWO
Result: Gets the wrong column and does not undo properly. (Restart script and repeat this step if you don't see the error at first, it sometimes works fine with fast typing. I usually get it with 3 to 4 tries at the most.)
QUESTION: Is this a bug in Tkinter, or am I not understanding something specific within Tkinter that would produce consistent columns for my undo/redo records?
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()
Upvotes: 1
Views: 4403
Reputation: 71
I finally figured out what was going on and has nothing to do with Tkinter, but all Toolkits. I can now honestly say that I can add something to Best Programming Practices and :
Do Not Process Key Events by Binding to a Key Release Method, Instead Process Keys with a Key Press Method
Why?
It's not really a programming issue, it's a hardware issue. When a key is pressed, the physical key goes down. There is a spring that pushes the key back up. If that spring or anything about your keyboard causes a key to be even 200ths of second slower, typing even 60 words a minute may cause keys that were typed in one order to come back in another order. This may happen because a spring may be slightly thicker, stiffer, over used, or even sticky mocha cause more resistance.
Capitalization can be affected as well. Pressing the shift key and another key to get an upper case must be simultaneously pressed down. But if you process keys on key release, it is possible the shift key springs up faster than the character key you are capitalizing, which will result in a lower case. This also allows characters to be inverted. If you check on positions of a character when it's typed, you can get the wrong position due to this as well.
Stick with Key Press.
Upvotes: 5