Hello again,
OK this seems to be the solution to the problem. It needed four pieces in place:
1) A reactiveValues() object (values$lastAction) that tracked which button was pushed last -- values are { NULL, data, plot }.
2) the getData() function - which doesn't look at item 1, but rather depends only on the second button (loadPlot)
3) the getData() function looks up which samples were selected but using isolate().
4) the renderPlot() function which returns NULL for values$lastAction != "plot".
Here is the functional code incase someone else may find it useful.
To recap, this code implements a two-step (two button) plot-loader:
1) First button loads the configuration for the selected dataset.
2) Second button loads the data and shows the plot.
The plot is thereafter automatically reactive to all changes that don't involve changing selected samples.
Attached are the demo config files that need to be in the same directory as the shiny app.
-----
ui.R:
suppressMessages(require(shiny))
suppressWarnings(shinyUI(fluidPage(
h3("Choosing multiple datasets"),
uiOutput("pickData"),actionButton("getData", "Make active dataset"),
uiOutput("pickSamples"),
numericInput("lty","Line type:", 1:3),
actionButton("loadPlot","Now plot it!"),
plotOutput("samplePlot")
)))
------
server.R:
# ########################################
# Helper function
# ########################################
readConfig <- function# get contents of individual config file.
( f ##<<(character) path to config file
){
dat <- read.delim(f,sep="\t",comment.char="#",as.is=T,header=F)
configParams <- list()
for (k in 1:nrow(dat)) {
configParams[[dat[k,1]]] <- dat[k,2]
if (dat[k,1]=="samples") configParams[[dat[k,1]]] <- unlist(strsplit(dat[k,2],","))
}
return(configParams)
### (list) Key-value pairs of config information
}
listConfig <- function# list all config files available
(indir=".") {
fList <- dir(path=indir,pattern=".txt")
configSet <- list()
for (f in fList) {
tmp <- readConfig(sprintf("%s/%s", indir,f));
configSet[[tmp$name]] <- tmp
}
return(configSet)
### (list of lists) key: name of dataset, value: config key-value pairs
}
# ########################################
# Main function
# ########################################
configSet <- listConfig()
currentSamps <- NULL
shinyServer(function(input,output) {
# which button was last pressed?
# this is needed to separate data load from plot plot.
values <- reactiveValues()
values$lastAction <- NULL
observe({if (input$getData!=0) {values$lastAction <- "data"}})
observe({if (input$loadPlot!=0) {values$lastAction <- "plot"}})
# tier 0 : shows up when page is loaded.
output$pickData <- renderUI({selectInput("dataset", "Select dataset:", names(configSet)) })
updateConfig <- reactive({
if (input$getData == 0) return(NULL) # only depends on first button
print("updated config")
isolate({return(configSet[[input$dataset]])})
})
# tier 1: shows up when "make active dataset" button is clicked.
output$pickSamples <- renderUI({
configParams <- updateConfig(); if (is.null(configParams)) return(NULL)
print("updating sample box")
print(configParams$samples)
selectInput("samples", "Select samples:", configParams$samples, multiple=T,selected=NULL)
})
# tier 2: triggered when "make active dataset" or "now plot it" buttons are selected
getData <- reactive({
if (input$loadPlot==0) return(NULL) # only depends on second button
isolate({
samps <- input$samples
cat("updating data")
if (length(samps)<1) return(NULL)
mat <- matrix(rnorm(10*length(samps)), byrow=F, ncol=length(samps));
colnames(mat) <- samps
return(mat)
})
})
# tier 2: shows up when either dataset or samples buttons are clicked.
output$samplePlot <- renderPlot({
if (is.null(values$lastAction)) return(NULL)
if (values$lastAction=="data") return(NULL) # don't plot if data were loaded; wait till samples are selected. Implicitly depends on plot button being pressed.
configParams <- updateConfig()
dat <- getData(); if (is.null(dat)) return(NULL)
print("making plot")
plot(0,0,type='n', xlab='x',ylab='y',main=sprintf("%s: interesting patterns", configParams$name),
xlim=c(0,10),ylim=c(-2,2))
for (k in 1:ncol(dat)) { points(1:nrow(dat), dat[,k],col=k,type='o',pch=16,lty=input$lty); }
legend("topright", fill=1:ncol(dat),legend=colnames(dat))
})
})