[R] callback environment for Tk buttons

Luke Tierney luke at stat.umn.edu
Sat Feb 3 21:32:38 CET 2001


Thomas Vogels wrote:
> 
> Hi.  I'm running into problems with using R functions as callback
> commands for buttons in Tk.
> 
> The following Tcl/Tk script creates three buttons.  If you press hello
> it prints hello world. If you press HALLO it prints HALLO WORLD.
> Not exciting, but I need an example...
> 
>     set tt [toplevel .tt]
>     foreach i {"hello" "HALLO"} {
>         pack [button $tt.b$i -text $i -command "puts $i"] -fill x
>     }
>     pack [button $tt.dismiss -text dismiss -command "destroy $tt"] -fill x
> 
> If I translate this to R, I get:
>     tt <- tktoplevel()
>     for (i in c("hello", "HALLO"))
>         tkpack (tkbutton (tt, text=i,
>                           command=function()cat(i,"world\n")), fill="x")
>     tkpack (tkbutton (tt, text="dismiss",
>                       command=function()tkdestroy(tt)), fill="x")
> 
> which is dumb, because the value of i is now always "HALLO" and either
> button will cause R to 'cat' HALLO world.
> 
> The second try works better:
>     tt <- tktoplevel()
>     for (i in c("hello", "HALLO")) {
>         cb <- eval(substitute(function()cat(w,"world\n"), list(w=i)))
>         tkpack (tkbutton (tt, text=i, command=cb), fill="x")
>     }
>     tkpack (tkbutton (tt, text="dismiss",
>                       command=function()tkdestroy(tt)), fill="x")
> 

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.

> At last the question:  am I missing the easy way out?  Or did I jump
> out of the frying pan into the fire?  I.e. out of Tcl's quoting hell
> into R's scoping rules ;-)
> 

I  guess the answer is yes :-)

luke

-- 
Luke Tierney
University of Minnesota                      Phone:           612-625-7843
School of Statistics                         Fax:             612-624-8868
313 Ford Hall, 224 Church St. S.E.           email:      luke at stat.umn.edu
Minneapolis, MN 55455 USA                    WWW:  http://www.stat.umn.edu
-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
r-help mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
Send "info", "help", or "[un]subscribe"
(in the "body", not the subject !)  To: r-help-request at stat.math.ethz.ch
_._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._._



More information about the R-help mailing list