3

I have a text widget with a scrollbar which looks something like this:

self.myWidget = Text(root) 
self.myWidget.configure(state=DISABLED)
self.myWidget.pack()

self.myWidgetScrollbar = Scrollbar(root, command=self.myWidget.yview)
self.myWidget.configure(yscrollcommand=self.myWidgetScrollbar.set)
self.myWidgetScrollbar.pack(side=LEFT,fill=Y)

The text widget is updated 4 times per second with:

self.myWidget.configure(state=NORMAL)
self.myWidget.delete(1.0, END) 
self.myWidget.insert(END, "\n".join(self.listWithStuff))  
self.myWidget.configure(state=DISABLED)

The problem is that when I try to scroll, it keeps scrolling me back up to the top (4 times per second, probably). I assume this is because all content is removed.

How can I prevent it from scrolling automatically, or possibly scroll "back" when the content has changed?

nbro
  • 15,395
  • 32
  • 113
  • 196

2 Answers2

2

You can safe the position and set it back after an update, like this Tcl code does:

proc update_view {w s data} {
  # store position
  set pos [$s get]
  $w configure -state normal -yscrollcommand {}
  $w delete 1.0 end
  $w insert end $data
  $w configure -state disabled -yscrollcommand [list $s set]
  $s get
  $s set {*}$pos
}

The Tkinter code would look similar, something like:

def update_view(self, data):
    pos = self.myWidgetScrollbar.get()
    self.myWidget.configure(yscrollcommand=None, state=NORMAL)
    self.myWidget.delete(1.0, END)
    self.myWidget.insert(END, data)
    self.myWidget.configure(yscrollcommand=self.myWidgetScrollbar.set, state=DISABLED)
    self.myWidgetScrollbar.get()
    self.myWidgetScrollbar.set(pos)

Not sure why the get() in between is needed, maybe to force some lookup, but it works.

schlenk
  • 7,002
  • 1
  • 25
  • 29
  • I was pretty sure this would work, but sadly, it did not. It keeps scrolling up to the top after every update. Also, the get-method randomly raises exceptions, like "ValueError: invalid literal for float(): None0.0", what's that all about? – Rixosupiazolipi Feb 06 '11 at 18:19
  • By playing around with your solution I got it to work, although the set-method doesn't seem to work too well, when I use the yview_moveto from the listbox however it works just fine. Here's (basicly) the code: `codepos = scrollbar.get()` `#remove old content` `#add new content` `listWidget.yview_moveto(pos[0])` Oh, and the exceptions were caused by a threading problem. Tkinter isn't thread safe and has to be maintained within a single thread, I solved my problem by using widget.after instead of using sleep and putting the function in it's own thread. – Rixosupiazolipi Feb 14 '11 at 15:47
1

You can jump to the bottom of the widget after each update using the yview method of the Text widget. Also, to prevent frustrating users by jumping when they are trying to scroll, you can do a simple check to make sure the scrollbar is already at the bottom (a.k.a the user isn't scrolling).

if self.myWidgetScrollbar.get() == 1.0:
    self.myWidget.yview(END)
Hollinski
  • 163
  • 2
  • 8