Version 25 of Text widget improvements

Updated 2003-09-03 18:58:23

A page to collect ideas for improving Tk's text widget, and to list current limitations.

  • Foremost amongst these is the 'scrollbar problem' when there are wrapped lines of varying lengths. As you scroll, the length of the scroll box changes!
  • Add a 'blockcursor' option to present a flashing block rather than thin line.
  • Add a method to count the number of characters between two index positions (currently the only way to do this is string length [.text get $idx1 $idx2])
  • Add helper code to implement a text replace operation which doesn't mess with the current insertion position or the current scroll position, unless such changes are actually required. (The naive use of delete followed by insert really isn't very satisfactory.)
  • Add the ability to find the index at the beginning or end of any display line, whether that line is actually currently displayed or not.
  • Add the ability to move up or down by display lines.

Vince has placed a work-in-progress patch at http://sourceforge.net/tracker/?func=detail&aid=791292&group_id=12997&atid=312997 which addresses many of these issues.


My biggest (and only) frustration with the text widget is that it is too slow or weak. Loading really large files into it is very slow and painful. That renders apps like Tkdiff or Tkgrep practically useless. -- RS: Well, comparing with Notepad, Word or such, text still loads quite acceptably, if you load the file line by line with updates in between - start reading on top, while bottom still loads. Even on a slow iPaq, iRead: a Gutenberg eBook reader is usable for files of say 400 KB (where PocketWord bails out at about 180KB)

I suppose you're right, but... Notepad and Word??? Were you out of good parameters when you wrote that? RS: Well, how else would I open a Word doc that someone sent me? And Notepad at least isn't bloated, and can handle exotic Unicodes sort of well. OTOH, emacs on Linux won't allow me to paste something from Windows if it's outside ASCII...


MGS [2003/08/22] - I would like to see Move cursor by display line in a text widget a standard feature. I guess it should be optional (and keep the current default method of cursor control for backwards compatibility).

Vince adds that he has just added '+Ndisplaylines' and '-Ndisplaylines' to the above patch so that basic display-line cursor control is now available.


Bryan Oakley: I agree with RS; I think it can also be said that the "uselessness" of an app is partly related to how the app is written. For example, tkdiff now works by leaving the whole window blank until all diffs have been processed. It is possible to write tkdiff where it would display data as it is available 1, 10 or 100 lines at a time, making it come up and be usable significantly faster. True, viewing the last diff will take just as long but most people use tools like tkdiff top down.

That's not to say I don't want the widget to be faster, but I do think it's a stretch to say tkdiff or tkgrep is "practically useless".

OK. What about a serious text editor, which is expected to display the entire document all the time? You can use that trick and display only portions at any given time, but programming most of the functionality becomes a lot more complex. ViRTually every single event, even key presses, and their relation with what is displayed or hidden must be managed all the time. - RS: No, it's easiest to load big files in one go into the text widget - just update while you're loading :D Bryan Oakley: right. I wasn't suggesting you don't load the whole document, only that you load it a bit lazily.


MGS [2003/08/22] - Of course, you can load chunks of the input file during the idle event loop with something like this:

 proc text:load {text channel {size 1024}} {

   $text insert end [read $channel $size]

   if { [eof $channel] } {
     close $channel
     puts "channel closed at [clock format [clock seconds]]"
   } else {
     after idle [list after 0 [info level 0]]
   }

 }

   text .t -yscrollcommand [list .y set]
   scrollbar .y -orient vertical -command [list .t yview]

   pack .y -side right -expand 0 -fill y
   pack .t -side left  -expand 1 -fill both

   set channel [open all.tcl r]
   puts "channel opened at [clock format [clock seconds]]"
   text:load .t $channel

Loading a 15 Mb file took about 4 seconds on my 700Mhz K7 Athlon with 384 Mb RAM.

The whiny fellow who's only contributed with pessimistic remarks in italics until now reports that the method above indeed seems to be quite efficient. But first let's put it through a serious stress test, then we'll see.

Please report your findings. We're only here to help! As far as performance goes, you might like to play with the size = 1024 value in the proc above. I only pulled that figure out of ... the air.

Yes, it looks better and better. 4096 seems to be the best size step on my machine. Don't know about other machines. And I still have to toy with [update]. Thanks.

Has anyone done any profiling to determine why loading a few megabytes into the text widget is slow. Is there an obvious bottleneck which could be improved?

On the same K7 Athlon above with the same 15 Mb file takes about 750 milliseconds to read in the data, and then about 1150 milliseconds to insert into a text widgets. That doesn't strike me as being so slow, but then I'm always open to performance improvements :-}


Category Suggestions