3
votes

I am new to Shiny, and I need to know how to use a button to retrigger shinyServer({}) even with no new input.

I have a function makeTrace() which creates a random time series with certain features. I just want to write a simple Shiny App to present the time series along with a button that calls makeTrace again to redo the time series so the viewer can see as many as he likes by pressing the button repeatedly. The randomization is in the makeTrace function so all I need my shiny to do is call this function.

The only way I got this to work was to add another input and link the button and the plot by way of that input, but this isn't really what I want:

server.R

source("helpers.R")

shinyServer(function(input, output) {

  # builds a reactive expression that only invalidates 
  # when the value of input$goButton becomes out of date 
  # (i.e., when the button is pressed)
  ntext <- eventReactive(input$goButton, {
    output$trace <- renderPlot({
      plot(makeTrace())
    })
    input$n
  })

  output$nText <- renderText({
    ntext()
  })

})

and ui.R

   shinyUI(pageWithSidebar(
  headerPanel("actionButton test"),
  sidebarPanel(
    numericInput("n", "N:", min = 0, max = 100, value = 50),
    br(),
    actionButton("goButton", "Go!"),
    p("Click the button to update the value displayed in the main panel.")
  ),
  mainPanel(
    verbatimTextOutput("nText"),
    plotOutput("trace")
  )
))

And I will paste helpers.R at bottom if you want to run it.

So if you run this, you'll see the display does update with a new time series, but the problem is I want to do this without the hack of having some fake input I am using as a fake parameter. I really just want to press a button and make the plot(makeTrace()) get called again, because as I said, makeTrace() takes care of creating new/random data each time it's called.

Thanks.

helpers.R

#MAKE STEPS WITHIN A CONDUCTANCE STEP

makeSteps <- function(lowG, highG, num.G.groups = 3, max.steps = max.steps.const, mean.step.p = 300, sd.step.p = 100, temp=0){

  #decide how many within conductance plateau events there are for this plateau and segment conductance into boxes
  mid               <- rnorm(2, mean = (lowG + highG)/2, sd = (lowG + highG)/5)
  conductance.boxes <- seq(from=min(mid), to=max(mid), length.out = num.G.groups)
  numSteps          <- sample(1:max.steps, 1)

  #choose conductance and step length for each step
  #enforce G values that are physically realistic, no higher or lower than cutoff values
  stepsG <- rnorm(numSteps, mean = (lowG + highG)/2, sd = (lowG + highG)/5)
  stepsG <- replace(stepsG, stepsG<lowG, lowG)
  stepsG <- replace(stepsG, stepsG>highG, highG)
  stepsL <- floor(abs(rnorm(n = numSteps, mean = mean.step.p, sd=sd.step.p)))

  #prepare entire molecular step
  trace  <- c(1:sum(stepsL))
  startp <- 1
  for(i in 1:length(stepsL)){
    endp               <- startp + stepsL[i] - 1
    trace[startp:endp] <- rnorm(n=stepsL[i], mean = stepsG[i], sd = stepsG[i]/12)
    startp             <- startp + stepsL[i]
  }

  trace
}

# MAKES TRACES WITH A MOLECULAR STEP
makeTrace <- function(molG=10^-4, molSD=10^-4, excursion=5, GDist=.5, numP=numPconst, multiples=FALSE, temp=0, shiny=0){

  #make 3,2,1  G0 steps that have within-plateau events if temp = 0, otherwise they are relatively flat
  g3 <- makeSteps(lowG=2.1, highG=2.5, temp=temp)
  g2 <- makeSteps(lowG=1.4, highG=min(g3), temp=temp)
  g1 <- makeSteps(lowG=.5, highG=min(g2), temp=temp)


  #length measured in points
  g1stepL <- length(g1)
  g2stepL <- length(g2)
  g3stepL <- length(g3)

  #conversion between nm and indices
  delX <- numP/excursion
  dt   <- seq(from = 0 , to = excursion, excursion/numP)

  #store conductance time series in g (G for conductance)
  g      <- c(1:numP + 1)
  offset <- 0

  #assign 3,2,1 G0 steps generated earlier individually to single time series
  g[(1 + offset) : (g3stepL + offset)] <- g3 
  offset                               <- g3stepL + offset

  g[(1 + offset) : (g2stepL + offset)] <- g2 
  offset                               <- g2stepL + offset

  g[(1 + offset) : (g1stepL + offset)] <- g1 
  offset                               <- g1stepL + offset

  #metallic junction breaks to electronics noise level
  g[(offset):numP + 1] <- rnorm(n=(numP-offset+1),mean=10^-5, sd = 10^-5)

  #subtract out negative noise so all values positive
  m <- min(g)
  g <- g - m +.000001
  g <- remove.repeat.NA(trace.smooth(g, type="moving-average", width=5))
  df <- data.frame(Displacement=dt,Conductance=g, check.names = FALSE, row.names = NULL)  
}
2

2 Answers

2
votes

In ui.R include:

actionButton('samp','New Sample')

Then in server.R include:

observe({
  if(input$samp > 0) {
    plot(makeTrace())
  }
})

You may need some other calls (possibly isolate) as well, but hopefully this will get you started.

1
votes

@GregSnow's suggestion was close but did not actually update the display. Here is the solution to make the button update a display generated from calling a function in a helper file:

 observe({   
    if(input$samp > 0) {
      plot(makeTrace())
      output$trace <- renderPlot({
        plot(makeTrace())
      })
    }
  })

isolate() should not be needed because there are not other inputs that I need to isolate from plot in this case.