Can I use nested command in Tk/GUI

Hi all,

I write a simple Tk/GUI with a data structure of an array of array of
hash.
After transforming into an array of array containing 2 elements, I want
to use “Next_button” to print out the nested array sequentially. I try
to use a nested command but it doesn’t work except for the first two
presses. Can anyone help me to improve the codes?

Thanks,

#########################################
require ‘tk’

root=TkRoot.new()
root.title=“Test_Button”

myarray1=[
[{“A”=>1}],
[{“B”=>2},{“C”=>3,“D”=>4}],
[{“E”=>5,“F”=>6},{“G”=>7},{“X”=>10,“Y”=>20,“Z”=>30}]
]

myarray=[]
myarray1.flatten.each do|hsh|
hsh.each_pair do |k,v|
myarray<< [k,v]
end#kv
end#hsh

$mycount=0
button_next=TkButton.new()do
text “Next”
pack()
command proc{
a=myarray[$mycount]
puts a[0]

    command proc{ puts a[1] }

    $mycount+=1
}

end

Tk.mainloop()

If you want to loop through something, use an Enumerator:

require ‘tk’

root=TkRoot.new()
root.title=‘Test_Button’

$looper =
[[‘A’,1],[‘B’,2],[‘C’,3],[‘D’,4],[‘E’,5],[‘F’,6],[‘G’,7],[‘X’,10],[‘Y’,20],[‘Z’,30]].to_enum

button_next=TkButton.new()do
text ‘Next’
pack()
command proc{
p (
begin
$looper.next
rescue StopIteration
$looper.rewind
$looper.next
end
)
}
end

Tk.mainloop

require ‘tk’

root = TkRoot.new()
root.title = “Test_Button”

data = [
[{“A”=>1}],
[{“B”=>2},{“C”=>3,“D”=>4}],
[{“E”=>5,“F”=>6},{“G”=>7},{“X”=>10,“Y”=>20,“Z”=>30}],
]

new_data = []

data.flatten.each do|hsh|
hsh.each_pair do |k, v|
new_data << [k,v]
end
end

e = new_data.dup.to_enum
curr_arr = []

button = TkButton.new() do
text “Next”
pack()
command lambda {
begin
curr_arr = e.next if curr_arr.size == 0
puts curr_arr.shift
rescue StopIteration
puts “No more data!”
exit
end
}

end

Tk.mainloop()

require ‘tk’

root = TkRoot.new()
root.title = “Test_Button”

data = [
[{“A”=>1}],
[{“B”=>2},{“C”=>3,“D”=>4}],
[{“E”=>5,“F”=>6},{“G”=>7},{“X”=>10,“Y”=>20,“Z”=>30}],
]

new_data = []

data.flatten.each do|hsh|
hsh.each_pair do |k, v|
new_data << [k,v]
end
end

arr_number = 0
index_number = 0
curr_arr = new_data[arr_number].dup

button = TkButton.new() do
text “Next”
pack()

command lambda {

if curr_arr.size == 0
  arr_number += 1

  if arr_number == new_data.size
    puts 'no more data'
    exit
  end

  curr_arr = new_data[arr_number].dup
end

puts curr_arr.shift

}

end

Tk.mainloop()

Joel and 7stud,

Thanks both of you for the codes. One follow-up question: why enumerator
is a better choice than an array in this case?

I teach myself on Ruby once in a while. But I never use Enumerator
before.

Regis,

Thank you for the explanation.

  1. BTW: what is Ruiby? I cannot google it.

  2. Also the codes given by 7Stud and Joel are pretty neat.

If I add another button called “previous_button” to read the data,
should I use enumerator or use array since this time I need to keep
track of the index?

Basically my GUI contains two buttons: next_button,
previous_button.

I want read the data back and forth based on my “mood”: If I press
“next_button” 6 times, the screen will show A, 1, B,2, C,3… Then at
the point if I press “previous button” twice, the screen should show B,
2. If
I press “previous button” againx2, it will show A,1.

Once again thank you all for all the helps.

I only just discovered that in Ruby 2.0 you can use #cycle instead of
#to_enum if you want to loop without some additional handling, like
rescuing StopIteration :slight_smile:

Interestingly, Enumerable doesn’t have a “prev” method as an opposite to
“next”, presumably because it’s impossible to tell what the previous
object would be in certain cases (what comes before “a”?). If you want
to go backwards then I’d recommend using an array with an index and
decrementing it.

Thanks.

I would be very thankful to see both “next_button” and “previous_button”
written in the same Tk/GUI in a Rubyish way.

Li CN wrote in post #1151576:

Joel and 7stud,

why enumerator is a better choice than an array in this case?

with an array,no need of enumerator : here a example , but it need a
two object : an array and an index for current position :

================================
require ‘Ruiby’

myarray1=[
[{“A”=>1}],
[{“B”=>2},{“C”=>3,“D”=>4}],
[{“E”=>5,“F”=>6},{“G”=>7},{“X”=>10,“Y”=>20,“Z”=>30}]
]

$akv=myarray1.flatten.each_with_object([]) { |hsh,a|
hsh.each { |k,v| a << [k,v.to_s] }
}

Ruiby.app width: 300,height: 0,title: “KV” do
ek,ev=nil,nil
stack do
flow { label “key :” ; ek=entry("") }
flow { label “value:” ; ev=entry("") }
index=0
button(“next”) do
index=(index+1) % $akv.size
ek.text,ev.text=*$akv[index]
end
end
def_style <<-EEND
.entry {background-color: #FFFFAA;font: Arial bold 16;}
EEND
end

(not tk : gtk/dsl)

Hi 7Stud,

Thank you for the codes. But what is the advantage using this line

e = new_data.dup.to_enum

specifically why not just using code like this?

e = new_data.to_enum

One follow-up question: why enumerator
is a better choice than an array in this case?

You can call next() until the data runs out–instead of having
to increment a counter and repeatedly check whether
the index value is greater than the size of the array–so
it simplifies the code. Compare my verison 1 versus my
version 2.

But what is the advantage using this line

e = new_data.dup.to_enum

It allows you to destroy a copy of the data as you loop over it(e.g.
shift), leaving the original data intact, which you may not care
about…although if you want a previous button then you still need
your original data to be intact. The downside of making copies
is that for large arrays, you may run out of memory.