Ranger
Ranger

Reputation: 101

Tcl Auto-updating clock

In tcl, I made the following simple login program.

package require Tk

wm geometry . "150x125"

set systemTime [clock seconds]

ttk::entry .loginUser
ttk::entry .loginPass -show "*"
ttk::label .l -text "Username:"
ttk::label .l2 -text "Password:"
ttk::button .b -text "Login"

pack .l
pack .loginUser
pack .l2
pack .loginPass
pack .b

bind . <Return> ".b invoke"

proc CheckLogin {} {
  if {[.loginUser get] eq "None"} {
    if {[.loginPass get] eq "1234"} {
      destroy {*}[winfo children .]
      ttk::label .time -text [clock format $::systemTime -format %T]
      ttk::button .b -text "Check time" -command {
        .time configure -text [clock format $::systemTime -format %T]
        pack .time
      }
      pack .b
    }
  }
}

.b configure -command CheckLogin

With this program, once you log in, you are presented with a single button: Check time. When the button is pressed, it gives a clock displaying the time as %H %M %S, %H being hours, %M being minutes, and %S being seconds. However, when you click the button, the time shows once, and it doesn't change. My question is, is there a way to make the clock update itself, like going 14:29:1 to 14:29:2 WITHOUT the previous time showing?

Upvotes: 0

Views: 585

Answers (2)

Donal Fellows
Donal Fellows

Reputation: 137627

The primary problem you've got is that the systemTime global variable is not being kept up to date with the actual system time. We either need to switch to using [clock seconds] directly, or we need to add a little worker “task” to keep the variable up to date. Here's the latter option:

proc updateTheTime {} {
    set ::systemTime [clock seconds]
    after 500 updateTheTime
}
updateTheTime

That'll keep the variable current and can be just added to your current code simply. Though you can do better and make it so that the current time display is also automatically updated. Doing that easily requires the -textvariable option to the ttk::label (and many other widgets support it as well):

proc updateTheTime {} {
    global systemTime displayedTime
    set systemTime [clock seconds]
    set displayedTime [clock format $systemTime -format "%T"]
    after 500 updateTheTime
}
updateTheTime

# ...

ttk::label .time -textvariable displayedTime

That's all it requires to make an active GUI; the widget will automatically notice the changed variable and update itself (it uses the same machinery that powers Tcl's variable traces). An update every half second is so cheap in computational terms that you'll not notice it, except for the fact that the timestamp will be updating. Want the widget to show something else? Just change the code that generates the value in the variable.

Upvotes: 5

Dinesh
Dinesh

Reputation: 16428

We have to use the clock seconds each time so that we can get the current time exactly. In your case, you have used ::systemTime to access the time, but it static since it is obtained at the start of the program.

If you have used

 ttk::label .time -text [clock format [clock seconds] -format %T]

Then you could have got the current time on each time you click the button.

I have added a loop to get the current time with a interval of 500ms and there is a button placed to stop the update of the time where update command places a role in it.

package require Tk

wm geometry . "150x125"

set systemTime [clock seconds]

ttk::entry .loginUser
ttk::entry .loginPass -show "*"
ttk::label .l -text "Username:"
ttk::label .l2 -text "Password:"
ttk::button .b -text "Login"

pack .l
pack .loginUser
pack .l2
pack .loginPass
pack .b

bind . <Return> ".b invoke"
set ::done 0
proc CheckLogin {} {
  if {[.loginUser get] eq "None"} {
    if {[.loginPass get] eq "1234"} {
      destroy {*}[winfo children .]
      ttk::label .time -text [clock format [clock seconds] -format %T]
      ttk::button .b -text "Check time" -command {
        .time configure -text [clock format [clock seconds] -format %T]
        pack .time  
        puts "init done : $::done"  
        while {!$::done} {
            .time configure -text [clock format [clock seconds] -format %T]
            after 500 
            update 
        }
      }
      pack .b
      ttk::button .stop -text "Stop Updating" -command {
        after 100 {
            puts "user stopped the time update"
            set ::done 1
        }
      }
      pack .stop
    }
  }
}


.b configure -command CheckLogin

Reference : update, after

Upvotes: 1

Related Questions