How Do I Stop An R gWidgets Script Exiting - r

I am using the gWidgets toolkit to create a GUI in an R script that is run using Rscript.
When the GUI is created, the script exits.
I can prevent this with a while(TRUE){Sys.sleep(9999)} loop at the end of the script but that seems hacky.
Is there a better way of telling R to exit only when the GUI is closed, or at least to enter the REPL once the GUI is constructed?

You might be able to adapt gbasicdialog for your needs. This constructor creates a modal container from which you can spawn other windows. Here is an example:
library(gWidgets)
options(guiToolkit="RGtk2")
require(fortunes) # just for fun
hold_it <- gbasicdialog(do.buttons=FALSE)
b <- gbutton("click me for a message", cont=hold_it, handler=function(h,...) {
gmessage(paste(fortune(), collapse="\n"), parent=hold_it)
})
visible(hold_it, TRUE)
The same works for the "tcltk" toolkit. It uses pretty much what Greg suggests can be done.

This subject may be closed but as a newbie to gwidgets, I have been confronted with. The solution given by jverzani is obviously a solution. I have chosen another one, not using any supplementary dialog, just because I don't want one, no other reason at all...
In the handler of the gwindow, after disposal I remove the variable from the environment:
handler = function(h,...) {dispose(EDFAnalysis$w); rm(w,envir=EDFAnalysis)}
where EDFAnalysis is the environment of my script... and w is the main gwindow.
Then, at the end of my script I added:
while(exists("w",EDFAnalysis)){Sys.sleep(5)}
of course, smaller value than 5 or greater value can be used. In my case, 5 s is sufficient and not for ever... :-)

The standard way of dealing with this is to request user input to continue. This one-liner will do the trick.
EDIT: readline only works under interactive use, so I've swapped it for scan, which is a little less pretty.
pause_for_input <- function()
{
message("Press ENTER to continue")
invisible(scan(n = 0, quiet = TRUE))
}
So you script should look like
#Create you GUI
#Whatever else
pause_for_input()

If you are using the tcltk package instead of gWidgets then you could possibly use the tkwait.window function from tcltk to tell the script to wait until the gui window goes away before continuing the script.

A good way to do it, I've found, is to use the gtkMain() function in the RGtk2 library. This simply keeps the main loop running until gtkMainQuit() is called.

For completeness: ozjimbob already gave the answer for a most "clean" way how to do it.
The answer of ffeschet did not work with me, neither on Unix nor on Windows.
Hence, in the main "launching" script, you have to at least have these entries:
options("guiToolkit"="RGtk2")
library(RGtk2)
library(gWidgets)
library(gWidgetsRGtk2)
StartMyGUI()
gtkMain()
In the "child" process "StartMyGUI()", your code could e.g. look like this:
StartMyGUI <- function(handler=function(h,...) {
dispose(h$obj)
}) {
window <- gwindow("Hello")
group <- ggroup(container = window)
glabel("Hello World!", container=group, expand=TRUE)
# A group to organize the buttons
button.group <- ggroup(container = group)
# Push buttons to right
addSpring(button.group)
gbutton("OK", handler=handler, container=button.group)
gbutton("Cancel", handler = function(h,...) {
dispose(window)
gtkMainQuit()
},
container=button.group)
return()
}
It is only when the user hits the "Cancel" button that gtkMainQuit() will be called, which exits the mother process in the main "launching" script.

Related

Custom autocomplete functions in R possible?

