#' GUI: Function Editor
#'
#' A graphical user interface (\acronym{GUI}) for defining functions in the \R language.
#'
#' @param cols list.
#' y
#' @param index integer.
#' An element index number in \code{cols}.
#' @param fun character.
#' Existing function, only used if \code{index = NULL}
#' @param value.length integer.
#' Required length for the evaluated function.
#' @param value.class character.
#' Required class for the evaluated function.
#' @param win.title character.
#' String to display as the title of the dialog box.
#' @param parent tkwin.
#' \acronym{GUI} parent window
#'
#' @details This \acronym{GUI} is appropriate for deriving new variables in a pre-existing data frame or query building.
#'
#' @return Returns an object of class list with the following components:
#' \item{fun}{user defined function (when evaluated, this string must be parseable).}
#' \item{class}{object class for the evaluated function.}
#' \item{summary}{default summary for the evaluated function.}
#' \item{sample}{first non-missing value for the evaluated function.}
#'
#' @author J.C. Fisher, U.S. Geological Survey, Idaho Water Science Center
#'
#' @seealso \code{\link{EvalFunction}}
#'
#' @keywords misc
#'
#' @import tcltk
#'
#' @export
#'
#' @examples
#' \dontrun{
#' d <- list(x = 1:10, y = 10:1)
#' Data("data.raw", d)
#' cols <- list()
#' cols[[1]] <- list(id = "X", index = 1, fun = "\"X\"")
#' cols[[2]] <- list(id = "Y", index = 2, fun = "\"Y\"")
#' cols[[3]] <- list(id = "New Variable", fun = "\"X\" + \"Y\"")
#' EditFunction(cols, index = 3)
#' }
#'
EditFunction <- function(cols, index=NULL, fun=NULL, value.length=NULL,
value.class=NULL, win.title="Edit Function", parent=NULL) {
# save function
SaveFunction <- function() {
txt <- as.character(tclvalue(tkget(f2.txt.2.1, "1.0", "end-1c")))
if (txt == "") {
rtn <<- list(fun="")
} else {
fun <- txt
pattern <- paste0("\"", ids, "\"")
replacement <- paste0("DATA[[", seq_along(ids), "]]")
for (i in seq_along(ids)) fun <- gsub(pattern[i], replacement[i], fun, fixed=TRUE)
fun <- paste0("function(DATA) {", fun, "}")
fun <- try(parse(text=fun), silent=TRUE)
if (inherits(fun, "try-error")) {
msg <- "There's a problem with function syntax, try revising."
tkmessageBox(icon="error", message=msg, detail=fun, title="Error", type="ok", parent=tt)
return()
}
obj <- EvalFunction(txt, cols)
if (inherits(obj, "try-error")) {
msg <- "Function results in error during evaluation, try revising."
tkmessageBox(icon="error", message=msg, detail=obj, title="Error", type="ok", parent=tt)
return()
}
if (!is.null(value.length) && length(obj) != value.length) {
msg <- sprintf("Evaluated function must be of length %s, try revising.", value.length)
dtl <- sprintf("Resulting vector is currently of length %s.", length(obj))
tkmessageBox(icon="error", message=msg, detail=dtl, title="Error", type="ok", parent=tt)
return()
}
if (!is.null(value.class) && !inherits(obj, value.class)) {
msg <- paste0("Filter must result in a vector of class \"", value.class,
"\". The evaluated function is a vector of class \"",
class(obj), "\", please revise.")
tkmessageBox(icon="error", message=msg, title="Error", type="ok", parent=tt)
return()
}
rtn <<- list(fun=txt, class=class(obj)[1], sample=stats::na.omit(obj)[1], summary=summary(obj))
}
tclvalue(tt.done.var) <- 1
}
# rebuild list box based on selected class type to show
RebuildList <- function() {
idx <- as.integer(tcl(f1.box.3.1, "current"))
if (idx > 0)
show.ids <- ids[vapply(cols, function(i) classes[idx] %in% i$class, TRUE)]
else
show.ids <- ids
tclvalue(variable.var) <- ""
for (i in seq_along(show.ids)) tcl("lappend", variable.var, show.ids[i])
tkselection.clear(f1.lst.2.1, 0, "end")
tclvalue(value.var) <- ""
tkconfigure(f1.but.5.1, state="disabled")
tkfocus(f2.txt.2.1)
}
# insert character string into text box
InsertString <- function(txt, sel="<variable>") {
tcl(f2.txt.2.1, "edit", "separator")
seltxt <- as.character(tktag.ranges(f2.txt.2.1, "sel"))
if (length(seltxt) > 1) tcl(f2.txt.2.1, "delete", seltxt[1], seltxt[2])
cur <- as.character(tkindex(f2.txt.2.1, "insert"))
cur <- as.integer(strsplit(cur, ".", fixed=TRUE)[[1]])
cur.line <- cur[1]
cur.char <- cur[2]
tkinsert(f2.txt.2.1, "insert", txt)
tkfocus(f2.txt.2.1)
if (txt %in% c("()", "[]")) {
cursor.insert <- paste(cur.line, cur.char + 1, sep=".")
tkmark.set(f2.txt.2.1, "insert", cursor.insert)
} else {
search.txt <- gregexpr(pattern=sel, txt)[[1]]
if (search.txt[1] > 0) {
match.idx <- search.txt[1]
match.len <- attr(search.txt, "match.length")[1]
tkfocus(f2.txt.2.1)
char <- c(match.idx, match.idx + match.len) + cur.char - 1
sel0 <- paste(cur.line, char[1], sep=".")
sel1 <- paste(cur.line, char[2], sep=".")
tktag.add(f2.txt.2.1, "sel", sel0, sel1)
tkmark.set(f2.txt.2.1, "insert", sel1)
}
}
}
# insert variable into text box
InsertVar <- function() {
idx <- as.integer(tkcurselection(f1.lst.2.1))
if (length(idx) == 0) return()
id <- as.character(tkget(f1.lst.2.1, idx, idx))
txt <- paste0("\"", id, "\"")
InsertString(txt)
}
# call date and time format editor
CallFormatDateTime <- function(sample) {
fmt <- FormatDateTime(sample=sample, parent=tt)
tkfocus(f2.txt.2.1)
if(!is.null(fmt)) InsertString(gsub("%OS[[:digit:]]+", "%OS", fmt))
}
# text edit functions
EditUndo <- function() {
tkfocus(f2.txt.2.1)
try(tcl(f2.txt.2.1, "edit", "undo"), silent=TRUE)
}
EditRedo <- function() {
tkfocus(f2.txt.2.1)
try(tcl(f2.txt.2.1, "edit", "redo"), silent=TRUE)
}
EditCut <- function() {
tkfocus(f2.txt.2.1)
tcl("tk_textCut", f2.txt.2.1)
}
EditCopy <- function() {
tkfocus(f2.txt.2.1)
tcl("tk_textCopy", f2.txt.2.1)
}
EditPaste <- function() {
tkfocus(f2.txt.2.1)
tcl("tk_textPaste", f2.txt.2.1)
}
EditSelectAll <- function() {
tkfocus(f2.txt.2.1)
tktag.add(f2.txt.2.1, "sel", "1.0", "end")
}
# clear console
ClearConsole <- function() {
tcl(f2.txt.2.1, "delete", "1.0", "end")
tkfocus(f2.txt.2.1)
}
# show unique values
ShowUniqueValues <- function() {
tkconfigure(tt, cursor="watch")
on.exit(tkconfigure(tt, cursor="arrow"))
idx <- as.integer(tkcurselection(f1.lst.2.1))
if (length(idx) == 0) return()
id <- as.character(tkget(f1.lst.2.1, idx, idx))
idx <- which(vapply(cols, function(i) i$id, "") == id)
var.fmt <- cols[[idx]]$format
if (is.null(var.fmt)) var.fmt <- ""
var.vals <- unique(EvalFunction(cols[[idx]]$fun, cols))
var.class <- cols[[idx]]$class
n <- length(var.vals)
if (n > 50000) {
msg <- paste("There are", n, "unique values; this operation can be",
"computationally expensive. Would you like to continue?")
ans <- tkmessageBox(icon="question", message=msg, title="Warning", type="yesno", parent=tt)
if (as.character(ans) == "no") return()
}
var.vals <- sort(var.vals, na.last=TRUE)
if (var.fmt == "") {
var.vals.txt <- format(var.vals)
} else if ("POSIXt" %in% var.class) {
var.vals.txt <- format(var.vals, format=var.fmt)
} else {
var.vals.txt <- try(sprintf(var.fmt, var.vals), silent=TRUE)
if (inherits(var.vals.txt, "try-error")) var.vals.txt <- format(var.vals)
}
var.vals.txt <- gsub("^\\s+|\\s+$", "", var.vals.txt)
tclvalue(value.var) <- ""
for (i in seq_along(var.vals.txt)) tcl("lappend", value.var, var.vals.txt[i])
tkselection.clear(f1.lst.4.1, 0, "end")
tkconfigure(f1.but.5.1, state="disabled")
tkfocus(f2.txt.2.1)
}
# change variable selection
ChangeVar <- function() {
tclvalue(value.var) <- ""
idx <- as.integer(tkcurselection(f1.lst.2.1))
if (length(idx) == 0) return()
tkconfigure(f1.but.5.1, state="normal")
}
# insert value into text box
InsertValue <- function() {
idx <- as.integer(tkcurselection(f1.lst.2.1))
if (length(idx) == 0) return()
id <- as.character(tkget(f1.lst.2.1, idx, idx))
idx <- which(vapply(cols, function(i) i$id, "") == id)
var.fmt <- cols[[idx]]$format
var.class <- cols[[idx]]$class
idx <- as.integer(tkcurselection(f1.lst.4.1))
if (length(idx) == 0) return()
val <- as.character(tkget(f1.lst.4.1, idx, idx))
if ("factor" %in% var.class && is.na(suppressWarnings(as.numeric(val))))
var.class <- "character"
if ("POSIXt" %in% var.class) {
if (var.fmt == "") var.fmt <- "%Y-%m-%d %H:%M:%S"
txt <- paste0("as.POSIXct(\"", val, "\", format = \"", var.fmt, "\", tz = \"GMT\")")
} else if ("Date" %in% var.class) {
if (var.fmt == "") var.fmt <- "%Y-%m-%d"
txt <- paste0("as.Date(\"", val, "\", format = \"", var.fmt, "\")")
} else if ("integer" %in% var.class && !val %in% c("NA", "NaN", "Inf", "-Inf")) {
txt <- paste0(val, "L")
} else if ("character" %in% var.class && !val %in% c("NA", "NaN", "Inf", "-Inf")) {
txt <- paste0("\"", val, "\"")
} else {
txt <- val
}
InsertString(txt)
}
# insert trigonometric function
InsertTrigFunction <- function(fun) {
is.inverse <- as.logical(as.integer(tclvalue(inverse.var)))
is.hyperbolic <- as.logical(as.integer(tclvalue(hyperbolic.var)))
angles <- as.character(tclvalue(angles.var))
if (is.inverse) fun <- paste0("a", fun)
if (is.hyperbolic) fun <- paste0(fun, "h")
if (angles == "deg")
InsertString(paste0(fun, "(<variable> * pi / 180)"))
else
InsertString(paste0(fun, "(<variable>)"))
}
old.fun <- if (is.null(index)) fun else cols[[as.integer(index)]]$fun
rtn <- NULL
ids <- vapply(cols, function(i) i$id, "")
# remove variable being defined
if (!is.null(index)) {
edit.fun.id <- ids[index]
ids <- ids[-index]
}
# class types
classes <- NULL
for (i in seq_along(cols)) {
if (!i %in% index) classes <- c(classes, cols[[i]]$class)
}
classes <- suppressWarnings(sort(unique(classes)))
# required vector length
if (!is.null(value.length)) value.length <- as.integer(value.length)
# assign variables linked to Tk widgets
variable.var <- tclVar()
for (i in seq_along(ids)) tcl("lappend", variable.var, ids[i]) # must be unique
value.var <- tclVar()
inverse.var <- tclVar(0)
hyperbolic.var <- tclVar(0)
angles.var <- tclVar("rad")
tt.done.var <- tclVar(0)
# open gui
tclServiceMode(FALSE)
tt <- tktoplevel()
if (!is.null(parent)) {
tkwm.transient(tt, parent)
geo <- unlist(strsplit(as.character(tkwm.geometry(parent)), "\\+"))
geo <- as.integer(geo[2:3]) + 25
tkwm.geometry(tt, sprintf("+%s+%s", geo[1], geo[2]))
}
tktitle(tt) <- win.title
# top menu
top.menu <- tkmenu(tt, tearoff=0)
# project menu
menu.edit <- tkmenu(tt, tearoff=0)
tkadd(top.menu, "cascade", label="Edit", menu=menu.edit, underline=0)
tkadd(menu.edit, "command", label="Undo", accelerator="Ctrl+Z", command=EditUndo)
tkadd(menu.edit, "command", label="Redo", accelerator="Ctrl+Y", command=EditRedo)
tkadd(menu.edit, "separator")
tkadd(menu.edit, "command", label="Cut", accelerator="Ctrl+X", command=EditCut)
tkadd(menu.edit, "command", label="Copy", accelerator="Ctrl+C", command=EditCopy)
tkadd(menu.edit, "command", label="Paste", accelerator="Ctrl+V", command=EditPaste)
tkadd(menu.edit, "separator")
tkadd(menu.edit, "command", label="Select all", accelerator="Ctrl+A", command=EditSelectAll)
tkadd(menu.edit, "command", label="Clear console", accelerator="Ctrl+L", command=ClearConsole)
menu.convert <- tkmenu(tt, tearoff=0)
tkadd(top.menu, "cascade", label="Convert", menu=menu.convert, underline=0)
menu.convert.char <- tkmenu(tt, tearoff=0)
tkadd(menu.convert.char, "command", label="Factor",
command=function() InsertString("as.factor(<variable>)"))
tkadd(menu.convert.char, "command", label="Numeric",
command=function() InsertString("as.numeric(<variable>)"))
tkadd(menu.convert.char, "command", label="Integer",
command=function() InsertString("as.integer(<variable>)"))
tkadd(menu.convert.char, "command", label="Logical",
command=function() InsertString("as.logical(<variable>)"))
tkadd(menu.convert.char, "command", label="POSIXct",
command=function() InsertString("as.POSIXct(strptime(<variable>, format = \"<format>\", tz = \"GMT\"))"))
tkadd(menu.convert.char, "command", label="Date",
command=function() InsertString("as.Date(<variable>, format = \"<format>\")"))
tkadd(menu.convert, "cascade", label="Character to", menu=menu.convert.char)
menu.convert.factor <- tkmenu(tt, tearoff=0)
tkadd(menu.convert.factor, "command", label="Character",
command=function() InsertString("as.character(<variable>)"))
tkadd(menu.convert.factor, "command", label="Integer",
command=function() InsertString("as.integer(<variable>)"))
tkadd(menu.convert, "cascade", label="Factor to", menu=menu.convert.factor)
menu.convert.num <- tkmenu(tt, tearoff=0)
tkadd(menu.convert.num, "command", label="POSIXct",
command=function() InsertString("as.POSIXct(<variable>, origin = \"1970-01-01 00:00:00.00\", tz = \"GMT\")"))
tkadd(menu.convert, "cascade", label="Numeric to", menu=menu.convert.num)
menu.convert.int <- tkmenu(tt, tearoff=0)
tkadd(menu.convert.int, "command", label="POSIXct",
command=function() InsertString("as.POSIXct(<variable>, origin = \"1970-01-01 00:00:00\", tz = \"GMT\")"))
tkadd(menu.convert.int, "command", label="Date",
command=function() InsertString("as.Date(<variable>, origin = \"1899-12-30\")"))
tkadd(menu.convert, "cascade", label="Integer to", menu=menu.convert.int)
menu.convert.log <- tkmenu(tt, tearoff=0)
tkadd(menu.convert.log, "command", label="Integer",
command=function() InsertString("as.integer(<variable>)"))
tkadd(menu.convert, "cascade", label="Logical to", menu=menu.convert.log)
menu.convert.posix <- tkmenu(tt, tearoff=0)
tkadd(menu.convert.posix, "command", label="Numeric",
command=function() InsertString("as.numeric(<variable>)"))
tkadd(menu.convert.posix, "command", label="Date",
command=function() InsertString("as.Date(<variable>, tz = \"UTC\")"))
tkadd(menu.convert, "cascade", label="POSIXct to", menu=menu.convert.posix)
menu.convert.date <- tkmenu(tt, tearoff=0)
tkadd(menu.convert.date, "command", label="Integer",
command=function() InsertString("as.integer(<variable>)"))
tkadd(menu.convert.date, "command", label="POSIXct",
command=function() InsertString("as.POSIXct(<variable>, tz = \"GMT\")"))
tkadd(menu.convert, "cascade", label="Date to", menu=menu.convert.date)
menu.math <- tkmenu(tt, tearoff=0)
tkadd(top.menu, "cascade", label="Math", menu=menu.math, underline=0)
tkadd(menu.math, "command", label="Square root",
command=function() InsertString("sqrt(<variable>)"))
tkadd(menu.math, "command", label="Absolute value",
command=function() InsertString("abs(<variable>)"))
tkadd(menu.math, "separator")
tkadd(menu.math, "command", label="Floor",
command=function() InsertString("floor(<variable>)"))
tkadd(menu.math, "command", label="Ceiling",
command=function() InsertString("ceiling(<variable>)"))
tkadd(menu.math, "command", label="Truncation",
command=function() InsertString("trunc(<variable>)"))
menu.math.round <- tkmenu(tt, tearoff=0)
tkadd(menu.math.round, "command", label="Decimal places",
command=function() InsertString("round(<variable>, digits = 0)"))
tkadd(menu.math.round, "command", label="Significant digits",
command=function() InsertString("signif(<variable>, digits = 6)"))
tkadd(menu.math, "cascade", label="Round to", menu=menu.math.round)
tkadd(menu.math, "separator")
tkadd(menu.math, "command", label="Exponential",
command=function() InsertString("exp(<variable>)"))
menu.math.log <- tkmenu(tt, tearoff=0)
tkadd(menu.math.log, "command", label="Common (base 10)",
command=function() InsertString("log10(<variable>)"))
tkadd(menu.math.log, "command", label="Natural (base \u0065)",
command=function() InsertString("log(<variable>, base = exp(1))"))
tkadd(menu.math.log, "command", label="Binary (base 2)",
command=function() InsertString("log2(<variable>)"))
tkadd(menu.math, "cascade", label="Logarithm", menu=menu.math.log)
tkadd(menu.math, "separator")
tkadd(menu.math, "command", label="Sine",
command=function() InsertTrigFunction("sin"))
tkadd(menu.math, "command", label="Cosine",
command=function() InsertTrigFunction("cos"))
tkadd(menu.math, "command", label="Tangent",
command=function() InsertTrigFunction("tan"))
tkadd(menu.math, "separator")
menu.math.cum <- tkmenu(tt, tearoff=0)
tkadd(menu.math.cum, "command", label="Sum",
command=function() InsertString("cumsum(<variable>)"))
tkadd(menu.math.cum, "command", label="Product",
command=function() InsertString("cumprod(<variable>)"))
tkadd(menu.math, "cascade", label="Cumulative", menu=menu.math.cum)
tkadd(menu.math, "separator")
tkadd(menu.math, "command", label="Sum",
command=function() InsertString("sum(<variable>, na.rm = TRUE)"))
tkadd(menu.math, "command", label="Product",
command=function() InsertString("prod(<variable>, na.rm = TRUE)"))
menu.stats <- tkmenu(tt, tearoff=0)
tkadd(top.menu, "cascade", label="Stats", menu=menu.stats, underline=0)
tkadd(menu.stats, "command", label="Minimum",
command=function() InsertString("min(<variable>, na.rm = TRUE)"))
tkadd(menu.stats, "command", label="Maximum",
command=function() InsertString("max(<variable>, na.rm = TRUE)"))
tkadd(menu.stats, "separator")
tkadd(menu.stats, "command", label="Mean",
command=function() InsertString("mean(<variable>, na.rm = TRUE)"))
tkadd(menu.stats, "command", label="Median",
command=function() InsertString("median(<variable>, na.rm = TRUE)"))
tkadd(menu.stats, "command", label="Standard deviation",
command=function() InsertString("sd(<variable>, na.rm = TRUE)"))
tkadd(menu.stats, "separator")
tkadd(menu.stats, "command", label="Set seed",
command=function() InsertString("set.seed(124)"))
menu.stats.ran <- tkmenu(tt, tearoff=0)
nobs <- ifelse(is.null(value.length), "<integer>", value.length)
tkadd(menu.stats.ran, "command", label="Normal distribution",
command=function() InsertString(paste0("rnorm(n = ", nobs, ", mean = 0, sd = 1)")))
tkadd(menu.stats.ran, "command", label="Uniform distribution",
command=function() InsertString(paste0("runif(n = ", nobs, ", min = 0, max = 1)")))
tkadd(menu.stats, "cascade", label="Random samples from a",
menu=menu.stats.ran)
menu.operator <- tkmenu(tt, tearoff=0)
tkadd(top.menu, "cascade", label="Operator", menu=menu.operator, underline=0)
tkadd(menu.operator, "command", label="And",
command=function() InsertString(" & "))
tkadd(menu.operator, "command", label="Or",
command=function() InsertString(" | "))
tkadd(menu.operator, "command", label="Not",
command=function() InsertString("!"))
tkadd(menu.operator, "separator")
tkadd(menu.operator, "command", label="In",
command=function() InsertString(" %in% "))
tkadd(menu.operator, "command", label="Match",
command=function() {
InsertString("match(<variable>, <values>, nomatch = NA)")
})
tkadd(menu.operator, "separator")
tkadd(menu.operator, "command", label="Exponentiation",
command=function() InsertString("<variable>^<power>"))
menu.const <- tkmenu(tt, tearoff=0)
tkadd(top.menu, "cascade", label="Constant", menu=menu.const, underline=0)
tkadd(menu.const, "command", label="\u03C0",
command=function() InsertString("pi"))
tkadd(menu.const, "command", label="\u0065",
command=function() InsertString("exp(1)"))
tkadd(menu.const, "separator")
tkadd(menu.const, "command", label="True",
command=function() InsertString("TRUE"))
tkadd(menu.const, "command", label="False",
command=function() InsertString("FALSE"))
tkadd(menu.const, "separator")
tkadd(menu.const, "command", label="Not available",
command=function() InsertString("NA"))
tkadd(menu.const, "command", label="Not a number",
command=function() InsertString("NaN"))
tkadd(menu.const, "separator")
tkadd(menu.const, "command", label="Positive infinity",
command=function() InsertString("Inf"))
tkadd(menu.const, "command", label="Negative infinity",
command=function() InsertString("-Inf"))
tkadd(menu.const, "separator")
menu.const.is <- tkmenu(tt, tearoff=0)
tkadd(menu.const.is, "command", label="Not available",
command=function() InsertString("is.na(<variable>)"))
tkadd(menu.const.is, "command", label="Not a number",
command=function() InsertString("is.nan(<variable>)"))
tkadd(menu.const.is, "separator")
tkadd(menu.const.is, "command", label="Finite (not infinite and not missing)",
command=function() InsertString("is.finite(<variable>)"))
tkadd(menu.const.is, "command", label="Infinite",
command=function() InsertString("is.infinite(<variable>)"))
tkadd(menu.const, "cascade", label="Which elements are ", menu=menu.const.is)
tkadd(menu.const, "separator")
tkadd(menu.const, "command", label="Are all values true",
command=function() InsertString("all(<variable>, na.rm = FALSE)"))
tkadd(menu.const, "command", label="Are any values true",
command=function() InsertString("any(<variable>, na.rm = FALSE)"))
menu.string <- tkmenu(tt, tearoff=0)
tkadd(top.menu, "cascade", label="String", menu=menu.string, underline=0)
tkadd(menu.string, "command", label="Concatenate",
command=function() {
InsertString("paste(<variable>, <variable>, sep = \" \")")
})
tkadd(menu.string, "command", label="Extract substring",
command=function() {
InsertString(paste("substr(<variable>, start = 1, stop = 2)"))
})
tkadd(menu.string, "command", label="Number of characters",
command=function() {
InsertString("nchar(<variable>)")
})
tkadd(menu.string, "command", label="Which elements are non-empty strings",
command=function() {
InsertString("nzchar(<variable>)")
})
menu.tools <- tkmenu(tt, tearoff=0)
tkadd(top.menu, "cascade", label="Tools", menu=menu.tools, underline=0)
menu.tools.time <- tkmenu(tt, tearoff=0)
tkadd(menu.tools.time, "command", label="POSIXct\u2026",
command=function() CallFormatDateTime(Sys.time()))
tkadd(menu.tools.time, "command", label="Date\u2026",
command=function() CallFormatDateTime(Sys.Date()))
tkadd(menu.tools, "cascade", label="Build format for", menu=menu.tools.time)
# finalize top menu
tkconfigure(tt, menu=top.menu)
# frame 0, ok and cancel buttons, and size grip
f0 <- tkframe(tt, relief="flat")
f0a <- tkframe(f0, relief="flat")
f0a.chk.1 <- ttkcheckbutton(f0a, text="Inverse", variable=inverse.var)
f0a.chk.2 <- ttkcheckbutton(f0a, text="Hyperbolic", variable=hyperbolic.var)
f0a.rad.3 <- ttkradiobutton(f0a, variable=angles.var, value="rad", text="Radians")
f0a.rad.4 <- ttkradiobutton(f0a, variable=angles.var, value="deg", text="Degrees")
tkgrid(f0a.chk.1, f0a.chk.2, f0a.rad.3, f0a.rad.4)
tkgrid.configure(f0a.chk.2, padx=c(2, 12))
tkgrid.configure(f0a.rad.3, padx=c(0, 2))
f0.but.3 <- ttkbutton(f0, width=12, text="OK", command=SaveFunction)
f0.but.4 <- ttkbutton(f0, width=12, text="Cancel",
command=function() tclvalue(tt.done.var) <- 1)
f0.but.5 <- ttkbutton(f0, width=12, text="Help",
command=function() {
print(utils::help("EditFunction", package="RSurvey"))
})
f0.grp.6 <- ttksizegrip(f0)
tkgrid(f0a, "x", f0.but.3, f0.but.4, f0.but.5, f0.grp.6)
tkgrid.columnconfigure(f0, 1, weight=1)
tkgrid.configure(f0a, padx=10, pady=c(0, 10), sticky="sw")
tkgrid.configure(f0.but.3, f0.but.4, f0.but.5, padx=c(0, 4), pady=c(15, 10))
tkgrid.configure(f0.but.5, columnspan=2, padx=c(0, 10))
tkgrid.configure(f0.grp.6, sticky="se")
tkraise(f0.but.5, f0.grp.6)
tkpack(f0, fill="x", side="bottom", anchor="e")
# paned window
pw <- ttkpanedwindow(tt, orient="horizontal")
# frame 1
f1 <- tkframe(pw, relief="flat")
txt <- "Double click to insert variable"
f1.lab.1.1 <- ttklabel(f1, text=txt, foreground="#141414")
f1.lst.2.1 <- tklistbox(f1, selectmode="browse", activestyle="none",
relief="flat", borderwidth=5, width=25, height=8,
exportselection=FALSE, listvariable=variable.var,
highlightthickness=0)
f1.ysc.2.2 <- ttkscrollbar(f1, orient="vertical")
box.vals <- "{Show all classes}"
if (length(classes) > 0) box.vals <- c("Show all classes", classes)
f1.box.3.1 <- ttkcombobox(f1, state="readonly", value=box.vals)
tkconfigure(f1.lst.2.1, background="white",
yscrollcommand=paste(.Tk.ID(f1.ysc.2.2), "set"))
tkconfigure(f1.ysc.2.2, command=paste(.Tk.ID(f1.lst.2.1), "yview"))
tcl(f1.box.3.1, "current", 0)
f1.lst.4.1 <- tklistbox(f1, selectmode="browse", activestyle="none",
relief="flat", borderwidth=5, width=25, height=5,
exportselection=FALSE, listvariable=value.var,
highlightthickness=0)
f1.ysc.4.2 <- ttkscrollbar(f1, orient="vertical")
f1.but.5.1 <- ttkbutton(f1, width=15, text="Unique Values", command=ShowUniqueValues)
tkconfigure(f1.lst.4.1, background="white",
yscrollcommand=paste(.Tk.ID(f1.ysc.4.2), "set"))
tkconfigure(f1.ysc.4.2, command=paste(.Tk.ID(f1.lst.4.1), "yview"))
tkconfigure(f1.but.5.1, state="disabled")
tkgrid(f1.lab.1.1, "x")
tkgrid(f1.lst.2.1, f1.ysc.2.2)
tkgrid(f1.box.3.1, "x")
tkgrid(f1.lst.4.1, f1.ysc.4.2)
tkgrid(f1.but.5.1, "x")
tkgrid.configure(f1.lab.1.1, padx=c(10, 0), pady=c(10, 0), sticky="w")
tkgrid.configure(f1.lst.2.1, padx=c(10, 0), pady=c(2, 0), sticky="nsew")
tkgrid.configure(f1.ysc.2.2, padx=c(0, 0), pady=c(2, 0), sticky="ns")
tkgrid.configure(f1.box.3.1, padx=c(10, 0), pady=c(4, 0), sticky="we")
tkgrid.configure(f1.lst.4.1, padx=c(10, 0), pady=c(15, 0), sticky="nsew")
tkgrid.configure(f1.ysc.4.2, padx=c(0, 0), pady=c(15, 0), sticky="ns")
tkgrid.configure(f1.but.5.1, padx=c(10, 0), pady=c(4, 0))
tkgrid.rowconfigure(f1, 1, weight=1)
tkgrid.rowconfigure(f1, 3, weight=1)
tkgrid.columnconfigure(f1, 0, weight=1, minsize=20)
# frame 2
f2 <- tkframe(pw, relief="flat")
txt <- "Define function"
if (!is.null(index) && edit.fun.id != "") txt <- paste0(txt, " for \"", edit.fun.id, "\"")
f2.lab.1.1 <- ttklabel(f2, text=txt, foreground="#141414")
if (is.null(value.length)) {
txt <- ""
} else {
txt <- paste("(resulting vector must be of length", format(value.length, big.mark=","))
if (!is.null(value.class)) txt <- paste(txt, "and class", value.class)
txt <- paste0(txt, ")")
}
f2.lab.1.2 <- ttklabel(f2, text=txt, foreground="#A40802")
f2.txt.2.1 <- tktext(f2, bg="white", font="TkFixedFont",
padx=2, pady=2, width=80, height=12, undo=1,
autoseparators=1, wrap="none", foreground="black", relief="flat",
yscrollcommand=function(...) tkset(f2.ysc.2.2, ...))
f2.ysc.2.2 <- ttkscrollbar(f2, orient="vertical")
tkconfigure(f2.ysc.2.2, command=paste(.Tk.ID(f2.txt.2.1), "yview"))
f2a <- tkframe(f2, relief="flat")
f2a.but.01 <- ttkbutton(f2a, width=3, text="\u002b",
command=function() InsertString(" + "))
f2a.but.02 <- ttkbutton(f2a, width=3, text="\u2212",
command=function() InsertString(" - "))
f2a.but.03 <- ttkbutton(f2a, width=3, text="\u00d7",
command=function() InsertString(" * "))
f2a.but.04 <- ttkbutton(f2a, width=3, text="\u00f7",
command=function() InsertString(" / "))
f2a.but.05 <- ttkbutton(f2a, width=3, text=">",
command=function() InsertString(" > "))
f2a.but.06 <- ttkbutton(f2a, width=3, text="<",
command=function() InsertString(" < "))
f2a.but.07 <- ttkbutton(f2a, width=3, text="\u2265",
command=function() InsertString(" >= "))
f2a.but.08 <- ttkbutton(f2a, width=3, text="\u2264",
command=function() InsertString(" <= "))
f2a.but.09 <- ttkbutton(f2a, width=3, text="=",
command=function() InsertString(" == "))
f2a.but.10 <- ttkbutton(f2a, width=3, text="\u2260",
command=function() InsertString(" != "))
f2a.but.11 <- ttkbutton(f2a, width=3, text="in",
command=function() InsertString(" %in% "))
f2a.but.12 <- ttkbutton(f2a, width=3, text="( )",
command=function() InsertString("()"))
f2a.but.13 <- ttkbutton(f2a, width=3, text="[ ]",
command=function() InsertString("[]"))
f2a.but.14 <- ttkbutton(f2a, width=3, text="\u25C0\u2212",
command=function() InsertString(" <- "))
f2a.but.15 <- ttkbutton(f2a, width=3, text="\u0078\u207F",
command=function() InsertString("^"))
tkgrid(f2a.but.01, f2a.but.02, f2a.but.03, f2a.but.04,
f2a.but.05, f2a.but.06, f2a.but.07, f2a.but.08,
f2a.but.09, f2a.but.10, f2a.but.11, f2a.but.12,
f2a.but.13, f2a.but.14, f2a.but.15, pady=c(4, 0))
tkgrid.configure(f2a.but.01, f2a.but.02, f2a.but.03,
f2a.but.04, f2a.but.06, f2a.but.07,
f2a.but.08, f2a.but.09, f2a.but.10,
f2a.but.11, f2a.but.13, f2a.but.14,
f2a.but.15, padx=c(2, 0))
tkgrid.configure(f2a.but.05, f2a.but.12, padx=c(12, 0))
tkgrid(f2.lab.1.1, f2.lab.1.2, "x")
tkgrid(f2.txt.2.1, "x", f2.ysc.2.2)
tkgrid(f2a, "x", "x")
tkgrid.configure(f2.lab.1.1, f2.lab.1.2, padx=c(2, 0), pady=c(10, 0), sticky="w")
tkgrid.configure(f2.txt.2.1, padx=c(2, 0), pady=c(2, 0), columnspan=2, sticky="nsew")
tkgrid.configure(f2.ysc.2.2, padx=c(0, 10), pady=c(2, 0), columnspan=2, sticky="ns")
tkgrid.configure(f2a, columnspan=2, sticky="we")
tkgrid.rowconfigure(f2, 1, weight=1)
tkgrid.columnconfigure(f2, 1, weight=1, minsize=20)
if (!is.null(old.fun) && old.fun != "") tkinsert(f2.txt.2.1, "end", old.fun)
tcl(f2.txt.2.1, "edit", "reset")
tkmark.set(f2.txt.2.1, "insert", "end")
# pack frames into paned window
tkadd(pw, f1, weight=0)
tkadd(pw, f2, weight=1)
tkpack(pw, fill="both", expand="yes")
# bind events
tclServiceMode(TRUE)
tkbind(tt, "<Destroy>", function() tclvalue(tt.done.var) <- 1)
tkbind(f1.lst.2.1, "<<ListboxSelect>>", ChangeVar)
tkbind(f1.lst.2.1, "<Double-ButtonRelease-1>", InsertVar)
tkbind(f1.lst.4.1, "<Double-ButtonRelease-1>", InsertValue)
tkbind(f1.box.3.1, "<<ComboboxSelected>>", RebuildList)
tkbind("Text", "<Control-KeyPress-z>", EditUndo)
tkbind("Text", "<Control-KeyPress-y>", EditRedo)
tkbind("Text", "<Control-KeyPress-v>", EditPaste)
tkbind("Text", "<Control-KeyPress-a>", EditSelectAll)
tkbind("Text", "<Control-KeyPress-l>", ClearConsole)
# gui control
tkfocus(tt)
tkgrab(tt)
tkwait.variable(tt.done.var)
tclServiceMode(FALSE)
tkgrab.release(tt)
tkdestroy(tt)
tclServiceMode(TRUE)
return(rtn)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.