Callback For A Timer Event To Display Widget in Ruby/Tk

Kind All,

Um, I need to code an elapsed time display in Ruby/Tk.

It was straightforward to use the Time class and the Time#srtftime
method to get the time in just the form I wanted: t = Time.new; puts
“Time is #{t.strftime(”%I:%M:%S %p")}"

Likewise, for displaying the string in a TkLabel widget:
TkLabel.new(canvas, ‘text’=>“The time is #{t.strftime(”%I:%M:%S %p")}"
).pack( ‘side’=>‘top’, ‘padx’=>15, ‘pady’=>15 )

It was also easy to understand binding in general: root.bind(‘q’) { exit
}.

Or to setup a callback for a TkButton press: myButton =
TkButton.new(root, ‘text’=>‘Click me!’, ‘command’=> proc { btnProc()
}).place( ‘x’=>x, ‘y’=>y, ‘width’=>w, ‘height’=>h )

But, I have searched high and low, and I can not find out how to set up
a callback for a timer event. I just want to display a time string on
the TkCanvas or TkTopLevel window once per second. I have gone through
a Perl/Tk reference book and I’m still having trouble figuring out how
to “translate” this into Ruby/Tk.

I’ll of course keep searching and trying stuff, but if anyone
knowledgeable has a code example of how to this, or can point me to one,
or point me to a reference to the technique, I’d be grateful. Thank you
and best regards,

David

From: David B. [email protected]
Subject: Callback For A Timer Event To Display Widget in Ruby/Tk
Date: Mon, 27 Mar 2006 23:14:23 +0900
Message-ID: [email protected]

But, I have searched high and low, and I can not find out how to set up
a callback for a timer event. I just want to display a time string on
the TkCanvas or TkTopLevel window once per second. I have gone through
a Perl/Tk reference book and I’m still having trouble figuring out how
to “translate” this into Ruby/Tk.

For such case which is sensitive about interval time,
I recommend TkRTTimer class ( available Ruby 1.8.3 or later).
That is unique to Ruby/Tk.
Please see “/ext/tk/sample/tkrttimer.rb”.
It shows the difference between TkTimer class and TkRTTimer class.

TkTimer :: TkTimer.new(inerval, -1, operation).start

| operation | | operation | | operation |
–±----------±-----------±----------±-----------±----------±->
time-line
| interval | | interval |

TkRTTimer :: TkRTTimer.new(inerval, -1, operation).start

| operation | | operation | | operation |
–±----------±--------±----------±--------±----------±------->
time-line
| interval | interval | interval

argument ‘-1’ means infinite loop

===========================================================================

Hidetoshi NAGAI wrote:

From: David B. [email protected]
Subject: Callback For A Timer Event To Display Widget in Ruby/Tk
Date: Mon, 27 Mar 2006 23:14:23 +0900
Message-ID: [email protected]

But, I have searched high and low, and I can not find out how to set up
a callback for a timer event. I just want to display a time string on
the TkCanvas or TkTopLevel window once per second. I have gone through
a Perl/Tk reference book and I’m still having trouble figuring out how
to “translate” this into Ruby/Tk.

For such case which is sensitive about interval time,
I recommend TkRTTimer class ( available Ruby 1.8.3 or later).
That is unique to Ruby/Tk.
Please see “/ext/tk/sample/tkrttimer.rb”.
It shows the difference between TkTimer class and TkRTTimer class.

TkTimer :: TkTimer.new(inerval, -1, operation).start

| operation | | operation | | operation |
–±----------±-----------±----------±-----------±----------±->
time-line
| interval | | interval |

TkRTTimer :: TkRTTimer.new(inerval, -1, operation).start

| operation | | operation | | operation |
–±----------±--------±----------±--------±----------±------->
time-line
| interval | interval | interval

argument ‘-1’ means infinite loop

===========================================================================

Kind Responder,

Thank you very much. I am forever grateful.

David

Hidetoshi NAGAI wrote:

From: David B. [email protected]
Subject: Callback For A Timer Event To Display Widget in Ruby/Tk
Date: Mon, 27 Mar 2006 23:14:23 +0900
Message-ID: [email protected]

