I have a script which I want to add to my Shiny app, and the original script can be simplified to the following:
plot(c(1:3),c(2,4,6), main ="This is first plot I want displayed")
a <- menu(c(1:5), title="what would you like to change the first point to?")
plot(c(1:3),c(a,4,6), main ="This is second plot I want displayed")
b <- menu(c(1:5), title="what would you like to change the second point to?")
plot(c(1:3),c(a,b,6), main ="This is second plot I want displayed")
The above script plots the first plot, then waits for user input before plotting second, and waits again for user input before plotting third.
However, when I try to convert it to shiny app as seen below, it never updates the first or the second plot, and the things I've tried to make it stop for user input where shown have not worked.
I have tried using req() but it seems to stop the script entirely so the last things are not run at all, and you have to start the entire script over.
So, how can i make it display all plots in sequence, and how can I make the script stop and wait for input before continuing?
if(interactive()){
ui <- fluidPage(
actionButton("button","Click me"),
selectInput("input", "Input", c(1:10)),
textOutput("text"),
plotOutput("plot")
)
server <- function(input, output) {
a<-1
observeEvent(input$button, {
output$plot <- renderPlot(plot(c(1:3),c(2,4,6), main ="This is first plot I want displayed"))
output$text <- renderText("Please select a number to multiply the first point with")
#This is where I want the script to wait for user input
output$plot <- renderPlot(plot(c((1),(2),(3)),c((input$input),(a),(3)), main="This is plot the second plot"))
a<-a+1
#Here I want the script to wait for user input again
output$plot <- renderPlot(plot(c((1),(2),(3)),c((input$input),(a),(3)), main="This is plot the third plot"))
})
}
shinyApp(ui=ui, server=server)
}
The goal is that it updates the plots when they are rendered in the code, and that it waits for user input until script continues, instead of just keeping going.
Perhaps this is what you want.
req is used to only display when a variable is available. You need to create the second renderUI in the server since you cannot use req in the ui part.
if(interactive()){
ui <- fluidPage(
plotOutput("plot1"),
numericInput ("num1", "Please select a number to multiply the first point with", NA, 1, 10),
plotOutput("plot2"),
uiOutput("num2"),
plotOutput("plot3")
)
server <- function(input, output) {
output$plot1 <- renderPlot(plot(c(1:3),c(2,4,6), main ="This is first plot I want displayed"))
output$plot2 <- renderPlot({
req(input$num1)
plot(c(1:3),c(2*(input$num1),4,(6)),
main="This is plot the second plot"
)
}
)
output$plot3 <- renderPlot({
req(input$num1, input$num2)
plot(c(1:3),c(2*(input$num1)+(input$num2),4,(6)),
main="This is plot the third plot"
)
}
)
output$num2 <- renderUI({
req(input$num1)
numericInput ("num2", "Please select a number to add to first point", NA, 1, 10)
})
}
shinyApp(ui=ui, server=server)
}
To be honest, I´m not 100% sure if I know what you expect, even after reading your text 5 times. I have a guess ;-).
Your pause function, which cause plots to render one step after another can be done with invalidateLater. This has to "live" inside a reactiveValue. I don´t know exactly who is the creator of this function, I saved it in my snippets (all glory to the unknown person).
To render a plot or run a script based on the input of a user, try to catch it by using an if statement.
Hope this helps :-).
library(shiny)
if(interactive()){
ui <- fluidPage(
selectInput("input", "Input", c(1:10)),
actionButton("apply", "Apply"),
textOutput("text"),
plotOutput("plot")
)
server <- function(input, output, session) {
rv <- reactiveValues(i = 0)
maxIter <- 10
observeEvent(input$apply, {
rv$i <- 0
observe({
isolate({
rv$i <- rv$i + 1
})
if (isolate(rv$i) < maxIter){
invalidateLater(2000, session)
}
})
})
output$plot <- renderPlot( {
if(rv$i > 0) {
if(input$input <= 4) {
plot(c((rv$i*1),(rv$i*2),(rv$i*3)),c((1),(2),(3)), main = sprintf("Input <= 4; Round %i", rv$i), type = "l")
} else {
plot(c((rv$i*1),(rv$i*5),(rv$i*4)),c((1),(2),(3)), main = sprintf("Input > 4; Round %i", rv$i), type = "l")
}
} else {
plot(c(1:3),c(2,4,6), main ="This is first plot")
}
})
}
shinyApp(ui=ui, server=server)
}
Related
I'm trying to create a plot with a bunch of boxes and then when a box gets clicked on it gets colored in up. I'm having two issues with this. 1. I can't figure out a way for the figure to update dynamically when I click. 2. I can't figure out how to store the values that come out of the click input variable so that I have stored all previous clicks and would be able to color in multiple boxes. You can see a few ways I've tried to solve and test either of the two issues and I'm not having any luck. Any help with either issue would be appreciated.
ui <- fluidPage(
# Application title
titlePanel("Boxes"),
sidebarLayout(
sidebarPanel(
textOutput("text")),
# Get it it's a pun
mainPanel(
plotOutput("boxPlot",click = "test")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
vals <- reactiveValues(x=NA,y=NA,test=NA)
observeEvent(input$click, {
vals$x <- c(vals$x,input$test$x)
vals$y <- c(vals$y,input$test$y)
vals$test <- input$test$x
})
output$boxPlot <- renderPlot({
par(mai=c(0,0,0,0))
plot(1,ylim=c(2,15),xlim=c(2,15),type='n',yaxs='i',xaxs='i',ylab='',xlab='',axes=F)
for (i in 2:15) {
abline(v=i)
abline(h=i)
}
observeEvent(input$click, { rect(floor(input$test$x),floor(input$test$y),ceiling(input$test$x),ceiling(input$test$y),col='blue')})
# if (length(vals$x) > 0) {
# rect(floor(vals$x[1]),floor(vals$y[1]),ceiling(vals$x[1]),ceiling(vals$y[1]),col='blue')
# }
})
# output$text <- renderText(vals$x[length(vals$x)])
output$text <- renderText(vals$test)
}
# Run the application
shinyApp(ui = ui, server = server)
This might be a simple solution.
You should only have one single observeEvent for your click event. In that observeEvent, store your x and y values as reactiveValues as you current are doing.
Then, your renderPlot should plot the grid lines and filled in rectangles based on your reactiveValues. By including input$boxPlot_click (as I called it) in renderPlot the plot will be redrawn each click.
library(shiny)
ui <- fluidPage(
titlePanel("Boxes"),
sidebarLayout(
sidebarPanel(
textOutput("text")),
mainPanel(
plotOutput("boxPlot", click = "boxPlot_click")
)
)
)
server <- function(input, output) {
vals <- reactiveValues(x=NA,y=NA)
observeEvent(input$boxPlot_click, {
vals$x <- c(vals$x,input$boxPlot_click$x)
vals$y <- c(vals$y,input$boxPlot_click$y)
})
output$boxPlot <- renderPlot({
input$boxPlot_click
par(mai=c(0,0,0,0))
plot(1,ylim=c(2,15),xlim=c(2,15),type='n',yaxs='i',xaxs='i',ylab='',xlab='',axes=F)
for (i in 2:15) {
abline(v=i)
abline(h=i)
}
for (i in seq_along(length(vals$x))) {
rect(floor(vals$x),floor(vals$y),ceiling(vals$x),ceiling(vals$y),col='blue')
}
})
output$text <- renderText(paste0(vals$x, ', ' , vals$y, '\n'))
}
shinyApp(ui = ui, server = server)
I'm trying to write a calculator using Shiny in R for a video game (you input the stats of you and your opponent, and it outputs your odds of winning a match). However, I can't get the Shiny app to output any of my variables. The app runs fine, but nothing outputs when the action button is selected.
Trying to find the issue, I simplified my code into a basic calculator that takes a numeric input, multiplies it by two, and outputs a result. As before, nothing is displayed when the action button is pushed. However, if you directly type a string into the renderText function, it works just fine.
I need to include an action button in my ultimate code because I don't want it to calculate the result until several numerical values have been typed in. Could the action button be causing an issue somewhere, or is it something else?
Below is the simplified code. If I can get this to run, I'm sure I could get my more complicated code to run. Thank you!
library(shiny)
library(shinythemes)
ui <- fluidPage(
titlePanel("Multiply by 2"),
fluidRow(
column(12, textOutput("test"),
numericInput(inputId = "start", "Start", value = 1),
actionButton("go", "Go!") )
)
)
server <- function(input, output) {
myval <- reactiveValues()
observeEvent(input$go, {
reactive ({
if (input$go == 0)
return()
isolate({
myval$calc <- paste("The result is", 2*input$start)
})
})
})
output$test <- renderText({
if (input$go == 0)
return()
isolate({
myval$calc
})
})
}
shinyApp(ui = ui, server = server)
It looks like there is some extra code in there we don't need, for example the isolate function. See the below minimal example:
input$go doesn't tell us what the button is doing. Try running print(input$go) and have a look at the output.
library(shiny)
ui <- fluidPage(
titlePanel("Multiply by 2"),
fluidRow(
column(12,
textOutput("test"),
numericInput(inputId = "start", "Start", value = 1),
actionButton("go", "Go!")
)
)
)
server <- function(input, output) {
myval <- reactiveValues()
#Observe button (will run when the button is clicked)
observeEvent(input$go, {
myval$calc <- paste("The result is", 2 * input$start)
})
#Text output (will run when myval$calc changes)
output$test <- renderText({
myval$calc
})
}
shinyApp(ui = ui, server = server)
I need help with action button working only once. If you click on button it will shows you graph,table and some text. But if you want to change something on sidebar like a colour it will change and you dont have to press the button... (I need to have: If you want to change something colour,values..you need to always push the action button to confirm it it cant change without button)
Also, I need to show only the last N rows from table airquality which depends on numericID input. Can anyone help me?
library(shiny)
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
observeEvent(input$gobutton, {
output$textik <- renderText({
vypis=c("Zobrazili ste tabuľku s", input$numericID, "riadkami a boxplot pre atribút Ozone ste nastavili na farbu ", input$radioID)
print(vypis)
})
output$table <- renderTable(airquality)
output$distPlot <- renderPlot({
x <- airquality[,input$selectID]
boxplot(x~airquality$Month, col = input$radioID, border = 'white', main=input$textID)
})
})
})
# Define UI for application that draws a histogram
shinyUI(fluidPage(
# Application title
titlePanel("Dáta Airquality"),
# Sidebar with a slider input for number of bins
sidebarPanel(
numericInput("numericID","PoÄet riadkov tabuľky",value=6, min=1, max=100, step=5),
selectInput("selectID","Vyberte atribút",choices=c(colnames(airquality))),
radioButtons("radioID","Vyberte farbu grafu", choices=c("yellow","green")),
textInput("textID","Zadajte nadpis grafu", value ="Nadpis"),
actionButton("gobutton","Start")
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot"),
tableOutput("table"),
textOutput("textik")
)
))
#Tomáš - I would recommend avoiding putting your output statements inside of observeEvent. Instead, I would create an eventReactive that will be triggered by your action button. And when this happens, it will store all your needed info in a list, and all of your outputs will be dependent on this list. Here is one way to do this (below is the server function only).
server <- function(input, output) {
aq_data <- eventReactive(input$gobutton, {
list(data = airquality, nID = input$numericID, rID = input$radioID, sID = input$selectID, tID = input$textID)
})
output$textik <- renderText({
vypis=c("Zobrazili ste tabuľku s", aq_data()[["nID"]], "riadkami a boxplot pre atribút Ozone ste nastavili na farbu ", aq_data()[["rID"]])
print(vypis)
})
output$table <- renderTable(tail(aq_data()[["data"]], aq_data()[["nID"]]))
output$distPlot <- renderPlot({
dat <- aq_data()
boxplot(reformulate("Month", dat[["sID"]]), col = dat[["rID"]], border = 'white', main = dat[["tID"]], data = dat[["data"]])
})
}
I recently ran into a problem that as my computing took lots of time, I'd like to show the text output on shiny rather than progress bar or loading message. My function looked like
printText <- function() {
for(i in 1:10){
Sys.sleep(0.1)
print(paste("My text", i))
y = i + 1
}
return(y)
}
I can print it with verbatimTextOutput but I also need the returned value of y. Now I am doing this:
runApp(list(
ui = shinyUI(fluidPage(
titlePanel("Print consol output"),
sidebarLayout(
sidebarPanel(actionButton("go", "Go")),
mainPanel(
verbatimTextOutput("text")
)
)
)),
server = shinyServer(function(input, output, session){
observeEvent(input$go, {
output$text <- renderPrint({
y <- printText()
})
})
})
))
The problem is that if I want to use the returned y I need to create a reactive object, which may take me 2 times longer because I execute printText() twice, while printing and pass to reactive object.
How could I get the value of y and print the text onto shiny without duplicated work? Notice that I'm not gonna use progress bar because my real function is not loop actually. What I want is to capture the text output during the process and get returned value.
You can do this way :
ui.r
textOutput("id")
server.r
output$id<-renderText(input$y)
I am constructing an animated graph project using R Studio's Shiny. Currently the "Go !" button initiates the animation. I would like to have the "Reset" button re-initialize the variables and re-run the animation, but since Shiny does not allow within-code changes to the input$button values, I am stuck on how to do this. The real project is similar in form to the sample blocks below, but much more involved. Animation is integral to the information being conveyed. When the project is completed, I intend to deploy it on the Shiny server, so I would like users to be able to re-run the animation with different selections without having to re-open the link.
# ui.R
library(shiny)
shinyUI(fluidPage(
# Application title
headerPanel("Cost Explorer"),
sidebarPanel(
actionButton("goButton", "Go!"),
actionButton("reset", "Reset"),
sliderInput("myvar", label=h6("Variability of cost"),
min=0, max=50, value=10)
),
mainPanel(
plotOutput(outputId="tsplot")
)
))
# server.R
library(shiny)
shinyServer(function(input, output, session) {
# initialize reactive values
ts <- reactiveValues(cost=rep(NA,100), year=(2010:2109), counter=1)
output$tsplot <- renderPlot({
plot(ts$year, ts$cost, xlim=c(2010,2110), ylim=c(-200,200), xlab="Year",
ylab="Cost (US Dollars)", type="l", main="Forecasted Cost Time series")
})
observe({
isolate({
if (ts$counter==1){
ts$cost[ts$counter]=50 #initial cost
}
if (ts$counter > 1){
ts$cost[ts$counter]=ts$cost[ts$counter-1]+rnorm(1,0,input$myvar)
}
ts$counter=ts$counter+1
})
if (((isolate(ts$counter) < 100)) & (input$goButton > 0)){
invalidateLater(200, session)
}
if (input$reset > 0){
# How do I add reset functionality?
}
})
})
Based on your app it was quicker to add another observe and reset the counter to 1 using the global assignment operator <<-. Also I changed the plot so it is plots indexed variables. Have a look at similar problem people had, here. NB: In some of my apps I also have the pause button when a user presses the start button twice, you can achieve this by checking if the button index is divisible by two or not since every time the button is clicked it increments by one.
I was further looking into your app, make sure you are garbage collecting unreferenced observers, as you might run out of memory (look at the memory profile via Task manager). Look into this example here, alternately you can set-up a log-off functionality per session where the client will be logged off after n amount of minutes.
rm(list = ls())
library(shiny)
ui <- (fluidPage(
# Application title
headerPanel("Cost Explorer"),
sidebarPanel(
actionButton("goButton", "Go!"),
actionButton("reset", "Reset"),
sliderInput("myvar", label=h6("Variability of cost"),min=0, max=50, value=10)
),
mainPanel(plotOutput(outputId="tsplot"))
))
server <- (function(input, output, session) {
# initialize reactive values
ts <- reactiveValues(cost=rep(NA,100), year=(2010:2109), counter=1)
output$tsplot <- renderPlot({
plot(ts$year[1:ts$counter], ts$cost[1:ts$counter], xlim=c(2010,2110), ylim=c(-200,200), xlab="Year",
ylab="Cost (US Dollars)", type="l", main="Forecasted Cost Time series")
})
observe({
isolate({
if (ts$counter==1){
ts$cost[ts$counter]=50 #initial cost
}
if (ts$counter > 1){
ts$cost[ts$counter]=ts$cost[ts$counter-1]+rnorm(1,0,input$myvar)
}
ts$counter=ts$counter+1
})
if (((isolate(ts$counter) < 100)) & (input$goButton > 0)){
invalidateLater(200, session)
}
})
observe({
if (input$reset > 0){
ts$counter <<- 1
}
})
})
runApp(list(ui = ui, server = server))