capturing cat output periodically for R shiny output (renderPrint) - r

Hope someone can help me with this.
Let's say there is a function "example" which is something like
##function from a package
example<-function(f){
#does something
cat("step 1 done....")
# etc etc
cat("step 2 done....")
return(some_data_frame)
}
##server ui code
example2<-reactive({
if(input$some_action_button==0)
return()
result<-isolate(example(input$f1))
return(result)
})
output$f2<-renderPrint({
example2()
})
Is there some way to capture the "cat" outputs from the function into renderPrint, periodically? Assuming that this is a long function to process and it would be nice for the user to get some feedbabk. invalidateLater does not work for things that are already within a function (at least it seems that way when I tried it here).
Also, as a secondary problem, writing the code in the above manner would cause renderPrint to capture both the "cat" and the data.frame together, possibly because of the "return".
If anyone could point me in the right direction, it would be most helpful! Thanks!

First of, great question I've been thinking a lot about this.
Since shiny is single threaded it's a bit tricky capturing function output and displaying it in shiny from what i know.
A work around for this would be using a non blocking file connection and running the function you want to capture the output from in the background while reading the file for the function output (Check the edit history to see how to do this).
Another way of doing this would be overriding the cat function to write to stderr (simply switching cat with message) and capture the function output like this:
library(shiny)
library(shinyjs)
myPeriodicFunction <- function(){
for(i in 1:5){
msg <- paste(sprintf("Step %d done.... \n",i))
cat(msg)
Sys.sleep(1)
}
}
# Override cat function
cat <- message
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
actionButton("btn","Click me"),
textOutput("text")
),
server = function(input,output, session) {
observeEvent(input$btn, {
withCallingHandlers({
shinyjs::text("text", "")
myPeriodicFunction()
},
message = function(m) {
shinyjs::text(id = "text", text = m$message, add = FALSE)
})
})
}
))
This example is mostly based on this question by daattali.

Related

Adding reactive values to a dataframe - Rshiny

At the moment I am attempting the following: import a file in Rshiny, give it a number (interactive), and then move on to the next file. This part works fine. However, I would also like to store the data of every iteration, and then show it on the user interface.
However, it is not working. So I guess something is not right with the reactivity, but I am not sure how to fix it.
ui<-fluidPage(
mainPanel(
radioButtons(inputId="score",label="Give a score",choices=c(1:9),selected=1),
actionButton(inputId="new","Next file"),
tableOutput("savdat")
)
)
server<-function(input,output){
NoFiles<-length(list.files())
Here an empty reactive data.frame
outputdata<-reactive(data.frame("file"="file","score"="score"))
filename<-eventReactive(input$new,{
WhichFile<-sample(1:NoFiles,1)
filename<-list.files()[WhichFile]
return(filename)
})
scores<-eventReactive(input$new,{
return(input$score)
})
Then I would like to append the previous values of the outputdata, with the new values. But it is not working
outputdata<-eventReactive(input$new,{
rbind(outputdata(),filename(),scores())
})
output$savdat<-renderTable(outputdata())
}
shinyApp(ui, server)
Any advice would be welcome
It appears you want the reactivity to occur each time you click on the 'Next file' button. I rewrote your code to respond just once, using 'ObserveEvent', each time the 'Next file' button is clicked. The 2nd challenge is permitting values to persist upon each reactive event. While there are multiple ways to handle this, I chose an expedient technique, the '<<-' assignment statement, to permit the variable 'output data' to persist (this is generally not a good programming technique). Because the variable 'outputdata' exists in all environments, you'll need to wipe your environment each time you want to run this program.
Here's my rewrite using the same ui you created:
ui<-fluidPage(
mainPanel(
radioButtons(inputId="score",label="Give a score",choices=c(1:9),selected=1),
actionButton(inputId="new","Next file"),
tableOutput("savdat")
)
)
server<-function(input,output){
NoFiles<-length(list.files())
setupData <- function(filename,score) {
data <- data.frame(filename,score,stringsAsFactors = FALSE)
return(data)
}
observeEvent (input$new, {
WhichFile<-sample(1:NoFiles,1)
filename<-list.files()[WhichFile]
if (!exists(c('outputdata'))) {
score <- input$score
outputdata <<- data.frame (filename,score,stringsAsFactors = FALSE)
}
else {
outputdata <<- rbind(outputdata,setupData(filename,input$score))
}
# Show the table
output$savdat<-renderTable(outputdata)
})
}
shinyApp(ui, server)

