A short comparison of the R
and Tcl
language is shown in the following two sessions that do exactly the same.
# Session in R
a <- 3
x <- c(1,2,3)
b <- 4 + a + x[1]
factorial <- function(n) {
if(n == 0) {
return(1)
} else {
return(factorial(n-1)*n)
}
}
print(factorial(7))
# Same Session in Tcl
set a 3
set x [list 1 2 3]
set b [expr {4 + $a + [lindex $x 0]}]
proc factorial {n} {
if {$n eq 0} {
return 1
} else {
return [expr {[factorial\
[expr {$n-1}]]*$n}]
}
}
puts [factorial 7]
The backslash \
in Tcl
can be used to write an expression on multiple lines. A general introduction to Tcl
written by Adrian Waddell can be found here. An introduction to the tcltk
R
package by the package author Peter Dalgaard's can be found found on page 27 in R News volume 1/3 and on page 25 in R News volume 2/3.
In R
, Tcl
code can be sent to the Tcl
interpreter with the tcl()
and .Tcl()
functions.
library(tcltk)
.Tcl('set a 1')
tcl('set','a',1)
Both functions return an R
object (S3) with class tclObj
which displays itself with <Tcl> 1
in the R
console. In Tcl
, everything is a string, which makes it easy for R
to display a tcl
object by just adding a <Tcl>
in front of the string that represents the Tcl
data structure.
Note that an object of class tclObj
in R
is just the return string of a Tcl
expression. So
x <- .Tcl('set a 1')
.Tcl('set a 2')
will end with the R
variable x
to hold the string <Tcl> 1
and the Tcl
variable a
to hold the value 2
.
To create a Tcl
variable that is accessible from R
one can use the tclVar()
function as follows
a <- tclVar()
tclvalue(a) <- "Hello World"
.Tcl('info globals') # display all global variables in Tcl
.Tcl('puts $RTcl1') # show the Tcl variable directly from the Tcl interpreter
The tclVar()
is the creator function whereas the tclvalue()
is the accessor function. The tclVar()
function will create a RTcl1
, RTcl2
, etc., global Tcl
variable. info globals
is a Tcl
command to display all global variables.
Since every Tcl
object has a string representation the tclvalue()
accessor might not be as expected, as for example in
tclvalue(a) <- c(1,2,3) # set Tcl variable hold the list {1 2 3}
tclvalue(a)
the latter function will return "1 2 3"
, the string representation in Tcl
for the list [list 1 2 3]
. A workaround to this "quoting problem" is to use tclObj()
as an alternate accessor function in combination with as.character()
, as.double()
, or as.integer()
function. For example, the following R
command
as.integer(tclObj(a))
will return the desired R
vector of length 3.
Data structures passed to the tcl()
function will be converted with as.tclObj()
to a Tcl
object. Valid R
data structures that can be converted to Tcl
objects need a storage.mode()
of one of character
, integer
, logical
, and raw
. Other objects will throw an error. This means vectors (also of length one) can be converted to Tcl
lists but other R
data structures such as lists or data.frames can not be converted to Tcl
objects. Note that matrices and arrays of numbers have a storage mode double
and can hence be converted to Tcl
lists of dimension 1xn
. Here two valid examples
tcl('set','a', c(pi, 1, 2.4))
tcl('set','b', c(1, 2, "a"))
R
functions can be called from the Tcl
interpreter via callback functions. For example
foo <- function(x,y) {
x <- as.numeric(x)
y <- as.numeric(y)
print(x^2-y)
}
.Tcl.callback(foo)
will return the string "R_call 0x16dca38 %x %y"
. Hence, to call foo()
from the Tcl
interpreter one can run
.Tcl('R_call 0x16dca38 4 6')
Note that the as.numeric()
conversation in foo()
is necessary since the function call from the Tcl
interpreter passes strings as arguments to foo()
. Also, if foo()
were to return an R
object it would be lost as the R_call
will not convert R
objects to Tcl
objects.
Tk
is a graphical user interface toolkit. GUI elements are called widgets. Widgets are arranged in a window with geometry managers (such as pack
and grid
). Hence, the general workflow of creating a GUI with Tk
is to create widgets, bind them to events such as mouse- or key- press and place them into a window.
A detailed introduction to the tcltk
R
package by the package author Peter Dalgaard's can be found found on page 27 in R News volume 1/3 and on page 25 in R News volume 2/3. A good Tk
overview with many examples can be found on TkDocs.
The following R
and Tcl
code create the same GUI with a button that says "Click Me" and when clicked will output "Hello World" to the prompt.
# Session in R
tt <- tktoplevel()
b <- tkbutton(tt, text="Click Me")
tkpack(b, side = "top", anchor="w")
foo <- function() {
print("Hello World")
}
tkconfigure(b, command=foo)
# Same Session in Tcl
set tt [toplevel .1]
set b [button .1.b -text "Click Me"]
pack $b -side top -anchor w
proc foo {} {
puts "Hello World"
}
$b configure -command foo
Whereas the Tcl
code comparison above did not show any conceptual differences, the Tk
part in R
has some shortcuts that can be taken via some wrapper functions (such as tktoplevel()
) that are available in the tcltk
R
package. These wrapper functions are supposed to make the creation of Tk
GUIs more natural for R
users.
Every widget has one parent widget and possibly many children. Widgets that can have children are usually used for layout, such as the frame
widget.
The child parent relationship is enforced in Tk
by syntax. Every widget is part of a window path name where parent and child are separated by .
(dot). The toplevel window itself is called .
(dot). So a widget path name might be .a.b.c
where c
is a child of b
is a child of a
is a child of .
(dot), the toplevel window.
The window path name is used to access and modify the state of a widget. For example in the above Tcl
session the $b
gets substituted with .1.b
the window path name of the button widget.
In R
, the explicit declaration of the window path name is omitted by having tktoplevel()
, tkbutton()
, tkframe()
, etc. creating its own unique and valid path name. This results with having to pass the parent widget or window to the widget creator functions (tktoplevel()
creates a window not a widget). Window path names, however, still exist in R
and can be seen when looking at b$ID
in R
.
The pack
geometry manager is explained in detail in this document written by Adrian Waddell.
A description of all Tk
widgets and their use can be found in the Tk 8.6 manual page. The core widgets in the Tk
packages are listed and displayed in this overview.
The tcltk
R
package provides many wrapper functions for widget commands such as the configure
and cget
which are valid sub commands for every Tk
widget. A list all functions provided by the tcltk
library including the wrapper functions can be obtained with
ls(name="package:tcltk")
If, for example, one wants to see the Tcl
equivalent of an R
wrapper function, say the tkwm.geometry()
function, one can often see the tcl()
call with
getAnywhere('tkwm.geometry')
which is in this case tcl("wm", "geometry", ...)
, hence the geometry
sub-command of wm
.