Tailing widget

Purpose: provide a mechanism for displaying the end of a file.


  proc setup filename {
    text .t -width 60 -height 10 -yscrollcommand ".scroll set"
    scrollbar .scroll -command ".t yview"
    pack .scroll -side right -fill y
    pack .t -side left

    set ::fp [open $filename]
    seek $::fp 0 end
  }


  proc read_more {} {
    set new [read $::fp]
    if {$new ne ""} {
      .t insert end $new
      .t see end
    }
    after 1000 read_more
  }

  setup logfile
  read_more

  # Notice this is portable code.
  # An industrial-strength version would probably exploit
  # namespaces ...

See also tail and tailf


Arjen Markus What is not so obvious from the above code, is that there is no easy way to tell whether the process responsible for writing the file that is tailed has finished or not. So, it is not a solution (alas) for monitoring the output of a program that writes to file and then finishes.

DKF 11Dec02 - There's no way to tell that anyway. And in any case, the process writing the file might not be on the same computer either.


People often ask for a way to 'pipe' from an external process into a text widget.


[MJ] The version below will allow more tail windows per interpreter.

 proc tailf filename {
     # if toplevel already exists wait 2 seconds before retrying
     set now [clock seconds]
     set tl .$now
     if {[info commands $tl]!=""} {
       after 2000 [list tailf $filename]
       return
     }
     toplevel $tl
     wm title $tl $filename 
     text $tl.t -width 60 -height 10 -yscrollcommand "$tl.scroll set"
     scrollbar $tl.scroll -command "$tl.t yview"
     pack $tl.scroll -side right -fill y
     pack $tl.t -side left -expand 1 -fill both
 
     set fp [open $filename]
     seek $fp 0 end
     set currpos [tell $fp]
     close $fp
     set mtime [file mtime $filename]
     read_more ${tl}.t $filename $mtime $currpos
   }
 
   proc read_more {widget filename oldmtime oldpos } {
       # check if the tail widget is not closed
       if {![winfo exists $widget]} {
         # tail window closed
         return
       } 
       set mtime [file mtime $filename]
       set currpos $oldpos
       if {$mtime != $oldmtime} {
         set fd [open $filename r]
         seek $fd $oldpos start
         while {![eof $fd]} {
           $widget insert end [read $fd]
         }
         $widget see end
         set currpos [tell $fd]
         close $fd
       }
       after 1000 [list read_more $widget $filename $mtime $currpos]
   }
 
 # example usage
 tailf c:/test.log
 tailf c:/test2.log