How many lines in a text widget?

AMG: It's as easy as [$win count -lines 1.0 end].


If you don't want to do it the easy way:

If we need to incrementally work through the lines of a text widget we can obtain the content size in terms of lines and characters using:

pathName index end

This returns an index value of the form line.char where line is in the range 1..n and char is in the range 0..m. If the very last character of your content is the fifth character on line five, that character has index 5.4. The widget automatically adds a newline character, which is at 5.5 (this is where you end up if you indicate the index 5.end or, equivalently, {5.0 lineend}). The index end gets you the position after the newline on the last line, or 6.0 (it's on the first character of the non-existing line six, as it were).

If we just need the lines, and not the characters, (eg. in an incremented loop) then the following proc is always handy.

#---------------
# return actual number of lines in a text widget
#---------------
proc _lines {t} {
    expr {[lindex [split [$t index end] .] 0] - 1}
}

You can indicate a position relative to an index: in the above example, the index {5.4 + 2 chars} (usually written like 5.4+2c) is actually the end position, and end-2c is the character at 5.4. This means you can avoid the expr calculation by backing up from the end position:

proc _lines {t} {
    lindex [split [$t index end-1c] .] 0
}

You can instead avoid the split-and-lindex operation by lying to Tcl and saying the index value is a floating point number that you want truncated:

proc _lines {t} {
    expr int([$t index end-1c])
}
#---------------
# test it
#---------------
proc demo {} {
    catch {console show}
    pack [text .txt]
    .txt insert end "1\n2\n3\n4\n5"
    puts [_lines .txt]

    for {set i 1} {$i <= [_lines .txt]} {incr i} {
        puts [.txt get $i.0 "$i.0 lineend"]
    }
}

demo

Duoas: Sometimes all you want to know is how many lines there are visible in the text window. It took me a while to figure this out so I'll post the solution here (it is actually very simple). This does not account for -wrap.

proc text.vlines text {
    set  begin   [lindex [split [$text index @0,0]         .] 0]
    set  end     [lindex [split [$text index @65535,65535] .] 0]
    set  vlines  $end
    incr vlines -$begin
    incr vlines
    return $vlines
}

MG July 21 2012 - How can you find the indexes of the visible characters in the text widget? I want to bind to a text widget so that, when it's resized, it moves to put the last line that was visible before on screen (default behaviour keeps the first line visible on screen instead), but can't find out which line that is.