title: Python Tkinter Text Widget with Auto & Custom Scroll
body: <p>I wrote a simple Tkinter based Python application that reads text from a serial connection and adds it to the window, specifically a text widged.</p>
  
  <p>After a lot of tweaks and some very strange exceptions, this works. Then I added autoscrolling by doing this:</p>
  
  <pre><code>self.text.insert(END, str(parsed_line))
  self.text.yview(END)
  </code></pre>
  
  <p>These lines run in a thread. The thread blocks on reading fromt the serial connection, splits lines and then adds all lines to the widget.</p>
  
  <p>This works, too. Then I wanted to allow the user to scroll which should disable auto-scroll until the user scrolls back to the bottom.</p>
  
  <p>I found this
  <a href="https://stackoverflow.com/questions/4704165/python-tkinter-stop-text-widget-from-scrolling-when-content-is-changed">Stop Text widget from scrolling when content is changed</a>
  which seems to be related. Especially, I tried the code from DuckAssasin's comment:</p>
  
  <pre><code>if self.myWidgetScrollbar.get() == 1.0:
      self.myWidget.yview(END)
  </code></pre>
  
  <p>I also tried <code>.get()[1]</code> which is actually the element I want (bottom position). However, this crashes with the following exception:</p>
  
  <pre><code>Traceback (most recent call last):
    File "transformer-gui.py", line 119, in run
      pos = self.scrollbar.get()[1]
    File "C:\Python26\lib\lib-tk\Tkinter.py", line 2809, in get
      return self._getdoubles(self.tk.call(self._w, 'get'))
    File "C:\Python26\lib\lib-tk\Tkinter.py", line 1028, in _getdoubles
      return tuple(map(getdouble, self.tk.splitlist(string)))
  ValueError: invalid literal for float(): None
  </code></pre>
  
  <p>It seems as if tkinter somewhere returns None which then is being parsed as a float. I read somewhere, that e.g. the index method of the text widged sometimes returnes None if the requested location is not visible.</p>
  
  <p>Hopefully, anybody can help me out with this problem!</p>
  
  <p>[EDIT]</p>
  
  <p>Ok, I have assembled a demo script that can reproduce this issue on my Win XP machine:</p>
  
  <pre><code>import re,sys,time
  from Tkinter import *
  import Tkinter
  import threading
  import traceback
  
  
  class ReaderThread(threading.Thread): 
      def __init__(self, text, scrollbar):
          print "Thread init"
          threading.Thread.__init__(self) 
          self.text = text
          self.scrollbar = scrollbar
          self.running = True
  
      def stop(self):
          print "Stopping thread"
          running = False
  
      def run(self):
          print "Thread started"
          time.sleep(5)
          i = 1
          try:
              while(self.running):
                  # emulating delay when reading from serial interface
                  time.sleep(0.05)
                  line = "the quick brown fox jumps over the lazy dog\n"
  
                  curIndex = "1.0"
                  lowerEdge = 1.0
                  pos = 1.0
  
                  # get cur position
                  pos = self.scrollbar.get()[1]
  
                  # Disable scrollbar
                  self.text.configure(yscrollcommand=None, state=NORMAL)
  
                  # Add to text window
                  self.text.insert(END, str(line))
                  startIndex = repr(i) + ".0"
                  curIndex = repr(i) + ".end"
  
                  # Perform colorization
                  if i % 6 == 0:
                      self.text.tag_add("warn", startIndex, curIndex)
                  elif i % 6 == 1:
                      self.text.tag_add("debug", startIndex, curIndex)                            
                  elif i % 6 == 2:
                      self.text.tag_add("info", startIndex, curIndex)                         
                  elif i % 6 == 3:
                      self.text.tag_add("error", startIndex, curIndex)                            
                  elif i % 6 == 4:
                      self.text.tag_add("fatal", startIndex, curIndex)                            
                  i = i + 1
  
                  # Enable scrollbar
                  self.text.configure(yscrollcommand=self.scrollbar.set, state=DISABLED)
  
                  # Auto scroll down to the end if scroll bar was at the bottom before
                  # Otherwise allow customer scrolling                        
  
                  if pos == 1.0:
                      self.text.yview(END)
  
                  #if(lowerEdge == 1.0):
                  #   print "is lower edge!"
                  #self.text.see(curIndex)
                  #else:
                  #   print "Customer scrolling", lowerEdge
  
                  # Get current scrollbar position before inserting
                  #(upperEdge, lowerEdge) = self.scrollbar.get()
                  #print upperEdge, lowerEdge
  
                  #self.text.update_idletasks()
          except Exception as e:
              traceback.print_exc(file=sys.stdout)
              print "Exception in receiver thread, stopping..."
              pass
          print "Thread stopped"
  
  
  class Transformer:
      def __init__(self):
          pass
  
      def start(self):
          """starts to read linewise from self.in_stream and parses the read lines"""
          count = 1
          root = Tk()
          root.title("Tkinter Auto-Scrolling Test")
          topPane = PanedWindow(root, orient=HORIZONTAL)
          topPane.pack(side=TOP, fill=X)
          lowerPane = PanedWindow(root, orient=VERTICAL)
  
          scrollbar = Scrollbar(root)
          scrollbar.pack(side=RIGHT, fill=Y)
          text = Text(wrap=WORD, yscrollcommand=scrollbar.set)
          scrollbar.config(command=text.yview)
          # Color definition for log levels
          text.tag_config("debug",foreground="gray50")
          text.tag_config("info",foreground="green")
          text.tag_config("warn",foreground="orange")
          text.tag_config("error",foreground="red")
          text.tag_config("fatal",foreground="#8B008B")
          # set default color
          text.config(background="black", foreground="gray");
          text.pack(expand=YES, fill=BOTH)        
  
          lowerPane.add(text)
          lowerPane.pack(expand=YES, fill=BOTH)
  
          t = ReaderThread(text, scrollbar)
          print "Starting thread"
          t.start()
  
          try:
              root.mainloop()
          except Exception as e:
              print "Exception in window manager: ", e
  
          t.stop()
          t.join()
  
  
  if __name__ == "__main__":
      try:
          trans = Transformer()
          trans.start()
      except Exception as e:
          print "Error: ", e
          sys.exit(1)     
  </code></pre>
  
  <p>I let this scipt run and start to scroll up and down and after some time I get a lot of always different exceptions such as:</p>
  
  <pre><code>.\source\testtools\device-log-transformer&gt;python tkinter-autoscroll.py
  Thread init
  Starting thread
  Thread started
  Traceback (most recent call last):
    File "tkinter-autoscroll.py", line 59, in run
      self.text.configure(yscrollcommand=self.scrollbar.set, state=DISABLED)
    File "C:\Python26\lib\lib-tk\Tkinter.py", line 1202, in configure
  Stopping thread
      return self._configure('configure', cnf, kw)
    File "C:\Python26\lib\lib-tk\Tkinter.py", line 1193, in _configure
      self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
  TclError: invalid command name ".14762592"
  Exception in receiver thread, stopping...
  Thread stopped
  
  .\source\testtools\device-log-transformer&gt;python tkinter-autoscroll.py
  Thread init
  Starting thread
  Thread started
  Stopping thread
  Traceback (most recent call last):
    File "tkinter-autoscroll.py", line 35, in run
      pos = self.scrollbar.get()[1]
    File "C:\Python26\lib\lib-tk\Tkinter.py", line 2809, in get
      return self._getdoubles(self.tk.call(self._w, 'get'))
  TclError: invalid command name ".14762512"
  Exception in receiver thread, stopping...
  Thread stopped
  
  .\source\testtools\device-log-transformer&gt;python tkinter-autoscroll.py
  Thread init
  Starting thread
  Thread started
  Traceback (most recent call last):
    File "tkinter-autoscroll.py", line 65, in run
      self.text.yview(END)
    File "C:\Python26\lib\lib-tk\Tkinter.py", line 3156, in yview
      self.tk.call((self._w, 'yview') + what)
  Stopping threadTclError: invalid command name ".14762592"
  
   Exception in receiver thread, stopping...
  Thread stopped
  
  .\source\testtools\device-log-transformer&gt;python tkinter-autoscroll.py
  Thread init
  Starting thread
  Thread started
  Traceback (most recent call last):
    File "tkinter-autoscroll.py", line 35, in run
      pos = self.scrollbar.get()[1]
    File "C:\Python26\lib\lib-tk\Tkinter.py", line 2809, in get
      return self._getdoubles(self.tk.call(self._w, 'get'))
    File "C:\Python26\lib\lib-tk\Tkinter.py", line 1028, in _getdoubles
      return tuple(map(getdouble, self.tk.splitlist(string)))
  ValueError: invalid literal for float(): None
  Exception in receiver thread, stopping...
  Thread stopped
  Stopping thread
  
  .\source\testtools\device-log-transformer&gt;python tkinter-autoscroll.py
  Thread init
  Starting thread
  Thread started
  Traceback (most recent call last):
    File "tkinter-autoscroll.py", line 53, in run
      self.text.tag_add("error", startIndex, curIndex)
    File "C:\Python26\lib\lib-tk\Tkinter.py", line 3057, in tag_add
      (self._w, 'tag', 'add', tagName, index1) + args)
  TclError: bad option "261.0": must be bbox, cget, compare, configure, count, debug, delete, dlineinfo, dump, edit, get, image, index, insert, mark, pe
  er, replace, scan, search, see, tag, window, xview, or yview
  Exception in receiver thread, stopping...
  Thread stopped
  </code></pre>
  
  <p>I hope this helps you to help me :)</p>
  
  <p>Thanks,</p>
  
  <p>/J</p>