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

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Efe70cff891d5722448a6512cfd1e1f1?d=identicon&s=25 David Bailey (dbailey7)
on 2006-03-27 16:14
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
E4f967492dbd03c526cc9b397e68021d?d=identicon&s=25 Hidetoshi NAGAI (Guest)
on 2006-03-27 18:45
(Received via mailing list)
From: David Bailey <david.bailey@technologist.com>
Subject: Callback For A Timer Event To Display Widget in Ruby/Tk
Date: Mon, 27 Mar 2006 23:14:23 +0900
Message-ID: <b5ce5324fa36899ba9344afc32efd04c@ruby-forum.com>
> 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 "<ruby source>/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
===========================================================================
Efe70cff891d5722448a6512cfd1e1f1?d=identicon&s=25 David Bailey (dbailey7)
on 2006-03-27 21:57
Hidetoshi NAGAI wrote:
> From: David Bailey <david.bailey@technologist.com>
> Subject: Callback For A Timer Event To Display Widget in Ruby/Tk
> Date: Mon, 27 Mar 2006 23:14:23 +0900
> Message-ID: <b5ce5324fa36899ba9344afc32efd04c@ruby-forum.com>
>> 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 "<ruby source>/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
Efe70cff891d5722448a6512cfd1e1f1?d=identicon&s=25 David Bailey (dbailey7)
on 2006-03-28 13:16
Hidetoshi NAGAI wrote:
> From: David Bailey <david.bailey@technologist.com>
> Subject: Callback For A Timer Event To Display Widget in Ruby/Tk
> Date: Mon, 27 Mar 2006 23:14:23 +0900
> Message-ID: <b5ce5324fa36899ba9344afc32efd04c@ruby-forum.com>
>> 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 "<ruby source>/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 "<ruby
source>/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
Efe70cff891d5722448a6512cfd1e1f1?d=identicon&s=25 David Bailey (dbailey7)
on 2006-03-28 16:40
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 "<ruby
source>/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
E4f967492dbd03c526cc9b397e68021d?d=identicon&s=25 Hidetoshi NAGAI (Guest)
on 2006-03-28 17:31
(Received via mailing list)
From: David Bailey <david.bailey@technologist.com>
Subject: Re: Callback For A Timer Event To Display Widget in Ruby/Tk
Date: Tue, 28 Mar 2006 20:17:14 +0900
Message-ID: <b34e30884066f0651439cca5e87f0053@ruby-forum.com>
> 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")}"
Efe70cff891d5722448a6512cfd1e1f1?d=identicon&s=25 David Bailey (dbailey7)
on 2006-03-28 20:36
Hidetoshi NAGAI wrote:
> From: David Bailey <david.bailey@technologist.com>
> Subject: Re: Callback For A Timer Event To Display Widget in Ruby/Tk
> Date: Tue, 28 Mar 2006 20:17:14 +0900
> Message-ID: <b34e30884066f0651439cca5e87f0053@ruby-forum.com>
>> 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.
This topic is locked and can not be replied to.