Loading and saving variables in R with gWidgets - r

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})
}

Related

Perform multiple actions upon pressing an actionButton

I've been struggling to get my Shiny app to perform multiple operations upon pressing a single actionButton. I've looked around a lot and haven't been able to solve it. I've tried eventReactive, observeEvent, putting everything in one block, separating them out - you can see the eventReactive and observeEvent attempts in the server.R code.
I have a button which is supposed to run some models, once pressed. Upon pressing the button, I'd like to do quite a few things:
Print a message letting the user know that something is happening
Concatenate the previously-defined user options into a .sh file and execute model prediction
Periodically check for the model output file
Once output file has been created, print "Done" and save as a global variable for further analysis
This is what I have so far (it doesn't work, the model runs [yay!] and that's it). It's particularly confusing to me that the sys command runs fine, but the "Running" message doesn't get printed. I've made sure that I haven't misspelt any var names.
Dummy python script to produce an output: Let's call it script_shiny.py
import time
import pandas as pd
# construct dummy df
d = {'col1': [1, 2], 'col2': [3, 4], 'col3': [5,6]}
df = pd.DataFrame(data=d)
time.sleep(60) # let's add a delay to simulate the 'real' situation
df.to_csv("test_out.txt",sep="\t") # create the output file to be detected
The relevant part of ui.R
*CODE TO CAPTURE PARAMS, WORKS FINE*
shiny::actionButton("button", "Run PIDGIN"), # RUN MODELS
tags$br(),
textOutput("pidginrunning"), # OUTPUT RUNNING MESSAGE
textOutput("pidgindone"), # OUTPUT DONE MESSAGE
The relevant part of server.R
# One approach - observeEvent and defining renderText and .sh file creation and running all together
observeEvent(input$button, { # OBSERVE BUTTON BEING PRESSED
output$pidginrunning <- renderText({ # LET USER KNOW ITS RUNNING - NOT WORKING
paste0("Running PIDGIN...")
})
# CREATE .SH FILE AND RUN DUMMY SCRIPT - WORKING (params previously defined)
bin_bash <- "#!/bin/bash"
output_name <<- "test_out.txt"
runline <- paste0("python script_shiny.py")
bash_file <- data.frame(c(bin_bash,runline))
write.table(bash_file,"./run_pidgin.sh",quote=F,row.names=F,col.names=F)
system("bash -i run_pidgin.sh")
})
# CHECK OUTPUT - NOT WORKING
# Defining output_name in the previous code block works, its defined as a global variable
# Other approach, use eventReactive to define a 'function' which is called separately
checkOutput <- eventReactive(input$button, {
reactivePoll(1000, session, checkFunc = function() {
if (file.exists(output_name)) # check for the output file
assign(x = "preds", # Read it in if so, and define global variable
value = read.csv(output_name,header=T,sep="\t"),
envir = .GlobalEnv)
paste0("Done. Please move onto the Results tab.")
})
})
output$pidgindone <- renderText({ # PRINT DONE MESSAGE SO USER KNOWS TO MOVE ON
checkOutput()
})
(Hope that counts as minimum example. As a proxy I suppose you can generate a dummy .sh file which produces a .txt file table, instead of running the models).
Cheers for your time and help.
EDIT: added example script which creates an output which should be detected

RStudio: Alternative hotkey to source selected part without echo

In RStudio, [Ctrl] + [Enter] runs the currently highlighted code part, but with echo. [Ctrl] [Shift] + [S] sources the whole file without echo.
Is it possible to run the highlighted/selected code part without having the input clutter the console? Or is this an implicit requirement when running code instead of sourcing? (There seem to be subtle differences mentioned in other SO posts)
In conclusion: Is there a Hotkey to press to get exactly what [Ctrl]+[Enter] does, but without cluttering the console with my script code?
I don't believe there is a built-in way to do that. However, you can create an Addin that will source() the code and sink() results to a text file, which can be openend automatically.
In RStudio, create a New Project > R Package.
Create a new R script called runSelected:
#' runSelected
#'
#' RStudio addin to run selected code and show results in
#' default text editor.
#'
#' #export
runSelection <- function() {
context <- rstudioapi::getActiveDocumentContext()
selection <- rstudioapi::primary_selection(context)
tmp_code <- tempfile()
f <- file(tmp_code)
writeChar(object = selection$text, con = f)
close(f)
tmp_results <- tempfile()
sink(tmp_results)
source(tmp_code)
sink()
shell.exec(tmp_results)
}
In the package's directory, create file inst/rstudio/addins.dcf:
Name: Run Selection
Description: Run selected text and see results in text editor
Binding: runSelection
Interactive: false
Generate documentation with roxygen2, build and voilĂ !

How to update the file related to a `fileInput` variable in R Shiny without user interaction?