But, I have searched high and low, and I can not find out how to set up
a callback for a timer event. I just want to display a time string on
the TkCanvas or TkTopLevel window once per second. I have gone through
a Perl/Tk reference book and I’m still having trouble figuring out how
to “translate” this into Ruby/Tk.

For such case which is sensitive about interval time,
I recommend TkRTTimer class ( available Ruby 1.8.3 or later).
That is unique to Ruby/Tk.
Please see “/ext/tk/sample/tkrttimer.rb”.
It shows the difference between TkTimer class and TkRTTimer class.

Hidetoshi,

I have coded the ‘elapsed time’ display that I needed. I used TkTimer
versus TkRTTimer because I am at 1.8.2 currently, and I do not require
accurate real-time event handling at this time. If and when I do, I
will use the TkRTTimer class.

I have two quick questions: will my creation of all those Time.new
objects (two per second) cause me to run out of resources if I do it for
a long time?

Secondly, while this ‘elapsed time’ code works:

“Elapsed is #{(Time.at(Time.new.to_i - (oldTime.to_i -
18000))).strftime(”%X")}"

I have a sneaky feeling that it is not the best (Ruby?) way. Is it way
too resource wasteful, and if so, is there a more “Ruby” way?

In any case, I thank you for your help,

David

P.S. It took me a while to translate “/ext/tk/sample/tkrttimer.rb” into the URL where the code actually
was. But I figured you meant that to be a test of my ‘worthiness’!
(:>)

P.P.S. The test that I constructed, straight from your example, follows:

continuously display ‘current’ and ‘elapsed’ time once per second


require ‘tk’

root = TkRoot.new(:title=>‘Timer Event Example’)

label2 = TkLabel.new(:parent=>root, :width=>10)
.pack(:side=>:bottom, :fill=>:both)

label = TkLabel.new(:parent=>root, :width=>10)
.pack(:side=>:bottom, :fill=>:both)

oldTime = Time.new

define the ‘current time’ procedure repeated by the TkTimer object

timeProc = proc{|tObj| #<== TkTimer object
tRtnVal = tObj.return_value + 1000 # return_value keeps a result of
the last proc
label.text format(“Time is #{Time.new.strftime(”%X")}",
*(cnt.divmod(100)))
tRtnVal
}

define the ‘elapsed time’ procedure repeated by the TkTimer object

elapsedProc = proc{|tObj2| #<== TkTimer object
tRtnVal2 = tObj2.return_value + 1000 # return_value keeps a result of
the last proc
label2.text format(“Elapsed is #{(Time.at(Time.new.to_i -
(oldTime.to_i - 18000))).strftime(”%X")}", *(cnt.divmod(100)))
tRtnVal2
}

timer = TkTimer.new(1000, -1, timeProc).start(0, proc{ label.text(‘The
time is …’); 0 })

timer2 = TkTimer.new(1000, -1, elapsedProc).start(0, proc{
label2.text(‘Elapsed time is …’); 0 })

ev_quit = TkVirtualEvent.new(‘Control-c’, ‘Control-q’, ‘q’)

Tk.root.bind(ev_quit, proc{Tk.exit}).focus

Tk.mainloop

Hidetoshi NAGAI wrote:

Hidetoshi,

I have coded the ‘elapsed time’ display that I needed. I used TkTimer
versus TkRTTimer because I am at 1.8.2 currently, and I do not require
accurate real-time event handling at this time. If and when I do, I
will use the TkRTTimer class.

I have two quick questions: will my creation of all those Time.new
objects (two per second) cause me to run out of resources if I do it for
a long time?

Secondly, while this ‘elapsed time’ code works:

