I am trying to display some plots using renderplot(), and when i update y plots, it stays how it was before...
Let's have a look on my code:
UI.R
...
uiOutput("firstGraph")
uiOutput("seconfGraph")
uiOutput("thirdGraph")
...
server.R
observeEvent({input$CalcBtn,
output$firstGraph <- renderUI ({
if (dataTab$tabOne != 0) {
plotOutput(plotsValue$graphOne)}
}))
observeEvent({input$CalcBtn,
output$secondGraph <- renderUI ({
if (dataTab$tabTwo != 0) {
plotOutput(plotsValue$graphTwo)}
}))
observeEvent({input$CalcBtn,
output$thirdGraph <- renderUI ({
if (dataTab$tabThree != 0) {
plotOutput(plotsValue$graphThree)}
}))
output$graphPlot121 <- renderPlot({...})
Here, input$CalcBtn is a button that makes a calculation and "refresh" the page. dataTab$tabOne (dataTab$tabTwo and dataTab$Three are approximately the same as dataTab$tabOne) is determinated by a function that reads an argument and transform it into a number. 0 if it must not print the plot and a number like 121 if it must print the plot 121. in plotsValue, the graphOne, graphTwo and graphThree are character strings made before using the value of dataTab, so it looks like "graphPlot121". So when I click the button, the programm makes everything great, expect displaying a second time the plot when I modify the inputs used inside the renderPlot.
The programm works well when I change from one plot to the other, but the problem comes when I try to display twice the same.
Please, help me if you know how to refresh the plot when I update it.
Related
I have some code I am trying to write in Shiny. This involves the following steps:
- choosing a date
- checking whether a valid input file exists for the date
- if a valid input file exists, pulling down remote time series ending on that date
- carrying out calculations
- plotting the result of the calculations
The date checking should be done reactively. However, the dime series pull down and calculations take some time. I therefore want this to be done only with a button press.
I have most of this working. However, whilst I can put the first instance of the calculation off until a button press using
"if(input$run_shiny_risk==0){
} else {
#some code
}
I can't stop it from calculating automatically on subsequent instances. In other words, as soon as a valid date is chosen, calculations start automatically. I have tried to isolate the calculations without success. I have also tried to isolate a dummy variable that is driven by the button value, but again no luck. Here is what I have in terms of code:
library(shiny)
ui <- fluidPage(
titlePanel("Risk Dashboard"),
sidebarLayout(
sidebarPanel(
helpText("Effective date"),
dateInput("shiny_end_date",
label = "Date (yyyy-mm-dd):",
value = "2018-12-31"),
actionButton("run_shiny_risk",
label = "Run risk report:"),
textOutput("selected_var")
),
mainPanel(
plotOutput("selected_var1")
)
)
)
server <- function(input, output) {
shiny_risk_test <- 0
output$selected_var <- renderText({
holdings_data_file <-paste(substr(input$shiny_end_date,3,4),substr(input$shiny_end_date,6,7),substr(input$shiny_end_date,9,10),"_Data.csv",sep="")
if(file.exists(holdings_data_file)){
end_date <- input$shiny_end_date
paste("You have selected", end_date)
} else {
"No such file"
}
})
output$selected_var1 <- renderPlot({
holdings_data_file <-paste(substr(input$shiny_end_date,3,4),substr(input$shiny_end_date,6,7),substr(input$shiny_end_date,9,10),"_Data.csv",sep="")
if(file.exists(holdings_data_file)){
if(input$run_shiny_risk==0){
#this stops the chart from being drawn initially...
} else {
plot_data <- cbind(c(1,2,3,4),c(1,2,3,4))
p<-plot(plot_data)
p
}
} else {
}
})
}
shinyApp(ui = ui, server = server)
So, this works until the first click. I can change dates between valid and invalid ones, and no chart is drawn until I hit the button. Then, if I move to an invalid date, the chart vanishes, which is right. But if I go back to a valid date, the chart appears again (and would be recalculated if I had the actual, lengthy code enclosed). And code that just stops calculations until the button is pressed, not only the first time but in subsequent case, would be great. Thank you!
I solved this problem in a previous application via the use of reactive values. Consider the following example:
server <- function(input, output, session) {
output_staging <- reactiveValues()
output_staging$title <- NA
observeEvent(input$updateButton,{ update_title() })
update_title <- function(){
output_staging$title <- paste0("random number ",runif(1))
}
output$title <- renderText(output_staging$title)
}
The renderText output looks at the reactive value stored in output_staging$title. Each time the button is clicked, an update function is called that replaces the reactive value.
Note that I could merge the function update_title and the observeEvent command. But having them separate gives me more flexibility.
In your context, I would recommend separating the data prep and the plot generation in order to use this method. You can use the reactive values at either place:
Button sends data to reactiveValue: data --> reactive --> make plot --> display plot
Button sends plot to reactiveValue: data --> make plot --> reactive --> display plot
But you don't need to use it at both.
I'm trying to update a text box based on the click events in multiple graphs. I use the observeEvent function to trigger the update of the text box. A snippet of the code is shown below.
observeEvent({
input$plot1_click
input$plot2_click
input$plot3_click
...
}, {
# only need the invalidated input
invalid_input <- which.invalid(input$plot1_click,
input$plot2_click,
input$plot3_click,
...)
updateTextInput(session,
"textbox",
label = NULL,
value = invalid_input)
})
Currently the updateTextInput function will run whenever there's a click in any of the plots, which is desired, but I can't figure out how to capture which plot is clicked last and should be used to update the text input. Is there a function to check which input is invalidated from a list of inputs?
Hi I think the easiest way is to make a separate observer for each input. This way the input that was changed last will always processed last. You can put it in a lapply if you don't want to bloat you code with repetitive code. It could look something like this
lapply(1:3, function(idx){
observeEvent({input[[paste0("plot",idx,"_click")]]},
{updateTextInput(session,
"textbox",
label = NULL,
value = input[[paste0("plot",idx,"_click")]])})
})
Hope this helps!
I want two different events to trigger an update of the data being used by various plots / outputs in my app. One is a button being clicked (input$spec_button) and the other is a point on a dot being clicked (mainplot.click$click).
Basically, I want to listed for both at the same time, but I'm not sure how to write the code. Here's what I have now:
in server.R:
data <- eventReactive({mainplot.click$click | input$spec_button}, {
if(input$spec_button){
# get data relevant to the button
} else {
# get data relevant to the point clicked
}
})
But the if-else clause doesn't work
Error in mainplot.click$click | input$spec_button :
operations are possible only for numeric, logical or complex types
--> Is there some sort of action-combiner function I can use for the mainplot.click$click | input$spec_button clause?
I know this is old, but I had the same question. I finally figured it out. You include an expression in braces and simply list the events / reactive objects. My (unsubstantiated) guess is that shiny simply performs the same reactive pointer analysis to this expression block as to a standard reactive block.
observeEvent({
input$spec_button
mainplot.click$click
1
}, { ... } )
EDIT
Updated to handle the case where the last line in the expression returns NULL. Simply return a constant value.
Also:
observeEvent(c(
input$spec_button,
mainplot.click$click
), { ... } )
I've solved this issue with creating a reactive object and use it in event change expression. As below:
xxchange <- reactive({
paste(input$filter , input$term)
})
output$mypotput <- eventReactive( xxchange(), {
...
...
...
} )
Here's the solution I came up with: basically, create an empty reactiveValues data holder, and then modify its values based on two separate observeEvent instances.
data <- reactiveValues()
observeEvent(input$spec_button, {
data$data <- get.focus.spec(input=input, premise=premise,
itemname=input$dropdown.itemname, spec.info=spec.info)
})
observeEvent(mainplot.click$click, {
data$data <- get.focus.spec(input=input, premise=premise, mainplot=mainplot(),
mainplot.click_focus=mainplot.click_focus(),
spec.info=spec.info)
})
This can still be done with eventReactive by putting your actions in a vector.
eventReactive(
c(input$spec_button, mainplot.click$click),
{ ... } )
The idea here is to create a reactive function which will execute the condition you want to pass in observeEvent and then you can pass this reactive function to check the validity of the statement. For instance:
validate_event <- reactive({
# I have used OR condition here, you can use AND also
req(input$spec_button) || req(mainplot.click$click)
})
observeEvent(validate_event(),
{ ... }
)
Keep Coding!
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
I am trying to get values from inputs with IDs like "imp1", "imp2", "imp3",...
There's no limit to the amount of 'imps' so it cannot be done manually.
What i thought is that i could make a list called "impIDList" that gets the amount of 'imps' and assign a String to each entry of the list as "imp1", "imp2",...
The problem is server.R doesn't use the String's to process the inputs but it uses only the imp1, or imp2 or imp3... without the quotes.
That happens when i get to:
impIDList<-for(i in 1:numImp){
impID<-paste("imp",toString(i))
imps[[i]]<-input$impID
}
because impID (3rd line) is "imp1", not to imp1.
ui.R
(...)
output$imps<-renderDataTable({
numImp<-(input$num_imp)
imps<-list()
impIDList<-for(i in 1:numImp){
impID<-paste("imp",toString(i))
imps[[i]]<-input$impID
}
})
(...)
If more information is needed (or the question is not in the correct format...) please vote down , but at least say something...I kind of need some help...
Ok, i solved it.
server.R uses inputs like " input$something " and creates outputs like " output$something ".
ui.R displays the outputs like xoutput("something"), and the inputs are created in the widget as, for example:
numericInput({
inputid="imp1",
label="blah blah"
)}
As i had inputs id's like "imp1", "imp2", "imp3",... and needed server.R to use them as input$imp1, input$imp2, input$imp3,... (without the quotes) i only used a parse inside a for loop. I saved the inputs in a list called "imps", another input called "num_imps" got the number of numericInputs.
The code looks like
v_imps<-reactive({
numImp<-(input$num_imp)
imps<-list(c("a","b"))
for(i in 1:numImp){
impID<-paste("imp",toString(i),sep = "")
imps[[1]][i]<-eval(parse(text = paste("input$",impID,sep = "")))
}
imps
})
I was going to put images of what i've done but i have not enough rep.
Thanks anyway