I'm working on an app in R where the users need to choose a file from their computer, with a RShiny fileInput button. I want to modify this, so that the associated variable can be assigned (i.e. a file can be loaded) automatically by the programm, without having the user click on the button and choose the file.
The problem I'm facing is that a fileInput has 4 fields, amongst which I only can know 3. For instance, when I load the file hello.csv in the variable inFile through the normal procedure, here is what I get :
inFile$name = hello.csv
inFile$size = 8320
inFile$type = text/csv
inFile$datapath = C:\\Users\\MyName\\AppData\\Local\\Temp\\Rtmpkh8Zcb/7d5f0ff0111d440c7a66b656/0
Though I could have guessed the second and the third one knowing the file, I have no idea how the datapath field is assigned...
I've tried to declare inFile as a NULL global variable, then to assign one by one the different fields, but I'm stuck with this last one. Is there an other way to do, like a function that mimics the behaviour of a user who clicks on the file input button and choose a specified file ?
Thank you very much.
If all you're looking to do is load a file initially, you don't have to rely on Shiny functions to do that. You can just rely on R functions. Set up your app like this:
ui <- shinyUI(
fileInput("inFile", label="Choose a file", multiple=F)
)
server <- shinyServer(function(input, output, session) {
values <- reactiveValues()
dat <- reactive({
if (is.null(inFile$datapath)) {
dat <- read.csv("path/to/your.csv")
values$file_name = "your.csv"
values$file_type = "csv"
values$file_size = file.size("path/to/your.csv")
values$file_path = "path/to/your.csv"
} else {
dat <- read.csv(inFile$datapath)
values$file_name = inFile$name
values$file_size = inFile$size
values$file_type = inFile$type
values$file_path = inFile$datapath
}
})
})
shinyApp(ui=ui, server=server)
In the above code, the Shiny app will start and see that inFile$datapath is NULL and will load a predefined file of your choosing. It won't run again until inFile changes, at which point it will load the file that the user pointed to.
Hope that helps.
Update
I changed the code above to use reactiveValues to store the pieces of information that need to be used throughout the app. If you just set those and then do a find/replace for input$inFile$datapath and replace it values$file_path, your code should work just fine.
Here is how I figured it out :
I edited the original code, so that all the read.csv(...) are replaced with calls to a data.frame global variable. I also added a small button that you need to click on before you continue. This button saves what you just loaded in the Database (if you chose a file with the fileInput) and assigns the right values to the global variables that will be needed for the following operations. If you chose no file at all, it will directly assign the variables from the data found in the Database.
So I did not find a proper solution to the problem, but this is a workaround that will do the job in my case.
#brittenb I couldn't get your reactive solution to work as I wanted to, that's why I ended up doing this another way. Thanks for having taken the time to think about it though.
I'm still open to suggestions on how to update the file in a fileInput without user interaction.

How Do I Stop An R gWidgets Script Exiting

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.

R sometimes does not save my history

I have a program in R. Sometimes when I save history, they do not write into my history file. I lost some histories a few times and this really drive me crazy.
Any recommendation on how to avoid this?
First check your working directory (getwd()). savehistory() saves the history in the current working directory. And to be honest, you better specify the filename, as the default is .History. Say :
savehistory('C:/MyWorkingDir/MySession.RHistory')
which allows you to :
loadhistory('C:/MyWorkingDir/MySession.RHistory')
So the history is not lost, it's just in a place and under a name you weren't aware of. See also ?history.
To clarify : the history is no more than a text file containing all commands of that current session. So it's a nice log of what you've done, but I almost never use it. I construct my "analysis log" myself by using scripts, as hinted in another answer.
#Stedy has provided a workable solution to your immediate question. I would encourage you to learn how to use .R files and a proper text editor, or use an integrated development environment (see this SO page for suggestions). You can then source() in your .R file so that you can consistently replicate your analysis.
For even better replicability, invest the time into learning Sweave. You'll be glad you did.
Check the Rstudio_Desktop/history_database file - it stores every command for any working directory.
See here for more details How to save the whole sequence of commands from a specific day to a file?
Logging your console on a regular basis to **dated* files is handy. The package TeachingDemos has a great function for logging your console session, but it's written as a singleton, which is problematic for automatic logging, since you wouldn't be able to use that function to create teaching demo's if you use it for logging. I re-used that function using a bit of meta-programming to make a copy of that functionality that I include in the .First function in my local .Rprofile, as follows:
.Logger <- (function(){
# copy local versions of the txtStart,
locStart <- TeachingDemos::txtStart
locStop <- TeachingDemos::txtStop
locR2txt <- TeachingDemos:::R2txt
# creat a local environment and link it to each function
.e. <- new.env()
.e.$R2txt.vars <- new.env()
environment(locStart) <- .e.
environment(locStop) <- .e.
environment(locR2txt) <- .e.
# reference the local functions in the calls to `addTaskCallback`
# and `removeTaskCallback`
body(locStart)[[length(body(locStart))-1]] <-
substitute(addTaskCallback(locR2txt, name='locR2txt'))
body(locStop)[[2]] <-
substitute(removeTaskCallback('locR2txt'))
list(start=function(logDir){
op <- options()
locStart(file.path(logDir,format(Sys.time(), "%Y_%m_%d_%H_%M_%S.txt")),
results=FALSE)
options(op)
}, stop = function(){
op <- options()
locStop()
options(op)
})
})()
.First <- function(){
if( interactive() ){
# JUST FOR FUN
cat("\nWelcome",Sys.info()['login'],"at", date(), "\n")
if('fortunes' %in% utils::installed.packages()[,1] )
print(fortunes::fortune())
# CONSTANTS
TIME <- Sys.time()
logDir <- "~/temp/Rconsole.logfiles"
# CREATE THE TEMP DIRECORY IF IT DOES NOT ALREADY EXIST
dir.create(logDir, showWarnings = FALSE)
# DELETE FILES OLDER THAN A WEEK
for(fname in list.files(logDir))
if(difftime(TIME,
file.info(file.path(logDir,fname))$mtime,
units="days") > 7 )
file.remove(file.path(logDir,fname))
# sink() A COPY OF THE TERMINAL OUTPUT TO A DATED LOG FILE
if('TeachingDemos' %in% utils::installed.packages()[,1] )
.Logger$start(logDir)
else
cat('install package `TeachingDemos` to enable console logging')
}
}
.Last <- function(){
.Logger$stop()
}
This causes a copy of the terminal contents to be copied to a dated log file. The nice thing about having dated files is that if you use multiple R sessions the log files won't conflict, unless you start multiple interactive sessions in the same second).

Resources