I know this question has been addressed multiple times and I've researched those instances that I've found, but have not found a solution that works.
I'm using an actionButton() as a Run button-- the intent is to not re-run the simulation unless the actionButton is clicked. I've used observe({ }) and isolate({ }) as I've seen discussed here, but still have several problems. I'll discuss the problems, then include the code below. The app is in early stages presently, and ONLY the second simulation on the drop down menu is partly implemented, so after starting the app select "Dorriefish growth/reproduction tradeoffs" from the selectInput() menu. Don't be concerned by any other rough spots at this time.
When the app begins, the run button behaves as expected in the sense that moving the top two sliders (S and p) does not cause the simulation to run or display output in a separate graphics device (reps.df == 1 is a special case requiring a dev.new()). At this point, input$runSim (the run button) == 0 because it has not been clicked yet.
First problem: when the run button is clicked, the graphics device opens and the simulation runs as expected, then ANOTHER graphics device opens and the same simulation displays again. Be patient while the first simulation display finishes-- a second will open on top of it. When those are closed (by clicking on their window controls), the next time the run button is pressed the same simulation is displayed a third time, then another graphics device opens and displays a new simulation run. If those graphics devices are closed, subsequent clicks on the run button behave as expected, opening one graphics device and displaying one, unique simulation run each time. Why do the first two clicks on the run button produce TWO graphics devices and repeat the first simulation run three times? (Pounds head on desk.)
Second problem: after the first time the run button is pushed, input$runSim increments and thereafter any movement of the S and p sliders causes a new graphics device to open and display a new simulation run. I don't want this behavior-- I need the simulation to display ONLY when the run button is clicked, not automatically when other input parameters are changed. I want the nominal behavior of the submitButton(), but I've read online that an actionButton with observe and isolate is the better practice. For the life of me I just cannot get it to isolate the run button function from the other sliders changing when input$runSim is greater than zero, i.e. after the first time it is clicked. There does not appear to be any obvious way to reset the value of that actionButton variable back to zero after a simulation run has been displayed, so after that first click the button no longer prevents new simulations whenever any other input variable is updated. (Pounds head some more.)
In summary: 1) why do the first two clicks on the run button each cause two graphics device windows to open, and why do they display the first simulation run three times? 2) How can I make the simulation run and display ONLY when the run button is clicked after the first time it is clicked?
Here is my (very preliminary) code:
ui.r
# filename: ui.r
# Michael Camann, Humboldt State University, 2014
################################################################################
#
# Define the user interface
#
################################################################################
library(shiny)
shinyUI(
fluidPage(
titlePanel("BIOL 330 ecological simulations"),
sidebarLayout(
sidebarPanel("",
helpText(HTML("<h3 style='text-align:center;'>Control Panel</h3>"), align='center'),
tags$hr(style='height:2px; border-width:0; color:gray; background-color:gray'),
# choose a simulation from a drop down menu
selectInput("sim", HTML("<b>Select a simulation to explore:</b>"),
# list the simulations available
c("No simulation selected (Introduction)" = "none",
"Dorriefish growth/reproduction trade-offs" = "dorriefish",
"Optimal foraging" = "optfor")
),
tags$hr(style='height:2px; border-width:0; color:gray; background-color:gray'),
conditionalPanel(condition="input.sim=='dorriefish'",
helpText(HTML("<b>Simulation model parameters:</b>")),
sliderInput("S.df", label=div(HTML("Specify <em>S</em>, the switch point mass (g) for
transition from somatic growth to reproduction:")),
min = 1, max = 50, value = 10, step=5),
sliderInput("p.df", label=div(HTML("Specify <em>p</em>, the probability of mortality
by predation in any given time step:")),
min = 0, max = 1, value = 0.12, step=0.01),
sliderInput("reps.df", label=div(HTML("Specify the number of full simulations to
run:")), min = 1, max = 100, value = 1, step=1)
),
# bottom controls common to all panels
conditionalPanel(condition="input.sim!='none'",
tags$hr(style='height:2px; border-width:0; color:gray; background-color:gray'),
fluidRow(column(4, actionButton("runSim", "Run simulation")),
column(4, actionButton("saveSim", "Save output file")),
column(4, actionButton("printSim", "Save/print plot"))),
tags$hr(style='height:2px; border-width:0; color:gray; background-color:gray')
)
),
mainPanel("",
# no model selected-- show information page
conditionalPanel(condition="input.sim=='none'",
includeHTML("www/simNoneText.html")
),
tabsetPanel(id="outTabs", type="tabs",
tabPanel("Plot", plotOutput("modl.plot")
),
tabPanel("Summary"
),
tabPanel("R Code"
)
)
)
)
)
)
server.r
# filename: server.r
# Michael Camann, Humboldt State University, 2014
library(shiny)
################################################################################
#
# Dorrie fish reproduction/growth trade-offs
#
################################################################################
# This function implements the Doriefish simulation exercise distributed in Java
# with Cain et al Ecology, 2nd ed.
# Arguments:
#
# S The switch point in grams for transitioning from growth to reproduction.
# Default = 10 g.
#
# p Probability of predation. Default = 0.12.
#
# show Logical. If TRUE (default), print and plot single simulation output. Set
# to FALSE when running multiple simulations in order to average the number
# of offspring across multiple simulations, in which case viewing the
# results of individual simulations isn't necessary.
df.sim <- function(S=10, p=0.12, show=TRUE)
{
if (S < 10 | S > 50) stop("S must be between 10 and 50 grams.")
doriefish <- 10 # cohort size
imass <- 5 # initial mass
mass <- rep(imass, doriefish) # init vector of dorrie fish mass
dead <- rep(FALSE, doriefish) # init vector of mortality status
mhistory <- matrix(dead, nrow=1, byrow=TRUE) # init a matrix of mortality history
offspring.t <- 0 # init offspring count
total.offspring <- vector() # storage vector
while(any(dead==FALSE)) # continue until all are dead
{
repro.t <- mass >= S & !dead # who reproduces during this time step?
offspring.t <- offspring.t + sum(mass[repro.t==TRUE]/5) # one offspring per 5 g body mass
total.offspring <- c(total.offspring, offspring.t) # keep track of offspring each time step
mass[!dead & !repro.t] <- mass[!dead & !repro.t] + 5 # incr mass by 5 g if not reproducing
die.t <- sapply(1:length(dead), function(x) runif(1) <= p) # stochastic selection of some for predation
dead <- dead | die.t # update the mortality vector
mhistory <- rbind(mhistory, dead) # update the mortality history matrix
}
rownames(mhistory) <- c(1:nrow(mhistory))
rval <- list(S=S, p=p, mass=mass, mhistory=mhistory, offspring.t=total.offspring,
offspring=total.offspring[length(total.offspring)], show=show)
class(rval) <- "df.sim"
return(rval)
}
print.df.sim <- function(sim) # print function for df.sim()
{
if(sim$show)
{
cat("\n\tDoriefish simulation\n\n")
cat("Switch point from growth to reproduction:", sim$S, "g.\n\n")
cat("Probability of predation:", sim$p, "per time step.\n\n")
cat("Time to cohort extinction:", length(sim$offspring.t), "time steps.\n\n")
cat("Total adult Doriefish biomass:", sum(sim$mass), "g.\n\n")
cat("Total offspring produced:", sim$offspring, "\n\n")
plot(sim)
}
}
plot.df.sim <- function(sim, sstep=FALSE, s=0.75, ...) # plot function for df.sim()
{
opar <- par(no.readonly=TRUE) # save graphics parameters
txt <- paste("Total offspring:", sim$offspring,
" Time to cohort extinction:", length(sim$offspring.t), "time steps.")
step.through <- function(sim, s=0.5, txt=txt)
{
len <- length(sim$offspring.t)
par(fig=c(0,1,0.1,0.9), xpd=NA)
plot(sim$offspring.t, type="n", xlab="Time steps", ylab="Cumulative Doriefish offspring",
xlim=c(1, max(len, length(sim$mass))))
mtext("Dorriefish living per time step (green = alive, red = dead):", side=3, at=(max(len)/2)+0.5,
line=4.2)
for(i in 1:nrow(sim$mhistory))
{
z <- rep("green", length(sim$mass))
z[sim$mhistory[i,]] <- "red"
points(seq(1,len, length=length(sim$mass)), rep(max(1.18*sim$offspring.t), length(sim$mass)),
pch=21, col="black", bg=z, cex=2.5)
lines(sim$offspring.t[1:i], type="h")
Sys.sleep(s)
}
mtext(txt, side=1, at=0, line=5, adj=0)
}
if(sstep) step.through(sim, s, txt)
else
{
par(fig=c(0,1,0.1,1), xpd=NA)
plot(sim$offspring.t, type="h", xlab="Time steps",
ylab="Cumulative Doriefish offspring", ...)
mtext(txt, side=1, at=0, line=5, adj=0)
}
par(opar)
}
################################################################################
#
# Define the shiny server
#
################################################################################
shinyServer(function(input, output) {
observe({
if(input$runSim == 0) return()
isolate({
sim <- reactive({
switch(input$sim,
dorriefish = {
df.sim(input$S.df, input$p.df, show=FALSE)
} # end case dorriefish
) # end switch(input$sim)
}) # end reactive()
output$modl.plot <- renderPlot({
switch(input$sim,
dorriefish = {
if (input$reps.df == 1)
{
dev.new()
plot(sim(), sstep=TRUE)
}
}, # end case dorriefish
) # end switch(input$sim)
}) # end renderPlot()
}) # end isolate()
}) # end observe()
}) # end shinyServer()
Thanks in advance for any advice you can offer. I'm rather new to Shiny and obviously need more time with it.
submitButton
rather then anactionButon
– jdharrison