Skip to content

callback environment for Tk buttons

3 messages · Luke Tierney, Peter Dalgaard, Thomas J Vogels

#
Thomas Vogels wrote:
I would avoid using eval(substitute... -- it will almost always lead
to code that is less clear and harder to get right or maintain than
using scoping rules.  Two ways to do this with scoping: you can define
a function that captures the value of i you want (I've renamed it j in
the funciton)

      tt <- tktoplevel()
      mkcb<-function(j) {
         cb <- eval(substitute(function()cat(w,"world\n"), list(w=j)))
         tkpack (tkbutton (tt, text=j, command=cb), fill="x")
      }
      for (i in c("hello", "HALLO")) mkcb(i)
      tkpack (tkbutton (tt, text="dismiss",
                        command=function()tkdestroy(tt)), fill="x")

If you don't want to create a function, use local:

     tt <- tktoplevel()
     for (i in c("hello", "HALLO"))
         local({
             j <- i
             cb <- eval(substitute(function()cat(w,"world\n"), list(w=j)))
             tkpack (tkbutton (tt, text=j, command=cb), fill="x")
         })
     tkpack (tkbutton (tt, text="dismiss",
                       command=function()tkdestroy(tt)), fill="x")
 
 
Scoping of the for loop variable is one of the few places where
scoping in R is a bit tricky because there are two possible ways you
could imagine it working, and you just need to know what R does.
In R

	for (i in x) ...

creates a new variable i in the calling environment (tom level in this
case) and changes the value of that variable each time through the
loop.  After the loop its value is the final value it had in the loop.

Another possibility would have been for each iteration to create a
local environment with a new variable named i with value equal to the
element of x for the current iteration. If this is the way R had
chosen to go then your first example would have been fine.  But for a
variety of reasons R went the other way, so if you want to capture the
current value of the iteration variable you need to do it by creating
a new variable with scope limited to the iteration, which is what both
these approaches do.
I  guess the answer is yes :-)

luke
#
Luke Tierney <luke at stat.umn.edu> writes:
Hmm. I thought you didn't want eval(substitute(..))?

How about 

    tt <- tktoplevel()
    for (i in c("hello", "HALLO")) {
	cb <- local({i<-i; function()cat(i,"world\n")})
        tkpack (tkbutton (tt, text=i,
                          command=cb), fill="x")
    }
    tkpack (tkbutton (tt, text="dismiss",
                      command=function()tkdestroy(tt)), fill="x")
#
Luke, Peter,

thanks for the explanation of how to generate the callbacks and how
the for-loop works.  This helped a lot to understand what was going
on.

Regards,
  -tom