R Shiny : Refreshing plot when entering input OR pressing an action button - r

I'm fairly new to R Shiny but am stuck with the following problem for some time now and hope you can provide some help:
I try to refresh a plot in R shiny that should be refreshed if either a new input argument is entered OR an action button is pressed. This should be straightforward, but unfortunately I can't solve it, despite googling/reading instructions for some time. Any advice would be recommended. Any solutions on the web seem to put the whole renderplot function inside the observeEvent function, but I also need the renderplot in addition outside of it to account for the possibility of just entering inputs without pressing the action button.
I have no trouble creating a (render)plot that either exclusively is refreshed when entering a new input or exclusively refreshed when pressing a button.
However when doing both at the same I fail: I first tried to copy the renderplot function including the resulting output twice one time within an observeEvent function (to account for clicking the action button) and one time outside of an observeEvent (to account for only refreshing the inputs to the plot) but this leads only to a greyed out graph that refreshes after ~10 seconds delay when pressing the action button. I imagine adding the reactive click input generated from clicking the action button directly to the renderplot outside of observe event , but so far I couldn't get it to run. Any recommendations would be much appreciated.
Thank you in advance.

Like this?:
Edit: No need to pass the selectInput to the reactive Vals.. this does the same:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId="select", label="title", choices=LETTERS[1:3], selected = "A"),
actionButton(inputId="btn", label="refresh")
),
mainPanel(
plotOutput("scatterPlot")
)
)
)
server <- function(input, output) {
plotSettings <- reactiveValues()
observeEvent(c(input$btn, input$select), {
plotSettings$values <- runif(100,1,100)
# plotSettings$title <- input$select
}, ignoreNULL = FALSE)
output$scatterPlot <- renderPlot({
plot(plotSettings$values, main=input$select)
})
}
shinyApp(ui = ui, server = server)

Related

In Shiny, the submit button isn't running

I wrote a R script (MAIN.R) that converts PDF tables to CSV. When I run MAIN.R as an individual file, it functions well. I've tried it many times.
Currently, I'm working on a R shiny app that uses "MAIN.R" as a source and takes a pdf file as input. When I push the submit button, the output should appear in the MAIN panel. Unfortunately, the submit button does not function as intended.
May anyone please assist me with this, as I am new to Shiny?
UI.R
shinyUI(fluidPage(
titlePanel("DATASET CONVERSION"),
sidebarLayout(
fileInput("filein", label = h2("Select a file to convert.")),
submitButton("Submit")
),
mainPanel(
tableOutput("Dataset")
)
)
)
Server.R
source("MAIN.R")
shinyServer(function(input, output) {
outputdf <- reactive({ input$filein
})
output$Dataset <- renderTable({
outputdf()
})
})
Your Submit button is not currently linked to anything, so it will not do anything. If I am reading the code right, you are just taking the input dataset and storing it as the output of outputdf. Your output$Dataset then just picks up that outputdf and displays it as-is, without any work being done on it.
You use an action button like so:
## In UI.R
actionButton("execute", "Execute the Main Function")
## In Server.R
observeEvent(input$execute, {
## Do stuff here
})
Note that the actionButton has two parameters, inputID (which is how you refer to it) and text to display on top. For example, with input$filein, 'filein' is the inputID.
In Server.R, observeEvent won't do anything until it detects a change in input$execute, which happens when someone clicks the button. That is where you put your code to do stuff.
Now, in output$Dataset, you need to access the results of whatever you did in that observeEvent. One way to do that is to use a reactiveValue. This is just like a reactive, but instead of a function, it stores a data element. Initialize it as an empty dataframe, and then update it in the observeEvent. Something like this:
## In Server.R
treated_output <- reactiveValue(data.frame())
observeEvent(input$execute, {
## Run the function on the file
updated <- main_function(input$filein)
# Update your reactiveValue
treated_output(updated)
})
output$Dataset <- renderTable({
treated_output()
})
Does this make sense?

Shiny observeEvent shows first event, but not second, third

