Ruby/tk and loop delay

I am writing a card game in ruby/tk.

I have a Tk_button(shuffle) that shuffles the cards and places them on a
canvas.The shuffle works fine but I would like to have a slight delay
between between each card being displayed on the canvas. Below is the
shuffle buttons command. I would like to delay the loop each time so
their is a slight delay. I have tried putting Tk.after(time)
{ part around the loop} but it just delays the shuffle and is not
visible. Not each card being displayed. I need a little direction
on implementing such a loop

thanks

shuffle.command do
52.times do
|i|

          nr = rand(56)
          nm = rand(56)
          io = newhash[nr]
          im = newhash[nm]

Tk.after(100) {

          place[nr].image  io
          newhash[nm] = io
          place[nr].image im
          newhash[nr] = im

}

        end

end

Ed Redman wrote:

I am writing a card game in ruby/tk.

I have a Tk_button(shuffle) that shuffles the cards and places them on a
canvas.The shuffle works fine but I would like to have a slight delay
between between each card being displayed on the canvas. Below is the
shuffle buttons command. I would like to delay the loop each time so
their is a slight delay. I have tried putting Tk.after(time)
{ part around the loop} but it just delays the shuffle and is not
visible. Not each card being displayed. I need a little direction
on implementing such a loop

thanks

shuffle.command do
52.times do
|i|

          nr = rand(56)
          nm = rand(56)
          io = newhash[nr]
          im = newhash[nm]

Tk.after(100) {

          place[nr].image  io
          newhash[nm] = io
          place[nr].image im
          newhash[nr] = im

}

        end

end

Are you not supposed to use sleep() for some reason? This works for me:

require ‘tk’

def do_press(e)
widget = e.widget

x = e.x
y = e.y

10.times do
x += 10
TkcLine.new(widget, x, y, x+100, y+100)
widget.update
sleep(2)
end

end

root = TkRoot.new {title ‘Canvas Test’}
canvas = TkCanvas.new(root)
canvas.pack

canvas.bind(‘ButtonRelease-1’, lambda {|e| do_press(e)})
Tk.mainloop

7stud – wrote:

Are you not suppo
sed to use sleep() for some reason?

Ok. Sleeping stops the whole event loop from executing, which isn’t a
good idea.

From: Ed Redman [email protected]
Subject: ruby/tk and loop delay
Date: Mon, 14 Apr 2008 08:20:05 +0900
Message-ID: 4ywMj.10891$682.6491@edtnps90

I have a Tk_button(shuffle) that shuffles the cards and places them on a
canvas.The shuffle works fine but I would like to have a slight delay
between between each card being displayed on the canvas. Below is the
shuffle buttons command. I would like to delay the loop each time so
their is a slight delay. I have tried putting Tk.after(time)
{ part around the loop} but it just delays the shuffle and is not
visible. Not each card being displayed. I need a little direction
on implementing such a loop

On Tk, re-drawing widgets occurs when idle-tasks.
The callback operation for button click is only one event.
Tk doesn’t call idle loop before finishing of the callback.
If you want to force idle loop, please call “Tk.update” or
“Tk.update_idletasks” at when you need.

However, I don’t recommend such long term operation on one callback.
Such callbacks may block callbacks for other events.
That is, you may get bad response of GUI.

I think that Thread + TkTimer is better for your case.

proc exchange{|nr, nm|
newhash[nr], newhasn[nm] = newhash[nm], newhasn[nr]
place[nr].image newhash[nr]
place[nm].image newhash[nm]

Tk.update ### if you need

}

100ms interval, 52 times

tm = TkTimer.new(100, 52){ exchange.call(rand(56), rand(56)) }

shuffle.command{
shuffle.state(:disable)
Thread.new{ tm.start.wait; shuffle.state(:normal) }
}

On this code, the callback for clicking ‘shuffle’ button
will finish in short term.
And GUI can treat the next event soon, although ‘shuffle’ button
is disabled.
The thread created on the callback starts and waits the timer,
and enable ‘shuffle’ button when the timer is finished.

I should add the method to entry a proc executed at end of a timer.

Then, a thread isn’t required for such operataion.

Hidetoshi NAGAI wrote:

From: Ed Redman [email protected]
Subject: ruby/tk and loop delay
Date: Mon, 14 Apr 2008 08:20:05 +0900
Message-ID: 4ywMj.10891$682.6491@edtnps90

I have a Tk_button(shuffle) that shuffles the cards and places them on a
canvas.The shuffle works fine but I would like to have a slight delay
between between each card being displayed on the canvas. Below is the
shuffle buttons command. I would like to delay the loop each time so
their is a slight delay. I have tried putting Tk.after(time)
{ part around the loop} but it just delays the shuffle and is not
visible. Not each card being displayed. I need a little direction
on implementing such a loop

On Tk, re-drawing widgets occurs when idle-tasks.
The callback operation for button click is only one event.
Tk doesn’t call idle loop before finishing of the callback.
If you want to force idle loop, please call “Tk.update” or
“Tk.update_idletasks” at when you need.

