title: Python - How To Get Cursor Position in Tkinter Text Widget
body: <p>I want to get the cursor position (line and column) of the insertion point of a Tkinter.Text, but for the specific situation below.</p>
  
  <p>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?</p>
  
  <p>TWO TEST PROCESS TO REPRODUCE THE ERROR:</p>
  
  <p>TEST ONE</p>
  
  <ul>
  <li><ol>
  <li>Type this string slowly: 'one two three'</li>
  </ol></li>
  <li><ol start="2">
  <li>Press F1 to see each word undo.</li>
  </ol></li>
  <li><p>Result:  Works fine. (For me atleast.  Ephasis: type slowly.)</p></li>
  </ul>
  
  <p>TEST TWO</p>
  
  <ul>
  <li><ol>
  <li>Type the same string as fast as you can: 'one two three'</li>
  </ol></li>
  <li><ol start="2">
  <li>Press F1 to see each word undo.</li>
  </ol></li>
  <li><p>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.)</p></li>
  </ul>
  
  <p>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?</p>
  
  <pre><code>from Tkinter import *
  
  class TextView(Text):
  
      def __init__(self, root):
          Text.__init__(self, root)
  
          self.history = History(self)
          self.bind("&lt;KeyRelease&gt;", 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 &lt; len(self.events):
              self.events = self.events[:self.index +1]
          self.events.append(event)
          self.index +=1
  
      def undo(self):
          if self.index &gt; 0:
              self.events[self.index].undo(self.text)
              self.index -=1
  
      def redo(self):
          if self.index +1  &lt; 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("&lt;F1&gt;", textView.undo)
      root.bind("&lt;F2&gt;", textView.redo)
  
      root.mainloop()
  </code></pre>