“Elapsed is #{(Time.at(Time.new.to_i - (oldTime.to_i -
18000))).strftime(”%X")}"

I have a sneaky feeling that it is not the best (Ruby?) way. Is it way
too resource wasteful, and if so, is there a more “Ruby” way?

In any case, I thank you for your help,

David

P.S. It took me a while to translate “/ext/tk/sample/tkrttimer.rb” into the URL where the code actually
was. But I figured you meant that to be a test of my ‘worthiness’!
(:>)

P.P.S. The test that I constructed, straight from your example, follows:

continuously display ‘current’ and ‘elapsed’ time once per second


require ‘tk’

root = TkRoot.new(:title=>‘Timer Event Example’)

label2 = TkLabel.new(:parent=>root, :width=>10)
.pack(:side=>:bottom, :fill=>:both)

label = TkLabel.new(:parent=>root, :width=>10)
.pack(:side=>:bottom, :fill=>:both)

oldTime = Time.new

define the ‘current time’ procedure repeated by the TkTimer object

timeProc = proc{|tObj| #<== TkTimer object
tRtnVal = tObj.return_value + 1000 # return_value keeps a result of
the last proc
label.text format(“Time is #{Time.new.strftime(”%X")}",
*(cnt.divmod(100)))
tRtnVal
}

define the ‘elapsed time’ procedure repeated by the TkTimer object

elapsedProc = proc{|tObj2| #<== TkTimer object
tRtnVal2 = tObj2.return_value + 1000 # return_value keeps a result of
the last proc
label2.text format(“Elapsed is #{(Time.at(Time.new.to_i -
(oldTime.to_i - 18000))).strftime(”%X")}", *(cnt.divmod(100)))
tRtnVal2
}

timer = TkTimer.new(1000, -1, timeProc).start(0, proc{ label.text(‘The
time is …’); 0 })

timer2 = TkTimer.new(1000, -1, elapsedProc).start(0, proc{
label2.text(‘Elapsed time is …’); 0 })

ev_quit = TkVirtualEvent.new(‘Control-c’, ‘Control-q’, ‘q’)

Tk.root.bind(ev_quit, proc{Tk.exit}).focus

Tk.mainloop

From: David B. [email protected]
Subject: Re: Callback For A Timer Event To Display Widget in Ruby/Tk
Date: Tue, 28 Mar 2006 20:17:14 +0900
Message-ID: [email protected]

I have two quick questions: will my creation of all those Time.new
objects (two per second) cause me to run out of resources if I do it for
a long time?

Probably, if you don’t keep any references for those Time objects,
GC will release the resources of those objects.

Secondly, while this ‘elapsed time’ code works:

“Elapsed is #{(Time.at(Time.new.to_i - (oldTime.to_i -
18000))).strftime(”%X")}"

I have a sneaky feeling that it is not the best (Ruby?) way. Is it way
too resource wasteful, and if so, is there a more “Ruby” way?

I think the following returns the same result as yours.

basetime = (Time.now - 18000).to_f # instead of “oldTime”

“Elapsed is #{(Time.now - basetime).strftime(”%X")}"

Hidetoshi NAGAI wrote:

From: David B. [email protected]
Subject: Re: Callback For A Timer Event To Display Widget in Ruby/Tk
Date: Tue, 28 Mar 2006 20:17:14 +0900
Message-ID: [email protected]

I have two quick questions: will my creation of all those Time.new
objects (two per second) cause me to run out of resources if I do it for
a long time?

Probably, if you don’t keep any references for those Time objects,
GC will release the resources of those objects.

Secondly, while this ‘elapsed time’ code works:

“Elapsed is #{(Time.at(Time.new.to_i - (oldTime.to_i -
18000))).strftime(”%X")}"

I have a sneaky feeling that it is not the best (Ruby?) way. Is it way
too resource wasteful, and if so, is there a more “Ruby” way?

I think the following returns the same result as yours.

basetime = (Time.now - 18000).to_f # instead of “oldTime”

“Elapsed is #{(Time.now - basetime).strftime(”%X")}"

H.N.,

Yes, it gives the same results. And it is much cleaner. My method was
a “Brute Force Coding” technique, because I do not understand the Time
class’ methods fully. I shall go back and study it more.

I’m having trouble placing my ‘current time’ and ‘elapsed time’ displays
(TkLabels) where I want them. I’m now studying the Ruby/Tk geometry
manager indirectly via a Perl/Tk book. I haven’t mastered it enough to
ask any intelligent questions yet, though. If I have problems all day,
I may ask you a question tomorrow. In any case, again, thank you for
your gracious assistance.

D.B.