I am looking to create a custom function in R that will allow the user to call the function and then it will produce an auto complete pipeline ready for them to edit, this way they can jump into quickly customizing the standard pipeline instead of copy pasting from an old script or retyping. How can I go about setting up this sort of autocomplete:
#pseudo code what I type---
seq.Date(1,2,by = "days") %>%
pblapply(function(x){
read.fst(list.files(as.character(x), as.data.table = T) %>%
group_by(x) %>%
count()
}) %>% rbindlist()
#how can I write a function so that when I call that function, it ouputs an autocomplete
#of the above so that I can go ahead and start just customizing the code? Something like this
my_autocomplete_function = function(x) {
print(
"
seq.Date(as.Date(Sys.Date()),as.Date(Sys.Date()+1),by = 'days') %>%
pbapply::pblapply(function(x){
fst::read.fst(list.files(as.character(x), as.data.table = T)) %>%
#begin filtering and grouping by below custom
group_by()
}) %>% rbindlist()
")
}
#I can just print the function and copy paste the text from the output in my console to my script
my_autocomplete_function()
#but I rather it just autocomplete and appear in the script if possible?
Putting text into the command line will probably be a function of the interface you are using to run R - is it plain R, Rstudio, etc?
One possibility might be to use the clipr package and put the code into the clipboard, then prompt the user to hit their "paste" button to get it on the command line. For example this function which creates a little code string:
> writecode = function(x){
code = paste0("print(",x,")")
clipr::write_clip(code)
message("Code ready to paste")}
Use it like this:
> writecode("foo")
Code ready to paste
Then when I hit Ctrl-V to paste, I see this:
> print(foo)
I can then edit that line. Any string will do:
> writecode("bar")
Code ready to paste
[ctrl-V]
> print(bar)
Its one extra key for your user to press, but having a chunk of code appear on the command line with no prompting might be quite surprising for a user.
I spend my day reading the source code for utils auto completion. The linebuffer only contains the code for ... one line, so that can't do fully what your looking for here, but it is quite customizable. Maybe the rstudio source code for autocompletion has more answers. Here is an example to write and activate your own custom auto completer. It is not well suited for packages as any new pacakge could overwrite the settings.
Below a
#load this function into custom.completer setting to activate
rc.options("custom.completer" = function(x) {
#perhaps debug it, it will kill your rsession sometimes
#browser()
###default completion###
##activating custom deactivates anything else
#however you can run utils auto completer also like this
#rstudio auto completation is not entirely the same as utils
f = rc.getOption("custom.completer")
rc.options("custom.completer" = NULL)
#function running base auto complete.
#It will dump suggestion into mutable .CompletionEnv$comps
utils:::.completeToken() #inspect this function to learn more about completion
rc.options("custom.completer" = f)
###your custom part###
##pull this environment holding all input/output needed
.CompletionEnv = utils:::.CompletionEnv
#perhaps read the 'token'. Also 'linebuffer', 'start' & 'end' are also useful
token = .CompletionEnv$token
#generate a new completion or multiple...
your_comps = paste0(token,c("$with_tomato_sauce","$with_apple_sauce"))
#append your suggestions to the vanilla suggestions/completions
.CompletionEnv$comps = c(your_comps,.CompletionEnv$comps)
print(.CompletionEnv$comps)
#no return used
NULL
})
xxx<tab>
NB! currently rstudio IDE will backtick quote any suggestion which is not generated by them. I want to raise an issue on that someday.
Bonus info: A .DollarNames.mys3class-method can be very useful and works with both rstudio and utils out of the box e.g.
#' #export
.DollarNames.mys3class = function(x, pattern = "") {
#x is the .CompletionEnv
c("apple","tomato")
}

Create stop button with gwidgets2 to interrupt running function on Ubuntu

I am making a GUI for a R program which does some quite lengthy calculations while constantly updating plots on a gwidgets GUI. I would like to give the user the possibility to interrupt the calculations if they take too long. I am trying to use a stop button.
The problem is that while the function is running, the window is greyed out and none of the controls will respond, so it is not possible to click the stop button.
This problem only occurs when running the program on Ubuntu, I tested the script on Windows and it worked as expected.
Here's a minimal working example of my code illustrating what I am trying to do:
require(gWidgets2RGtk2)
require(gWidgets2)
stopsignal <- FALSE
w <- gwindow()
button_gp <- ggroup(cont=w)
gbutton("calculate", cont=button_gp, handler=function(...) calculate())
gbutton("stop", cont=button_gp, handler=
function(...) assign("stopsignal", TRUE , envir = .GlobalEnv))
calculate <- function() {
while(TRUE) {
#do something
print("Hello World")
if(stopsignal) break
}
}
I am trying to understand why Ubuntu is behaving differently and how I can fix it.

Loading and saving variables in R with gWidgets

For the past few months I've been building a simulation in R I hope to package. It consists of two usable functions, and many internal ones which one of the two usable functions call while looping, to perform the stages of simulation.
A simple conceptual example is:
# Abstract representation 1st Usable function, generates object containing settings for simulation.
settings <- function(){
matrix(c(1:4),nrow=2,ncol=2)
}
# Abstract representation of one of many internal functions, does some action in simulation.
int.func <- function(x){
out <- x*2
return(out)
}
# Abstract representation of second usable function, takes settings & invokes internal functions generates results & saves as R object files.
sim.func <- function(x){
ans <- int.func(x)
ans2 <- ans-2
save(ans2, file="outtest")
}
With my package so far, using it is done like so after loading and attaching the package with library():
INPUT <- settings()
fix(settings) # If you want to change from the defaults.
sim.func(INPUT)
Nothing needs returning from the simulation function because results and data-dumps get saved as an object with save() commands and the objects can be read in afterwards.
Now I'd like it to have a simple GUI which can be used in conjunction with command line - think Rcmdr but much more simple, to allow my co-workes who have never touched R to use it.
The gui needs to be able to edit the settings - like with the fix command above, to save a settings object to file, and to read in setting from an object file. I've built this with gWidgets:
gui <- function(){
INPUT <- matrix(c(1:4),nrow=2,ncol=2)
mainwin <- gwindow("MainWindow")
button1 <- gbutton("Edit Settings", cont=mainwin, handler=
function(h,...){
fix(INPUT)
print("Settings Edited")
})
button2 <- gbutton("RUN", cont=mainwin, handler=
function(h,...){
sim.func(INPUT)
print("The run is done")
})
savebutton <- gbutton("Write Settings to File",cont=mainwin, handler=
function(h,...){
setfilename <- ginput("Please enter the filename")
save(INPUT, file=setfilename)
})
loadutton <- gbutton("Load Settings from File", cont=mainwin,
handler=function(h,...){
fname <- gfile(test="Choose a file",
type="open",
action="print",
handler =
function(h,...){
do.call(h$action, list(h$file))
}
)
load(fname)})
}
Note the job of the settings function from before is now done on the first line of this gui function.
I add this to the same R file as the three functions above, add gui to the namespace as an export, set gWidgets and gWidgetstcltk as imports, and rebuild, then I library() the package and do gui().
The interface shows up. However I have a few issues:
The gui shows up fine, but if I click button1 ("Edit Settings") to edit settings through fix(INPUT), change the values, close the editor and click the button again to see if the changes have persisted and been stored in INPUT, they have not.
Same goes for reading in an object, it does not overwrite the INPUT object generated by default in the first line of function gui().
I think this has something to do with environments of functions but I'm not too sure. In the gui-less version of my package, the user generates the object containing settings, which is in workspace and feeds it to the simulation function as an argument. However since with the gui version, everything is run inside the function gui() and gWidgets handlers makes use of functions(h,...) I can't help but feel as if environments are the issue here. It's odd that when clicking on button 1, it will find INPUT from the gui() environment, but won't make the changes back there.
Can anybody help out with this and suggest what it is I need to do?
Apologies for a long question, but I've tried to explain clearly. Code is reproducible, as is the issue, just by having library(gWidgets, gWidgetstcltk)
and copying and pasting the code I've provided here, to define the functions and then running gui(). Then click the "Edit Settings" button, change the cells, exit, then click the button again to see if the changes persisted (they don't). The abstract example I've provided faithfully reproduces the same issues I have with my proper simulation functions so if I can't get it working, I won't get the real thing working.
Thanks,
Ben W.
UEA
The Sainsbury Laboratory.
[EDIT] Here is a fix/workaround using .GlobalEnv:
gui <- function(){
INPUT <- matrix(c(1:4),nrow=2,ncol=2)
.GlobalEnv$INPUT <- INPUT
mainwin <- gwindow("MainWindow")
button1 <- gbutton("Set", cont=mainwin, handler=
function(h,...){
INPUT <- .GlobalEnv$INPUT
fix(INPUT)
print("Settings have been edited...")
})
button2 <- gbutton("RUN", cont=mainwin, handler=
function(h,...){
sim.func(.GlobalEnv$INPUT)
print("The run is done")
})
writebutton <- gbutton("Write Settings to File",cont=mainwin, handler=
function(h,...){
setfilename <- ginput("Please enter the filename")
INPUT <- .GlobalEnv$INPUT
save(INPUT, file=setfilename)
})
loadutton <- gbutton("Load Settings from File", cont=mainwin,
handler=function(h,...){
fname <- gfile(test="Choose a file",
type="open",
action="print",
handler =
function(h,...){
do.call(h$action, list(h$file))
}
)
load(fname)
.GlobalEnv$INPUT <- INPUT})
}

Designing dialog box in R

How can we a design a dialog box in R?
In my mind I have something in which an alert message is displayed or where we can write a certain value and then clicking a button it performs some computation... is that possible within the R workspace?
Something like this should work
library("tcltk")
button <- tkmessageBox(title='Message',message='Error x!',type='ok')
button <- tclvalue(button)
if(button == 'ok'){
#do something
}
winDialog also works:
library(utils)
answer<-winDialog("yesno", "was the suggestion useful?")
if (answer=='YES') {print('good!')} else {print('sorry')}
Just try to copy the full code and paste it into your R console: a dialog box will come out and a final output ('good!' or 'sorry') will appear depending on the answer you click.
You could try:
utils: select.list if you are choosing from pre-set alternatives. edit is more general, but less polished.
gWidgets: Interfaces with either tcltk or RGtk2 to produce dialogues. gmessage and ginput are probably what you are looking for.

How to create a demo for a presentation tutorial?

I want to prepare a demo (that will play sequentially in clicks) for a presentation tutorial...Can somebody help me how can I write a demo, suppose the following are steps in the demo...
#start
set.seed(1345)
x1 <- sample(letters[1:10], 5)
x1
sort(x1)
x <- sample(1:10, 5)
y <- sample(c(11:20), 5)
require(lattice)
plot(x,y)
z <- rnorm(5, 1, 0.5)
dataframe <- data.frame(x, y, z)
model1 <- lm(y ~x)
aov(model1)
#end
Sorry I could find a solution after hours and days of search. I appreciate your help.
Another way to do it:
Save your script in a file (demo.R)
Edit the script and sprinkle it with pause() in strategic places
In R, define pause <- function() invisible(readline())
Run the script with source("demo.R", echo=TRUE)
It will then print & run your commands and stop and wait for input at the sprinkled pause(). Just hit <Enter> to continue.
EDIT: I don't know a good way to hide the pause() statement. A possible way would be to copy the code for source() and modify it to skip printing calls to pause(), but that's a little overkill I think...
...but you could rename the pause function to anything you like - including '....', but you still need to call it like this: ....()
Hmmm. Maybe something like this:
'....' <- function(...) invisible(readline())
Then sprinkle your script with either:
....('Press Enter to continue')
# Or
....(Press_Enter_to_continue)
Another possibility if you rename the pause function to Pausing...:
Pausing...(Press_Enter)
A hacky way of doing what you want is:
Save commands as a script, eg testDemo.r
Copy into and existing package's demo folder, eg <Library>/base/demo
Run with demo(testDemo,package="base")
But it pauses in pages rather than by command. Ultimately though, you may want to create your own package to contain custom demos.
Edit
It seems the code for demo is mainly for checking that a demo exists, and the core is quite simple:
op <- options(device.ask.default=TRUE)
source("testDemo.r",echo=TRUE,max.deparse.length=Inf,keep.source=TRUE)
options(op)
Note that any pausing is only done by the presence of graphics, not any length of echoed text, as is actually the case with demo.

Resources