However, I don’t recommend such long term operation on one callback.
Such callbacks may block callbacks for other events.
That is, you may get bad response of GUI.

I think that Thread + TkTimer is better for your case.

proc exchange{|nr, nm|
newhash[nr], newhasn[nm] = newhash[nm], newhasn[nr]
place[nr].image newhash[nr]
place[nm].image newhash[nm]

Tk.update ### if you need

}

100ms interval, 52 times

tm = TkTimer.new(100, 52){ exchange.call(rand(56), rand(56)) }

shuffle.command{
shuffle.state(:disable)
Thread.new{ tm.start.wait; shuffle.state(:normal) }
}

On this code, the callback for clicking ‘shuffle’ button
will finish in short term.
And GUI can treat the next event soon, although ‘shuffle’ button
is disabled.
The thread created on the callback starts and waits the timer,
and enable ‘shuffle’ button when the timer is finished.

I should add the method to entry a proc executed at end of a timer.

Then, a thread isn’t required for such operataion.

Isn’t after() supposed to accomplish the same thing? Why doesn’t
after() work in my code in my previous post?

I tried my own threading solution, but it didn’t work–there was no
delay when drawing lines to a Canvas widget:

require ‘tk’

def do_press(e)
widget = e.widget
x = e.x
y = e.y

10.times do |i|
x += 10
TkcLine.new(widget, x, y, x+100, y+100)

t = Thread.new do
  sleep(2)
  widget.update
end

end
end

root = TkRoot.new {title ‘Canvas Test’}
canvas = TkCanvas.new(root)
canvas.pack

canvas.bind(‘ButtonRelease-1’, lambda {|e| do_press(e)})
Tk.mainloop

From: 7stud – [email protected]
Subject: Re: ruby/tk and loop delay
Date: Mon, 14 Apr 2008 19:22:40 +0900
Message-ID: [email protected]

Isn’t after() supposed to accomplish the same thing? Why doesn’t
after() work in my code in my previous post?

Probably, you misunderstand about ‘update’.
It is the command to force to flush the event queue.

First, ‘update’ executes each of the standard evenst (window-event,
timer-event, file-event, and so on) on the event queue.
Next, ‘update’ executes each of the idle tasks on the queue.
After those (the event queue is empty), ‘update’ finishes and returns.

‘update_idletasks’ doesn’t executes standard events.
It executes idle tasks only.

‘update’ and ‘update_idletasks’ are used to avoid the trouble
about the conflict between current status and planned status
of a widget.

Each widget, when needs re-drawing, registers an idle task
(for re-drawing) to the event queue.
Idle tasks are called by the eventloop, when no standard event
exists on the event queue.

So,

10.times do |i|
x += 10
TkcLine.new(widget, x, y, x+100, y+100)

t = Thread.new do
  sleep(2)
  widget.update
end

end

you cannot delay drawing widgets by ‘update’ or ‘update_idletasks’.
Even if you replace ‘widget.update’ to ‘TkcLine.new(…)’,
your code will write 10 lines at much the same time,
because those 10 threads starts at much the same time.
If you need 2 seconds of intervel, you must set the next callback
in each callback of after().

TkTimer objects do that. For example,

TkTimer.new(2000, 10){|tm|
x, y = tm.return_value # <= return value of the previous callback
x += 10
TkcLine.new(widget, x, y, x+100, y+100)
[x, y] # => passed to the next callback
}.start{ [50, 50] } # <= initail proc: return value is passed to the
# first callback

However, the intervals are not accurate.
Tk doesn’t have RealTime monitor, so the time of callback execution
and the timing of starting callbacks are the reason of why.

TkRTTimer objects do error correction about intervals.
The intervals are not accurate, too.
But TkRTTimer objects keep difference between summation of the
intervals and realtime small.

7stud – wrote:

Ed Redman wrote:

I am writing a card game in ruby/tk.

I have a Tk_button(shuffle) that shuffles the cards and places them on a
canvas.The shuffle works fine but I would like to have a slight delay
between between each card being displayed on the canvas. Below is the
shuffle buttons command. I would like to delay the loop each time so
their is a slight delay. I have tried putting Tk.after(time)
{ part around the loop} but it just delays the shuffle and is not

Try using the calling the update() method on the widget. I can’t get
the after() method to work at all: even if I specify 40,000,000
milliseconds as the delay, all the lines get drawn instantly.

require ‘tk’

def do_press(e)
widget = e.widget
x = e.x
y = e.y

threads = []

10.times do |i|
x += 10
TkcLine.new(widget, x, y, x+100, y+100)

Tk.after(40000000, proc{widget.update} )

end

end

root = TkRoot.new {title ‘Canvas Test’}
canvas = TkCanvas.new(root)
canvas.pack

canvas.bind(‘ButtonRelease-1’, lambda {|e| do_press(e)})
Tk.mainloop