When using a range slider in a shiny app, can you require a minimum range of selected values? I am using the sliderTextInput() function in the shinyWidgets package, but think this is general to range sliders. Toy example code:
testx=1:150
testy=1:150
library(shiny) # also requires shinyWidgets package be installed
ui <- fluidPage(
plotOutput("plot"),
shinyWidgets::sliderTextInput("range","Input Size:",
choices=c(1,25,50,100),
selected=c(25,50), grid = T)
)
server <- function(input, output) {
output$plot <- renderPlot({
plot(testx[input$range[1]:input$range[2]],testy[input$range[1]:input$range[2]],
xlim=c(0,150),ylim=c(0,150))
})
}
shinyApp(ui, server)
The issue I am trying to avoid is the one below, where both ends of a slider are set to the same value, which results in a single point being plotted--I'd like to require a range be selected.
You can update the values if the are the same:
testx=1:150
testy=1:150
library(shiny) # also requires shinyWidgets package be installed
library(shinyWidgets)
ui <- fluidPage(
plotOutput("plot"),
sliderTextInput("range","Input Size:",choices=sliderchoice,selected=c(25,50), grid = T)
)
server <- function(input, output,session) {
observeEvent(input$range,{
if(input$range[1] == input$range[2]){
updateSliderTextInput(session,"range",selected = c((input$range[1]-1),input$range[2]))
}
})
output$plot <- renderPlot({
plot(testx[input$range[1]:input$range[2]],testy[input$range[1]:input$range[2]],
xlim=c(0,150),ylim=c(0,150))
})
}
shinyApp(ui, server)
Related
In the below example code I reactively subset the mtcars dataframe inside the renderPlot() function. However, in my larger App with many render functions in the server section I am having to repeat the same rv$x[1:input$samples], etc., over and over in many places. How would I apply this subsetting instead "at the top", into the rv <- reactiveValues(...) function itself or equivalent "master function"? I tried subsetting inside the reactiveValues() and got the message "Warning: Error in : Can't access reactive value 'samples' outside of reactive consumer. Do you need to wrap inside reactive() or observer()?" I assumed incorrectly that the reactiveValues() function is a "reactive consumer".
If someone can answer this basic understanding question, please explain the logic for correctly subsetting "at the top" because I am getting very embarrassed by my repeated questions about Shiny reactivity.
library(shiny)
ui <- fluidPage(
sliderInput('samples','Nbr of samples:',min=2,max=32,value=16),
plotOutput("p")
)
server <- function(input, output, session) {
rv <- reactiveValues(
x = mtcars$mpg,
y = mtcars$wt
)
output$p <- renderPlot({plot(rv$x[1:input$samples],rv$y[1:input$samples])})
}
shinyApp(ui, server)
There are multiple ways you can handle this.
Here is one way to create new subset reactive values inside observe.
library(shiny)
ui <- fluidPage(
sliderInput('samples','Nbr of samples:',min=2,max=32,value=16),
plotOutput("p")
)
server <- function(input, output, session) {
rv <- reactiveValues(
x = mtcars$mpg,
y = mtcars$wt
)
observe({
rv$x_sub <- rv$x[1:input$samples]
rv$y_sub <- rv$y[1:input$samples]
})
output$p <- renderPlot({plot(rv$x_sub,rv$y_sub)})
}
shinyApp(ui, server)
I'd use reactiveValues only if you need them to be modified in different places.
reactive is shiny's basic solution for this:
library(shiny)
library(datasets)
ui <- fluidPage(
sliderInput(
'samples',
'Nbr of samples:',
min = 2,
max = 32,
value = 16
),
plotOutput("p")
)
server <- function(input, output, session) {
reactive_mtcars <- reactive({mtcars[1:input$samples,]})
output$p <- renderPlot({
plot(reactive_mtcars()$mpg, reactive_mtcars()$wt)
})
}
shinyApp(ui, server)
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)
When using a range slider in a shiny app, can you require a minimum range of selected values? I am using the sliderTextInput() function in the shinyWidgets package, but think this is general to range sliders. Toy example code:
testx=1:150
testy=1:150
library(shiny) # also requires shinyWidgets package be installed
ui <- fluidPage(
plotOutput("plot"),
shinyWidgets::sliderTextInput("range","Input Size:",
choices=c(1,25,50,100),
selected=c(25,50), grid = T)
)
server <- function(input, output) {
output$plot <- renderPlot({
plot(testx[input$range[1]:input$range[2]],testy[input$range[1]:input$range[2]],
xlim=c(0,150),ylim=c(0,150))
})
}
shinyApp(ui, server)
The issue I am trying to avoid is the one below, where both ends of a slider are set to the same value, which results in a single point being plotted--I'd like to require a range be selected.
You can update the values if the are the same:
testx=1:150
testy=1:150
library(shiny) # also requires shinyWidgets package be installed
library(shinyWidgets)
ui <- fluidPage(
plotOutput("plot"),
sliderTextInput("range","Input Size:",choices=sliderchoice,selected=c(25,50), grid = T)
)
server <- function(input, output,session) {
observeEvent(input$range,{
if(input$range[1] == input$range[2]){
updateSliderTextInput(session,"range",selected = c((input$range[1]-1),input$range[2]))
}
})
output$plot <- renderPlot({
plot(testx[input$range[1]:input$range[2]],testy[input$range[1]:input$range[2]],
xlim=c(0,150),ylim=c(0,150))
})
}
shinyApp(ui, server)
Quick question on conditionalPanel for shiny/R.
Using a slightly modified code example from RStudio, consider the following simple shiny app:
n <- 200
# Define the UI
ui <- bootstrapPage(
numericInput('n', 'Number of obs', n),
conditionalPanel(condition = "input.n > 20",
plotOutput('plot') ),
HTML("Bottom")
)
# Define the server code
server <- function(input, output) {
output$plot <- renderPlot({
if (input$n > 50) hist(runif(input$n)) else return(NULL)
})
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)
My objective is to hide the graph and move up the HTML text to avoid a gap. Now, you can see that if the entered value is below 20, the graph is hidden and the text "Bottom" is moved up accordingly. However, if the entered value is larger than 20, but smaller than 50, the chart function returns NULL, and while no chart is shown, the text "Bottom" is not moving up.
Question is: is there a way I can set a conditionalPanel such that it appears/is hidden based on whether or not a plot function returns NULL? The reason I'm asking is because the trigger a bit complex (among other things it depends on the selection of input files, and thus needs to change if a different file is loaded), and I'd like to avoid having to code it on the ui.R file.
Any suggestions welcome,
Philipp
Hi you can create a condition for conditionalPanel in the server like this :
n <- 200
library("shiny")
# Define the UI
ui <- bootstrapPage(
numericInput('n', 'Number of obs', n),
conditionalPanel(condition = "output.cond == true", # here use the condition defined in the server
plotOutput('plot') ),
HTML("Bottom")
)
# Define the server code
server <- function(input, output, session) {
output$plot <- renderPlot({
if (input$n > 50) hist(runif(input$n)) else return(NULL)
})
# create a condition you use in the ui
output$cond <- reactive({
input$n > 50
})
outputOptions(output, "cond", suspendWhenHidden = FALSE)
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)
Don't forget to add the session in your server function and the outputOptions call somewhere in that function.
I am trying to have multiple html outputs in my shiny App but it seems like it can only show one at a time.
My UI is:
# ui.R
shinyUI(
mainPanel(
tableOutput("view"),
plotOutput("view2")
))
And my server is:
# server.R
library(googleVis)
library(RMySQL)
shinyServer(function(input, output) {
datasetInput <- reactive({
"try2" = subset(try1, idCampaign == input$inputId)
})
output$view <- renderGvis({
gvisTable(datasetInput(),options=list(width=1000, height=270, col='blue'))
})
output$view2 <- renderGvis({
gvisScatterChart(datasetInput2())
})
})
in the output to view2 you use datasetInput2() , this should be datasetInput(). Here datasetInput() just represents a dynamic version of a dataframe, you can use it in as many functions as you want, there is no need to index it.
alternatively i think you can use the tabsetPanel to divide your main page into certain parts and assign output objects to each of your tabPanel.