[R] R and tcltk Package

Ivan Krylov
Wed, 16 Mar 2022 11:19:34 -0000

? Tue, 15 Mar 2022 19:27:13 +0000 (UTC)
Roderick <hruodr at gmail.com> ?????:

> > library(tcltk)
> > .Tcl("set k 2")
> <Tcl> 2
>
> How do I access the variable k from R?

tclvalue('k') can give you the string representation of the variable.
With R-controlled variables (the tclVar interface), you can use tclObj
to get other representations of variables and avoid quoting hell. You
can also get the Tcl object using tcl('set', 'k').

> And how do I access R variables from tcl?

I think that you have to use callbacks for that. At least I see no way
of giving R environments to Tcl, and that would be the basis of
accessing arbitrary variables.

> Since ::RTcl2 existed, b<-tclVar() should have created something
> different and new, perhaps ::RTcl3.

The idea behind the tclVar interface was to avoid dealing with names
(since creating global variables manually may result in namespace
collisions) and only use tclvalue() and friends to access the
variables.

> Can I at least be sure that all variables generated by tclVar()
> and tclArray() are of the form ::RTcl2N ?

Yes, this has been mentioned in the R News articles (see below) and you
can see that in the tclVar/tclArray implementations. If you peek behind
the curtain, you'll see that R assumes that the number of global Tcl
variables stored in .TkRoot$env$TclVarCount is correct.

> And what are these callbacks, are they functions callable from Tcl?
> The following does not work:
>
> > f<-function() return(7)
> > f()
> [1] 7
> > .Tcl.args(f)
> [1] " { R_call 0x80e29e2e0 }"
> > .Tcl(.Tcl.args(f))
> Error in structure(.External(.C_dotTcl, ...), class = "tclObj") :
>    [tcl] invalid command name " R_call 0x80e29e2e0 ".

That's because passing an R function to a Tcl call converts it into a
command string (e.g. { R_call 0x80e29e2e0 }) to be evaluated as a whole:

f <- function() message('Hello from R from Tcl from R!')
tcl('eval', f)
# Hello from R from Tcl from R!
# <Tcl>

(Alternatively, .Tcl(.Tcl.callback(f)), because that doesn't wrap the
string into {}. .Tcl.args(f), which does wrap, makes Tcl interpret that
as a single command, which is results in an error, because "R_call
whatever" is, indeed, not a command.)

If you want to call it yourself, you have to separate R_call from its
argument. Be careful, because passing the wrong pointer to R_call will
crash the process:

.Tcl.callback(f)
# [1] "R_call 0x563e6e10b900"
tcl('R_call', '0x563e6e10b900')
# Hello from R from Tcl from R!
# <Tcl>
tcl('R_call')
#
#  *** caught segfault ***
# address (nil), cause 'memory not mapped'
# ...

> Is there a more detailed guide than help(TclInterface)?

As far as I can tell, the R-Tcl/Tk interface is not well documented,
but here are two R News articles providing some more information with
examples:

https://cran.r-project.org/doc/Rnews/Rnews_2001-3.pdf#section*.69
https://cran.r-project.org/doc/Rnews/Rnews_2002-3.pdf#section*.54

"Programming Graphical User Interfaces in R" by Michael F. Lawrence
John Verzani (ISSN 978-1-4398-5683-3) also provides some information.
After that, I'm afraid you'll have to resort to source code diving.

--
Best regards,
Ivan