Test whether any input in a set of numbered input objects in R Shiny is empty

Let's say I have created 10 selectInput dropdowns for a multi plot export and these selectInputs are called "xaxis_1", "xaxis_2", ..... , "xaxis_10"
for a single 1 I can write:
if(!is.null(input$xaxis_1)) { .... do stuff } to stop it running export when the user hasn't entered any name, and presses submit, to avoid crashes.
A bit more general you can check this:
if(!is.null(input[[paste('xaxis', i, sep = '_')]])) { ...}
how can you write it elegantly so that 1 line of code checks whether ANY of the 1:10 input[[...]] is empty, i.e. NULL?
The nr of inputs depends on how many plots the user wants to export per file, so all is build with lapply(1:input$nrofplots, function(i) { .... } renderUI structure, and my if statement needs to have the same flexibility of 1:n
In a situation like below in the image, pressing Initiate export should give a sweetalert (got that covered) saying there is at least 1 value missing
Here a snippet I used in the UI side to validate the user's inputs.
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(), # Set up shinyjs
numericInput('axis1','Val 1',1),
numericInput('axis2','Val 2',1),
numericInput('axis3','Val 3',1),
actionButton('Go','Plot')
)
server <- function(input, output, session) {
#Try 1, space, AAA and check what shiny will return
observe(print(input$axis1))
observe({
All_Inputs <- vapply(paste0('axis',1:3),
function(x){isTruthy(input[[x]])},
logical(1))
All_InputsCP <- all(All_Inputs)
shinyjs::toggleState(id="Go", condition = All_InputsCP) #This is to make the button Go able or disable according to condition All_InputsCP #
})
}
shinyApp(ui, server)
I hope it helps.

R shiny: printing the console output produced by an R package to UI

I am using an R package that, in addition to calculating and returning things, prints some very useful info to the console. For example, it prints what iteration it is on right now.
How could I print that console output directly to my UI?
Assume this is my UI:
ui <- shinyUI(
fluidPage(
titlePanel("Print consol output"),
sidebarLayout(
sidebarPanel(actionButton("go", "Go")),
mainPanel(
verbatimTextOutput("console_text")
)
)
)
)
My user clicks on actionButton “Go” and my package starts doing things - while sending stuff to the console at the same time. I guess, I want the content of the console to be saved as output$console_text - but I don’t know if that’s the right approach and how to do it.
I didn't want to make the code super-complicated. So, instead of a package, I created my own little printing function in Server.
server <- function(input, output, session) {
myfunction <- function(x) {
for(i in 1:x) cat(i)
return(x)
}
observeEvent(input$go, {
{
# This is probably wrong:
output$console_text <- renderPrint(onerun <- myfunction(20))
}
})
}
shinyApp(ui, server)
Thank you very much!

One Action Button for Multiple Dependent Events in Shiny