TL;DR: observeEvent is only working on the first instance, but not subsequent.
Explanation:
In a small shiny app below, I dynamically build a URL based on user input, which points to the corresponding pre-rendered "timer" gif images I have hosted on GH. In this shiny app code below, observeEvent works great to pull the sliderInput value (e.g. 5sec), build a URL, and then clicking 'go' will show the timer using a clever shinyjs package. However, if I do not change the input (still 5sec), clicking go doesn't rebuild the image. If I change the value to a different number (4sec), it will show the correct gif. If I change back to the original number (5sec), no gif. If I go to a new number (3sec), correct gif. If I print the value of input$time or of rv$time in my observeEvent, then each of those values are updating correctly (both print the corresponding value).
Goal: to show the gif that corresponds to the input$time upon each update of input$go
Reprex:
library(shiny)
library(tidyverse)
library(glue)
library(shinyjs)
# Define UI
ui <- navbarPage(title = "Reprex",
## RHYME TIME -----
tabPanel("Time",
useShinyjs(),
sidebarLayout(
sidebarPanel(
sliderInput("time",
"Seconds",
min = 3,
max = 10,
value = 5
),
actionButton(inputId = "go",
label = "Go"),
),
mainPanel(
HTML("<center>"),
shinyjs::hidden(htmlOutput("simple_timer")),
HTML("</center>")
)
)
)
)
# Define server logic
server <- function(input, output, session) {
#a container to store my time var
rv <- reactiveValues(
time = 0
)
#the event that is triggered by input$go getting clicked
observeEvent(input$go, {
rv$time <- input$time #this should update rv$time after go is clicked
shinyjs::show("simple_timer") #this is the clever package with simple show/hide funs
})
#the reactive text that generates my HTML
output$simple_timer <- renderText({
glue::glue('<img src ="https://github.com/matthewhirschey/time_timer/raw/main/data/{rv$time}_sec.gif",
align = "center",
height="50%",
width="50%">')
})
}
# Run the application
shinyApp(ui = ui, server = server)
There are a couple of issues with your code:
renderText won't refire if you press input$go again (w/o changing the slider). Becasue the idea is that observer/render fires whenever their reactives change. As your renderText depends on rv$time which does not change when input$time does not change, the render function is not fired on subsequent button presses. This can be remedied by including input$go in the render function setting an additional dependency on the button.
This will not, however, solve your problem, because the browser uses caching. It sees that the <img> tag did not change (same src), thus it does not reload the picture. To circumvent that you can use the trick from Disable cache for some images by adding a timestamp to the src.
To make a long story short, this code does the trick:
output$simple_timer <- renderText({
input$go # make code dependent on the button
# add `?timestamp=<timestamp>`to the src URL to convince the browser to reload the pic
glue::glue('<img src ="https://github.com/matthewhirschey/time_timer/raw/main/data/{rv$time}_sec.gif?timestamp={Sys.time()}",
align = "center",
height="50%",
width="50%">')
})

R Shiny: Is there a way to check if a button is disabled using shinyjs package?

Is there a way to check if a download button is disabled using the shinyjs R package? I want to use shinyjs or something similar because they have very easy syntax. This is my package:
server.R:
library(shiny)
library(shinyjs)
library(shinyBS)
shinyServer(function(input, output) {
observe({
shinyjs::disable("download1")
if(shinyjs::is.disabled("download1")){ ## This is what I am trying to do
# Do something
}
})
})
ui.R
shinyUI(fluidPage(
downloadButton("download1")
))
Not directly (well, not easily*).
Buttons can only be disabled when you decide to disable them, so you can have some sort of a reactive variable that holds whether or not the button should be disabled, and whenever you disable the button, you also change the value of that variable. In order to make sure they stay in sync, every time you want to disable the button you can set the variable to mirror that, and then you can use shinyjs::toggleState(condition = variable) so that the disabled state will mirror what the variable says.
Example code to illustrate what I mean:
library(shiny)
ui <- fluidPage(
shinyjs::useShinyjs(),
numericInput("num", "When divisible by 3, disable the button", 1),
actionButton("btn", "button")
)
server <- function(input, output, session) {
values <- reactiveValues(disable = FALSE)
observe({
values$disable <- (input$num %% 3 == 0)
})
observe({
shinyjs::toggleState("btn", condition = !values$disable)
})
}
shinyApp(ui = ui, server = server)
In this app, whenever you want to disable the button, simply set values$disable to FALSE and to enable the button set it to TRUE. To check if the button is currently on or off at any point in time, you can look at the value of values$disable.
*I'm guessing that you wanted a more direct approach to ask the app a question in real time "hey shiny, is button X currently disabled?". You can do that, but it would involve writing custom javascript code to check for the button state, and for custom code to ask javascript that question and to listen for its response. This would work and be guaranteed to be correct, but it's likely overkill for most cases.

Creating a responsive actionButton for the user selcted checkboxes generated on the fly using dynamicUI [shiny/R]

I am stuck in a small problem related to shiny/R.
I am reading in a text file and displaying selective column names returned by grep search into the shiny application on the fly. For this, I am using the dynamicUI.
After the file is read in, the following function runs in server.R. It checks for specific colnames and displays this on the UI using uiOutput. Whatever column names are selected by the user, they are sent to another function for data processing and the plot it returned on the mainPanel.
server.R
output$Bait <- renderUI({
data <- input.data();
if(is.null(data)) return()
colnames <- names(data)
colnames = colnames[grep("*LFQ*",colnames,ignore.case=TRUE)]
# Creating the checkboxes using the above colnames
checkboxGroupInput("bait", "Choose Bait LFQ columns",
choices = colnames,
selected = colnames)
})
ui.R
shinyUI(
sidebarPanel(
uiOutput("Bait"),
),
mainPanel(
plotOutput(outputId = 'plot'),
)
)
Everything is fine, what I am trying to create is an action button for the checkboxes. Some files are big and have a longer list of column names >60, so whenever a checkbox is clicked, the whole function runs for processing and displays a plot. This gets unnecessary when the user has to deselect/select more than 10 columns.
An easy fix is, I kept selected=NULL but what I want is to add an actionButton after the checkboxGroupInput, so that user can select as many as checkBoxes but the function only runs when the GO button is pressed via the actionButton. If add a actionButton control after the checkbocGroupInput, it doesnt' works.
Can someone guide me in this regard. After working on this for sometime, now I am bit lost.
Thanks
Did you look into ?isolate? Lets say i want function initialFunction() only be evaluated if input$actionButtonis clicked.
observe({
input$actionButton # everything that triggers initialFunction() should come before isolate()
isolate({
# everything that should not trigger initialFunction() should come inside isolate()
initialFunction()
})
})

Can I save the old value of a reactive object when it changes?

Note: After coming up with the answer I reworded the question to make if clearer.
Sometimes in a shiny app. I want to make use of a value selected by the user for a widget, as well as the previous value selected for that same widget. This could apply to reactive values derived from user input, where I want the old and the new value.
The problem is that if I try to save the value of a widget, then the variable containing that value has to be reactive or it will not update every time the widget changes. But, if I save the the value in a reactive context it will always give me the current value, not the previous one.
How can I save the previous value of a widget, but still have it update every time the user changes the widget?
Is there a way that does not require the use of an actionButton every time the user changes things? Avoiding an actionButton can be desirable with adding one is otherwise unnecessary and creates excess clicking for the user.
Seeing as the session flush event method seems to be broken for this purpose, here is an alternative way to do it using an observeEvent construct and a reactive variable.
library(shiny)
ui <- fluidPage(
h1("Memory"),
sidebarLayout(
sidebarPanel(
numericInput("val", "Next Value", 10)
),
mainPanel(
verbatimTextOutput("curval"),
verbatimTextOutput("lstval")
)
)
)
server <- function(input,output,session) {
rv <- reactiveValues(lstval=0,curval=0)
observeEvent(input$val, {rv$lstval <- rv$curval; rv$curval <- input$val})
curre <- reactive({req(input$val); input$val; rv$curval})
lstre <- reactive({req(input$val); input$val; rv$lstval})
output$curval <- renderPrint({sprintf("cur:%d",curre())})
output$lstval <- renderPrint({sprintf("lst:%d",lstre())})
}
options(shiny.reactlog = TRUE)
shinyApp(ui, server)
Yielding:
Update This answer was posted before the advent of the reactiveValues/observeEvent model in shiny. I think that #MikeWise 's answer is the better way to do this.
After some playing around this is what I came up with. The ui.r is nothing special
ui.r
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId="XX", label="Choose a letter",choices=letters[1:5])
),
mainPanel(
textOutput("Current"),
textOutput("old")
)
)
))
"Current" will display the current selection and "old" displays the previous selection.
In the server.r I made use of three key functions: reactiveValues, isolate and session$onFlush.
server.r
library(shiny)
server <- function(input, output,session) {
Values<-reactiveValues(old="Start")
session$onFlush(once=FALSE, function(){
isolate({ Values$old<-input$XX })
})
output$Current <- renderText({paste("Current:",input$XX)})
output$old <- renderText({ paste("Old:",Values$old) })
}
The server.r works like this.
First, Values$old is created using the reactiveValues function. I gave it the value "Start" to make it clear what was happening on load up.
Then I added a session$onFlush function. Note that I have session as an argument in my server function. This will run every time that shiny flushes the reactive system - such as when the selectizeInput is changed by the user. What is important is that it will run before input$XX gets a new value - so the value has changed at the selectizeInput but not at XX.
Inside the session$onFlush I then assign the outgoing value of XX to Values$old. This is done inside an isolate() as this will prevent any problems with input$XX gets updated with the new values. I can then use input$XX and Values$old in the renderText() functions.

Resources