This is my first post here so I hope I can explain my problem clearly. I am currently getting into shiny and want to start off by doing basic stuff. I decided to challenge myself by making an app that takes two numeric inputs and plots them whenever I press an actionbar. The problem is that I want to keep the previous points that I plot. I can't get it to work as it keeps resetting the plot. I've tried many different ways and I dont really know how points() works on shiny. Here is the code:
library(shiny)
ui <- fluidPage(
actionButton(inputId="execute",label="Execute"),
numericInput(inputId="numY",label="Y",value=0),
numericInput(inputId="numX",label="X",value=0),
plotOutput("plot")
)
server <- function(input, output) {
coordx <- eventReactive(input$execute,{input$numX})
coordy <- eventReactive(input$execute,{input$numY})
if(!exists("input$execute"))
{
output$plot <- renderPlot({
plot(x=coordx(),y=coordy())
})
}
else
output$plot <- renderPlot({
points(x=coordx(),y=coordy())
})
}
shinyApp(ui = ui, server = server)
Thank you in advance!
As the commenters #Limey and #fvall said, the issue seems to be the eventReactive() which overwrites each time. What I did instead is place the x and y coordinates in a reactiveValues(). Then I placed an observeEvent() for any time input$execute was pressed, writing the x and y coordinates to update the reactiveValues(). This will keep both the old and new values. I also added a little tableOutput() just to keep track of the values:
library(shiny)
ui <- fluidPage(
actionButton(inputId="execute",label="Execute"),
numericInput(inputId="numY",label="Y",value=0),
numericInput(inputId="numX",label="X",value=0),
plotOutput("plot"),
tableOutput("TABLE")
)
server <- function(input, output) {
coord<-reactiveValues("x" = NULL, "y" = NULL)
observeEvent(input$execute, {
req(input$numY, input$numX)
tempx<-c(isolate(coord$x), input$numX)
tempy<-c(isolate(coord$y), input$numY)
coord$x <- tempx
coord$y <- tempy
})
output$plot <- renderPlot({
req(input$execute)
plot(x=isolate(coord$x),y=isolate(coord$y))
})
output$TABLE<-renderTable({
data.frame("x" = coord$x, "y" = coord$y)
})
}
shinyApp(ui = ui, server = server)
Best of luck! Though I had hiccups along the way, I really enjoyed learning Shiny myself, and I hope you do too!
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 have a Shiny app where I have a dynamically created tabsetPanel where each tab contains a table. I do not know how many tabs/tables will be created in each session by users. I understand that it is bad practice to put render* functions inside observe or observeEvent calls but I can't think of any other way to do this. A minimal example of what I'm trying to do is shown below, which just picks a data set randomly to display on a given tab. Essentially, I'm trying to figure out how to call my table renderers without putting them inside an observe. More generally, although I have read it is bad practice to do this, I would also appreciate an explanation of exactly why it's not a good thing to do:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
numericInput("tabs", "Number of tabs", value = 5),
),
mainPanel(
uiOutput("mytabset")
)
)
)
server <- function(input, output) {
output$mytabset <- renderUI({
mytabs <- lapply(seq_len(input$tabs), function(x) {
tabPanel(
paste("Tab", x),
tableOutput(paste0("tab", x))
)
})
do.call(tabsetPanel, mytabs)
})
observe({
set.seed(1)
lapply(seq_len(input$tabs), function(x) {
output[[paste0("tab", x)]] <- renderTable({
sample(list(mtcars, iris, trees, cars), 1)
})
})
})
}
shinyApp(ui = ui, server = server)
I haven't used them in a while, but I think if you use modules, you can call them from outside of a reactive context, and won't need an observe..? :)
I'm trying to take my Shiny apps and break them into smaller files to make collaborating via git with coworkers much easier. This question helped me figure out how to source() in files to my server.r by using source(...,local=T). Now I'm trying to do the same thing with my UI layer.
Consider this toy Shiny app:
library(shiny)
ui <- bootstrapPage(
plotOutput("test"),
numericInput("n","Number of points",value=100,min=1)
)
server <- function(input, output, session) {
output$test = renderPlot({
x = rnorm(input$n)
y = rnorm(input$n)
plot(y~x)
})
}
shinyApp(ui, server)
This app does what you would expect, one overly-wide graph of 100 random data points. Now, what if I want to move just the plotOutput to a separate file (the real use case is in moving whole tabs of UI to separate files). I make a new file called tmp.R and it has:
column(12,plotOutput("test"),numericInput("n","Number of points",value=100,min=1))
The reason for wrapping it in the column statement is because the comma's can't just be hanging out. Now I update my UI to:
library(shiny)
ui <- bootstrapPage(
source("tmp.R",local=T)
)
server <- function(input, output, session) {
output$test = renderPlot({
x = rnorm(input$n)
y = rnorm(input$n)
plot(y~x)
})
}
shinyApp(ui, server)
Now, the word "TRUE" is just hanging out at the bottom of the page.
How do I eliminate this word from showing up? Why is it there?
Try source("tmp.R",local = TRUE)$value maybe
I am hoping to get some clarity on Shiny's reactivity behavior using the simplified code below as example.
When y is updated in the app, the graph updates.
When x is updated in the app, the graph does NOT update.
I have read Shiny's tutorials and my understanding is that given that I have wrapped both test() and plot() functions in observeEvent, both parameters should not cause the graph to update when changed.
Can someone help explain the logic behind this?
library(shiny)
test <- function(x){x*2}
shinyServer(function(input, output, session) {
observeEvent(input$run, {
x = test(input$x)
output$distPlot <- renderPlot({
if(input$y){
x = x+2
}
plot(x)
})
})
})
shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
numericInput("x", "x:", 10),
checkboxInput("y", label = "y", value = FALSE),
actionButton("run", "run")
),
mainPanel(
plotOutput("distPlot")
)
)
))
If you put the line x = test(input$x) inside of the renderPlot it will react when either x or y changes. Essentially the observer creates a reactive output when the action button is clicked the first time, then you simply have a reactive element that responds to changes to inputs inside of it. Hope that helps.
To make it so the graph only updates when the button is clicked, you will probably need to put the data that is being graphed in a eventReactive and use that as the input for the graph.
Something like this:
data <- eventReactive(input$run, {
x = test(input$x)
if(input$y){
x = x+2
}
x
})
output$distPlot <- renderPlot({
plot(data())
})
In my Shiny App, there are a few numericInput and selectInput.
Shiny updates outputs during typing, especially when users type is slower in the numericInput.
sumbitButton could you be used to stop automatically updading. But I prefer to not to use it.
How could I let Shiny waits for a longer time for numericInput?
Thanks for any suggestion. Let me know if my question is not clear.
You can use debounce on the reactive function that uses your Inputs.
Setting it to 2000 milliseconds felt OK to me.
If you use the input directly in a render function you might need to create the data to use in your render function in a reactive function.
An example is here: https://shiny.rstudio.com/reference/shiny/latest/debounce.html
## Only run examples in interactive R sessions
if (interactive()) {
options(device.ask.default = FALSE)
library(shiny)
library(magrittr)
ui <- fluidPage(
plotOutput("plot", click = clickOpts("hover")),
helpText("Quickly click on the plot above, while watching the result table below:"),
tableOutput("result")
)
server <- function(input, output, session) {
hover <- reactive({
if (is.null(input$hover))
list(x = NA, y = NA)
else
input$hover
})
hover_d <- hover %>% debounce(1000)
hover_t <- hover %>% throttle(1000)
output$plot <- renderPlot({
plot(cars)
})
output$result <- renderTable({
data.frame(
mode = c("raw", "throttle", "debounce"),
x = c(hover()$x, hover_t()$x, hover_d()$x),
y = c(hover()$y, hover_t()$y, hover_d()$y)
)
})
}
shinyApp(ui, server)
}