I have developed a Shiny app that allows the user conditional selection of some dependent events. A very simplified toy example is below to help illustrate my question/problem.
In my real problem, the server code contains multiple computationally expensive procedures that are optional to run. There is a "baseline" function that must run to produce output and then firstObject or secondObject take that as input and produce more output if it is selected by the user to do so.
Each function can take upwards of 30 to 40 minutes. So, I wrote the code to allow the user to select using the checkInputBox which functions they want to run and then after selecting them, there is a single action button that runs them all allowing the user to leave and let the process take its course over many hours. This was more convenient than having an actionButton associated with each possible event.
The code below is successful in yielding all the desired output. But, I am not sure from a design point of view if it is "right". In my toy example, the code is simple, but suppose the code for baseObject takes 30 minutes to run. While baseObject is running, the code for firstObject and secondObject were also executed because they depend on the same action button. But, they cannot do anything until the function for baseObject is done. Similarly secondObject cannot do anything until firstObject is done.
Again, this all works and yields the correct output (in my real code as well as in the toy code). But, is there a way to maintain the single action button, but for firstObject to not do anything UNTIL baseline Object has produced its output and then secondObject would wait for firstObject to yield its output if the user selected it.
My worry is that I am creating additional computational overhead in the firstObject is trying to do something it cannot do until baseObject is done and it is cycling over and over until it can properly execute.
I know I can create different action buttons. For instance I could create an action button for baseline and then the user could wait until it is done and then click the action button for firstObject and so on. But, functionally this would not work as in the real problem this allows the entire selected process to run, which can take hours and the user does not need to be in front of their machine.
Thank you and I hope this code helps illustrate the problem as I have described it.
ui <- {
fluidPage(
h3('Run Stuff'),
checkboxInput("runModel1", "Model 1"),
checkboxInput("runModel2", "Model 2"),
actionButton('runAll', 'Run Models'),
verbatimTextOutput("out1"),
verbatimTextOutput("out2")
)
}
server <- function(input, output, session) {
baseObject <- eventReactive(input$runAll, {
if(input$runModel1){
runif(100)
}
})
firstObject <- eventReactive(input$runAll, {
if(input$runModel1){
runif(100) + baseObject()
}
})
secondObject <- eventReactive(input$runAll, {
if(input$runModel2){
runif(100) + firstObject()
}
})
output$out1 <- renderPrint({
if (input$runModel1)
firstObject()
})
output$out2 <- renderPrint({
if (input$runModel2)
secondObject()
})
} # end server
shinyApp(ui, server) #run
Two things to remember about reactive expressions:
Reactive expressions are lazy and only execute when called by something else. This is different from observers, which execute immediately any time their dependencies change.
Reactive expression results are cached. As long as their dependencies have not changed, subsequent calls won't cause the expression to re-execute, but instead retrieve the cached value.
Based on these two points, I don't think you have a problem and your example does what you're looking for. With both checkboxes ticked, each reactive expression would only run once per action button click.
Although I can suggest removing the unnecessary if statements in the eventReactives. That would allow the user to only check runModel2 and have all its dependencies run properly. Modified example below - I also added some message(...) statements so you can see the execution flow in the R console.
library(shiny)
ui <- fluidPage(
h3('Run Stuff'),
checkboxInput("runModel1", "Model 1"),
checkboxInput("runModel2", "Model 2"),
actionButton('runAll', 'Run Models'),
verbatimTextOutput("out1"),
verbatimTextOutput("out2")
)
server <- function(input, output, session) {
baseObject <- eventReactive(input$runAll, {
message("calculating baseObject...")
result <- runif(100)
message("...baseObject done")
return(result)
})
firstObject <- eventReactive(input$runAll, {
message("calculating firstObject...")
result <- runif(100) + baseObject()
message("...firstObject done")
return(result)
})
secondObject <- eventReactive(input$runAll, {
message("calculating secondObject...")
result <- runif(100) + firstObject()
message("...secondObject done")
return(result)
})
output$out1 <- renderPrint({
if (input$runModel1)
firstObject()
})
output$out2 <- renderPrint({
if (input$runModel2)
secondObject()
})
}
shinyApp(ui, server)

R Shiny: mirror R console outputs to Shiny

I made a function that is performing some complex calculations via a for loop. In order to show progress, the function will print out the current progress via something like message(...), and the final outcome of this function is a data frame.
But when I implement this in Shiny, the for loop counter is printed only in the R console rather than the Shiny document as intended. Is there a way to showing the outputs in the R console in real time during executions?
A very minimal example is here. Notice that in the Shiny interface, the counter is not present.
foo <- function() {
ComplexResult = NULL # vector initiation
for(i in 1:5){
ComplexResult[i] = letters[i]
# At each stage of the for loop, we store some complex calculations
message(paste0("For loop counter is on i = ", i))
# This shows progress of the for loop, also other relevant messages if necessary.
Sys.sleep(0.1) # Comment this out to remove pauses during execution.
}
return(as.data.frame(ComplexResult))
}
runApp(shinyApp(
ui = fluidPage(
dataTableOutput("VeryFinalOutcome")
),
server = function(input,output, session) {
fooOutcome = foo()
output$VeryFinalOutcome = renderDataTable(fooOutcome) # This will only display the function output (i.e. the letters) in Shiny.
}
))
My attempt: the capture.output(foo(),type="message") function did not help. Even though it captured the messages successfully, but it can only be displayed after all execution. And there is a extra issue of not being able to store the actual foo() outputs.
Thanks

Resources