How to wait for a keypress in R? - r

I want to pause my R script until the user presses a key.
How do I do this?

As someone already wrote in a comment, you don't have to use the cat before readline(). Simply write:
readline(prompt="Press [enter] to continue")
If you don't want to assign it to a variable and don't want a return printed in the console, wrap the readline() in an invisible():
invisible(readline(prompt="Press [enter] to continue"))

Method 1
Waits until you press [enter] in the console:
cat ("Press [enter] to continue")
line <- readline()
Wrapping into a function:
readkey <- function()
{
cat ("Press [enter] to continue")
line <- readline()
}
This function is the best equivalent of Console.ReadKey() in C#.
Method 2
Pause until you type the [enter] keystroke on the keyboard. The disadvantage of this method is that if you type something that is not a number, it will display an error.
print ("Press [enter] to continue")
number <- scan(n=1)
Wrapping into a function:
readkey <- function()
{
cat("[press [enter] to continue]")
number <- scan(n=1)
}
Method 3
Imagine you want to wait for a keypress before plotting another point on a graph. In this case, we can use getGraphicsEvent() to wait for a keypress within a graph.
This sample program illustrates the concept:
readkeygraph <- function(prompt)
{
getGraphicsEvent(prompt = prompt,
onMouseDown = NULL, onMouseMove = NULL,
onMouseUp = NULL, onKeybd = onKeybd,
consolePrompt = "[click on graph then follow top prompt to continue]")
Sys.sleep(0.01)
return(keyPressed)
}
onKeybd <- function(key)
{
keyPressed <<- key
}
xaxis=c(1:10) # Set up the x-axis.
yaxis=runif(10,min=0,max=1) # Set up the y-axis.
plot(xaxis,yaxis)
for (i in xaxis)
{
# On each keypress, color the points on the graph in red, one by one.
points(i,yaxis[i],col="red", pch=19)
keyPressed = readkeygraph("[press any key to continue]")
}
Here you can see the graph, with half of its points colored, waiting for the next keystroke on the keyboard.
Compatibility: Tested under environments use either win.graph or X11. Works with Windows 7 x64 with Revolution R v6.1. Does not work under RStudio (as it doesn't use win.graph).

Here is a little function (using the tcltk package) that will open a small window and wait until you either click on the continue button or press any key (while the small window still has the focus), then it will let your script continue.
library(tcltk)
mywait <- function() {
tt <- tktoplevel()
tkpack( tkbutton(tt, text='Continue', command=function()tkdestroy(tt)),
side='bottom')
tkbind(tt,'<Key>', function()tkdestroy(tt) )
tkwait.window(tt)
}
Just put mywait() in your script anywhere that you want the script to pause.
This works on any platform that supports tcltk (which I think is all the common ones), will respond to any key press (not just enter), and even works when the script is run in batch mode (but it still pauses in batch mode, so if you are not there to continue it it will wait forever). A timer could be added to make it continue after a set amount of time if not clicked or has a key pressed.
It does not return which key was pressed (but could be modified to do so).

R and Rscript both send '' to readline and scan in non-interactive mode (see ? readline). The solution is to force stdin using scan.
cat('Solution to everything? > ')
b <- scan("stdin", character(), n=1)
Example:
$ Rscript t.R
Solution to everything? > 42
Read 1 item

The function keypress() from the package keypress reads a single key stroke instantly, without having to hit enter.
However, it only works in the Unix/OSX terminal or Windows command line. It does not work in Rstudio, the Windows R GUI, an emacs shell buffer etc.

This answer is similar to that of Simon's, but does not require extra input other than a newline.
cat("Press Enter to continue...")
invisible(scan("stdin", character(), nlines = 1, quiet = TRUE))
Using nlines=1 instead of n=1, the user can simply press enter to continue the Rscript.

A way of doing it (kinda, you have to press a button rather than a key, but close enough) is to use shiny:
library(shiny)
ui <- fluidPage(actionButton("button", "Press the button"))
server <- function(input, output) {observeEvent(input$button, {stopApp()})}
runApp(shinyApp(ui = ui, server = server))
print("He waited for you to press the button in order to print this")
To my experience, this has a unique characteristic: even if you ran a script that had code written following the runApp function, it will not run until you've pressed the button in the app (button that stops the apps from inside using stopApp).

Related

How can code execution be paused until a key is pressed?

I'm trying to get R to pause code execution, for the user to review some prior console output, to verify that file links have been correctly matched (using RegEx to match file names to their respective objects).
From some other answers, I've come up with:
readline(prompt="Press [enter] to continue or [esc] to exit")
As a stand alone line of code, this works as expected, but as soon as I add code below it, and send the whole block to the console, it runs right through the readline call without stopping:
readline(prompt="Press [enter] to continue or [esc] to exit")
x <- 1
y <- 2
Is there a way to get R to actually pause here?
I've also tried wrapping readline in a function, but it still doesn't work
pause <- function(){
p <- readline(prompt="Press [enter] to continue or [esc] to exit")
return(p)
}
pause()
x <- 1
y <- 2
Edit:
If I call the script via source(), readline does work properly. Is there a way to get that behavior without doing that?
By "send the whole block to the console", it sounds like you are copy-pasting your code into a running R instance.
If you do that, R will run each line in the order it receives it, as soon as it receives it. If any of the lines tries to take input, that input will come from whatever you are copy-pasting. So if you copy-paste this:
readline(prompt="Press [enter] to continue or [esc] to exit")
x <- 1
y <- 2
R will read and run the first line first. That first line will run and read the next line of input (which is a blank line here). Then R will come back and read and execute the other two lines.
You need to get your code entered into R completely before any of it runs. Try wrapping the whole thing in a function:
dostuff <- function(){
readline(prompt="Press [enter] to continue or [esc] to exit")
x <- 1
y <- 2
}
dostuff()
Using scan would always work:
message("Press [enter] to continue or [esc] to exit")
scan(quiet = TRUE, what = "")

Rscript and user prompts

Have a fully working R-script. When executing it from Rscript it doesn't stop to accept user input however.
The user input is some readline statements. The cat statements prompting for input works as intended. Have I missed something?
I execute 'Rscript scriptfile.R' from terminal on macOS.
You can create a function typeline() to read an input line and then use the result for your next commands. It will wait for your input text either you run your code in Rstudio or in terminal:
typeline <- function(msg="Enter text: ") {
if (interactive() ) {
txt <- readline(msg)
} else {
cat(msg);
txt <- readLines("stdin",n=1);
}
return(txt)
}
txt=typeline("your message: ")
print(txt)
Managed to get it to work by changing readline to readLines as mentioned in the post suggested by meenaparam. The downside with this method is that ithe script only works in batch mode, running it in Rstudio makes it hang. Would be good to know a general way to capture keyboard input i.e that works both in interactive and batch mode.
Use this
cat("What's your name? ")
name <- readLines(file("stdin"),1)
cat("What's your age? ")
age <- readLines(file("stdin"),1)
print(name)
print(age)

R: I fail to pause my code

I am trying to pause my code for a little while, time for me to observe the plots.
I tried:
print('A')
something = readline("Press Enter")
print('B')
print('C')
, then there is no pause, the line print('B') is fed to readline and get stored into something and therefore only A and C got printed on the screen. Note that if I add an empty line between Something = readline("Press Enter") and print("B"), then print("B") get printed on the screen but still the console doesn't allow the user to press enter before continuing.
And I tried:
print('A')
Sys.sleep(3)
print('B')
print('C')
The program waits 3 seconds before starting and then run "normally" without doing any pause between print('A') and print('B').
What do I missunderstand?
Here is my R version: R 3.1.1 GUI 1.65 Snow Leopard build (6784)
The problem with readline is that if you paste your script into an R console, or execute it from eg Rstudio, the redline function is read and then the next line of the script is read in as the console entry, which in your case sets the value of something to print('B).
An easy way to get around this is to stick your entire code in a function, then call the function to run it. So, in your case:
myscript = function(){
print('A')
something = readline(prompt = "Press Enter")
print('B')
print('C')
}
myscript()
The output of this for me (in Rstudio, with R version 3.1.1):
[1] "A"
Press Enter
[1] "B"
[1] "C"
This has always felt like a bit of a hack to me, but it's essentially what the readline documentation recommends in its example.
I've never used sleep in my code, so I can't help you there.
Edit to clarify based on comments: This will only work if myscript() is the very last line of your script, or if it is manually entered into the console after running the script to generate the function. Otherwise, you will run into the same problem as before- the next line of code will be automatically entered.

User input when executing R code in batch mode

I am searching for a way to get user input inside a loop while executing in batch mode.
readLines() and scan() work well for me in interactive mode only, in batch mode they start to read in lines of code as user input, unless all the code is surrounded by {}, which is inconvenient. I need a simple solution to get just 1 integer value in a way that I can just type in value and press ENTER, so
the input field (if the solution involves GUI) must automatically get focus and
ENTER must trigger end of input/submission.
I can't find a way to do it that will satisfy both conditions, e.g. ginput() from gWidgets activates the input field, but ENTER doesn't trigger form submission.
Here is how I solved my own problem:
require(gWidgets)
options(guiToolkit="RGtk2")
INPUT <- function(message) {
CHOICE <- NA
w <- gbasicdialog(title=message, handler = function(h,...) CHOICE <<- svalue(input))
input <- gedit("", initial.msg="", cont=w, width=10)
addHandlerChanged(input, handler=function (h, ...) {
CHOICE <<- svalue(input)
dispose(w)
})
visible(w, set=TRUE)
return(CHOICE)
}
repeat{
x=INPUT("Input an integer")
if(!is.na(as.integer(x))) break
}
print(x)
Update:
I can't test this right now, but take a look at ?menu and have it pop up a gui window.
I'm not certain if that will work, but it is different in that it takes a mouse-click response.
original answer:
As per the documentation to ?readline:
This can only be used in an interactive session.
..
In non-interactive use the result is as if the response was RETURN and the value is "".
If you are simply waiting for one piece of information, and you do not know this piece of information before beginning the execution of the script (presumably, there is a decision to be made which is dependent on the results earlier in the script), then one alternative is to simply break your script up into three parts:
everything before the decision point.
an interactive script which prompts for input
everything after the decision point.
And simply chain the three together by having the first end by calling the second in an interactive session. Then have the second end by calling the third.

Programmatically close the window created by `View(x)`

I'm viewing a dataframe in R using View:
my_df <- data.frame(a=1:10, b=letters[1:10])
View(my_df)
I'd like to now close the resulting window programmatically (rather than clicking the X button).
How may I do this? graphics.off doesn't work as it isn't a graphics device. Looking at the View code, internal function dataviewer is used to display the window but I'm not sure what it uses in the background (tcltk?) so am not sure how to close the window.
Re some comments as to why I want this.
I'm basically doing a user-checking step in a script whereby the user is asked if a snippet of a dataframe and a corresponding image go together. It goes something like this:
for (i in 1:heaps) {
1. View(a snippet of a big dataframe)
2. show an image
3. readline('Is this OK? [Y/N]: ') (store the i for which it's not OK)
4. close the image window (graphics.off()), close the View(..) window.
}
I basically wanted to reduce the user interaction down staring at the image & dataframe snippet and typing Y or N, so they don't have to manually close th dataframe window for each i in the loop.
(I'm part-way through this validation myself and am dealing with 200 View(snippet) windows that I haven't bothered to close D:. Also, have noticed that the opening of the windows steals keyboard focus away from the prompt so me typing Y/N is not as fast as I'd like. But I only have to do this once, so it'll do for now. I am curious as to the answer to the question though, for next time).
One way to accomplish what you're after is to make use of the system function. You could, for example, determine the Window ID/Name and then issue a close command like so:
system('(sleep 10; wmctrl -c "Data: my_df") &')
The above command will wait 10 seconds, and then issue a command to the window manager to close any window with the name "Data: my_df". These 2 commands are wrapped in parens. This is known as a Compound Command in bash. The entire Compound Command is backgrounded, '&'.
I tested the following and it worked:
# sample1.R
my_df <- data.frame(a=1:10, b=letters[1:10])
system('(sleep 10; wmctrl -c "Data: my_df") &')
View(my_df)
Another way to accomplish this is like so:
# sample2.R
my_df <- data.frame(a=1:10, b=letters[1:10])
View(my_df)
system('read -p "Press [Enter] key to start backup..."')
my_df2 <- data.frame(a=1:10, b=letters[1:10])
View(my_df2)
system('read -p "Press [Enter] key to start backup..."')
I'm running these like this:
R CMD BATCH sample2.R
NOTE: The prompt from the read -p command isn't showing up in my terminal but you could just print a duplicate prompt message in R.
If you want to close all open "View" windows, building on the answer of slm :
CloseViews <- function(){
cmd <- paste0('wmctrl -c "Data:" -v')
ok <- TRUE
while(ok){
out <- suppressWarnings(system(cmd,intern=TRUE,ignore.stderr=TRUE))
Sys.sleep(0.2)
ok <- is.null(attr(out,"status"))
print(ok)
}
}
Then CloseViews() does the trick, which I found particularly helpful when working interactively.

Resources