Reputation: 23
I am trying to plot live changes in a file using tcl/tk
canvas utility. I have written a simple code to find the difference in file and plot it using .c create line $oldx $oldy $newx $newy
command.
My code has a while loop to keep checking for changes in the file. When I comment out the while loop, the plot canvas opens fine, but when I uncomment the while loop, the plot canvas does not open at all.
Please suggest edits, the code:
#!/usr/bin/wish
#PROGRAM 2 : Print something when a file is changed
#
#package require Tk
#graph prep
set width 100
set height 100
canvas .c -width $width -height $height -background white
pack .c
#bind .c <Configure> {
# bind .c <Configure> {}
# .c xview scroll 0 unit
# set t 0
#}
#set t 0
#.c create line $t 239 [expr $t + 5] 239 -fill gray
.c create line 0 12 1 13
#Initial reading
set filename "data.txt"
#puts $filename
if [file exists $filename] {
#puts "file exits!"
set accessTime [file mtime $filename]
#puts $accessTime
}
#opening file
set a [open $filename]
set lines [split [read -nonewline $a] "\n"]
close $a; # Saves a few bytes :-)
#puts [llength $lines]
#printing file
set oldx 0
set oldy [lindex $lines 0]
for {set i 1} {$i < [llength $lines]} {incr i} {
#puts "$i : [lindex $lines $i]"
set newx $i
set newy [lindex $lines $i]
.c create line $oldx $oldy $newx $newy
set oldx $newx
set oldy $newy
}
## after 10000
## #looping to detect change
while 1 {
if [file exists $filename] {
after 1000
# check if new access time
set nAccessTime [file mtime $filename]
if {$accessTime != $nAccessTime} {
#puts $nAccessTime
#puts "found new"
#update access time
set accessTime $nAccessTime
#read new lines
set a [open $filename]
set lines [split [read -nonewline $a] "\n"]
close $a; # Saves a few bytes :-)
#puts [llength $lines]
for {} {$i < [llength $lines]} {incr i} {
#puts "$i : [lindex $lines $i]"
set newx $i
set newy [lindex $lines $i]
.c create line $oldx $oldy $newx $newy
set oldx $newx
set oldy $newy
}
}
}
}
Upvotes: 1
Views: 1297
Reputation: 137707
This is a classic problem with doing dynamic time-driven updates in Tk (animations have the same issue). The issue is that Tk only redraws itself when the event loop is idle; it postpones actual drawing activity until that happens, allowing it to group multiple state changes into one redraw (a vast practical efficiency boost). Most of the time this happens transparently, but when you've got a driving loop such as you've written, you get no updates happening at all.
The quick-hack way of fixing this is to change:
after 1000
to:
after 1000 {set update_ready yes}
vwait update_ready
which runs the event loop during the pause instead of stopping the process entirely. Another approach is to instead change it to:
update
after 1000
but that's significantly inferior because it means that the application is unresponsive during the wait.
By far better is to rewrite the code so that it processes the changes in timer callbacks. That's fairly major surgery to your code… unless you have Tcl 8.6, when you can use a coroutine to do it easily:
package require Tcl 8.6; # <<<< GOOD STYLE
package require Tk; # <<<< GOOD STYLE
set width 100
set height 100
canvas .c -width $width -height $height -background white
pack .c
.c create line 0 12 1 13
#Initial reading
set filename "data.txt"
#puts $filename
if [file exists $filename] {
#puts "file exits!"
set accessTime [file mtime $filename]
#puts $accessTime
}
#opening file
set a [open $filename]
set lines [split [read -nonewline $a] "\n"]
close $a; # Saves a few bytes :-)
#puts [llength $lines]
#printing file
set oldx 0
set oldy [lindex $lines 0]
for {set i 1} {$i < [llength $lines]} {incr i} {
#puts "$i : [lindex $lines $i]"
set newx $i
set newy [lindex $lines $i]
.c create line $oldx $oldy $newx $newy
set oldx $newx
set oldy $newy
}
## #looping to detect change
coroutine mainloop apply {{} { # <<< CHANGED LINE
global i filename accessTime oldx oldy
while 1 {
after 1000 [info coroutine]; # <<< CHANGED LINE
yield; # <<< CHANGED LINE
if {[file exists $filename]} {
# check if new access time
set nAccessTime [file mtime $filename]
if {$accessTime != $nAccessTime} {
#puts $nAccessTime
#puts "found new"
#update access time
set accessTime $nAccessTime
#read new lines
set a [open $filename]
set lines [split [read -nonewline $a] "\n"]
close $a; # Saves a few bytes :-)
#puts [llength $lines]
for {} {$i < [llength $lines]} {incr i} {
#puts "$i : [lindex $lines $i]"
set newx $i
set newy [lindex $lines $i]
.c create line $oldx $oldy $newx $newy
set oldx $newx
set oldy $newy
}
}
}
}
}}
You probably also want the delay before the check if the file exists, so that a non-existent file doesn't result in you hammering the OS.
Upvotes: 4