R/simpleDisplay_module.R

Defines functions simpleDisplay simpleDisplayUI

Documented in simpleDisplay simpleDisplayUI

#' A shiny Module to display and save plots
#' @param id shiny id
#' @import shiny
#' @export
#' @examples 
#' \dontrun{
#' library(shiny)
#' library(shinydashboard)
#' library(gridExtra)
#' library(ggplot2)
#' library(plotly)
#' 
#' if (interactive()){
#' 
#'   ui <- dashboardPage(
#'     dashboardHeader(title = "simpleDisplay"),
#'     sidebar = dashboardSidebar(disable = TRUE),
#'     body = dashboardBody(
#'       fluidRow(
#'         column(12, box(width = NULL, simpleDisplayUI("simple_display_module")))
#'       )
#'     )
#'   )
#' 
#'   server <- function(input, output, session) {
#' 
#'     params <- reactiveValues(top = "Iris", use_plotly = FALSE)
#' 
#'     plot_list <- reactive({
#' 
#'       plist <- list()
#' 
#'       plist[[1]] <- ggplot(iris, aes(x=Sepal.Length, y = Sepal.Width, color = Species)) +
#'         geom_point(alpha = 0.5) +
#'         facet_wrap(~Species)
#' 
#'        plist[[2]] <- ggplot(iris, aes(x=Species, y = Sepal.Length, fill = Species)) +
#'          geom_col(alpha = 0.5)
#' 
#'       return(plist)
#' 
#'     })
#' 
#'     callModule(simpleDisplay, "simple_display_module", 
#'                plot_list = plot_list, 
#'                params = params,
#'                size = 500)
#' 
#'   }
#' 
#'   shinyApp(ui, server)
#' 
#' }
#'}
simpleDisplayUI <- function(id){

  ns <- NS(id)

  tagList(
    uiOutput(ns("ui_plot")),
    br(),
    br(),
    fluidRow(
      uiOutput(ns("ui_options_all"))
    )
  )
}


#' display server function
#' @param input shiny input
#' @param output shiny output
#' @param session shiny session
#' @param plot_list a reactivevalues object containing a plot or a list of plots
#' @param params reactivevalues object used to initialize plot parameters
#' with the following elements (not mandatory):
#' \describe{
#'   \item{use_plotly}{: use plotly library to render an interactive plot}
#'   \item{top}{: main title}
#'  }
#' @param save logical. Add buttons to save plot and plot data?
#' @param multirow logical. Allow plots to be displayed on multiple rows?
#' @return a reactivevalues object with:
#' \describe{
#'   \item{plot}{: the plots displayed}
#'   \item{params}{: input parameters. These include events describing user interaction 
#'   with the plot such as:
#'     \describe{
#'       \item{plot_brush}{: plot brush events}
#'       \item{plot_click}{: plot click events}
#'       \item{plot_dblclick}{: plot double click events}
#'     }
#'   }
#' }
#' @importFrom gridExtra marrangeGrob
#' @importFrom grDevices pdf dev.off
#' @importFrom plotly plotlyOutput renderPlotly
#' @export
#' @rdname simpleDisplayUI
simpleDisplay <- function(input, output, session,
                          plot_list,
                          params = reactiveValues(),
                          save = TRUE,
                          multirow = FALSE) {
  
  rval_plot <- reactiveValues(nrow = 1,
                              use_plotly = FALSE,
                              title = "",
                              zoom = 100,
                              width = 300,
                              height = 300,
                              max_height = 1000,
                              min_size = 150,
                              init = TRUE,
                              show_title = TRUE)
  
  rval_mod <- reactiveValues(update_render = FALSE,
                             nrow_display = 1,
                             ncol = 1,
                             ncol_facet = 1,
                             nrow_facet = 1)
  
  rval_input <- reactiveValues()
  
  ### Set plot parameters using plot_params ##########################################################
  
  observeEvent(reactiveValuesToList(params), {
    for(var in intersect(names(params), names(rval_plot))){
      rval_plot[[var]] <- params[[var]]
    }
  })
  
  ### store plot parameters in rval_input #############################################################
  
  observeEvent(reactiveValuesToList(rval_plot), {
    for(var in names(rval_plot)){
      rval_input[[var]] <- rval_plot[[var]]
    }
  })
  
  observe({
    for(var in names(input)){
      rval_input[[var]] <- input[[var]]
    }
  })
  
  ### Layout plots #################################################################################
  
  plot_display <- reactive({
    
    
      rval_mod$ncol_facet <- 1
      rval_mod$nrow_facet <- 1
      rval_mod$ncol <- 1
      rval_plot$nrow_display <- 1
      
     if(class(plot_list())[1] == "list"){
       
         n <- length(plot_list())
         if(n>0){
          if("ggplot" %in% class(plot_list()[[1]]) ){
            if(n > 0){
              p <- plot_list()[[1]]
              if("facet" %in% names(p)){
                facet_layout <- p$facet$compute_layout(p, p$facet$params)
                
                if(!is.null(facet_layout)){
                  rval_mod$ncol_facet <- max(facet_layout$COL)
                  rval_mod$nrow_facet <- max(facet_layout$ROW)
                }
              }
            }
            
            if(n > 1){
              
              rval_plot$use_plotly <- FALSE

              rval_mod$nrow_display <- min(n, rval_input$nrow)
              rval_mod$ncol <- ceiling(n/rval_input$nrow)
              
              top <- ""
              if(!is.null(rval_input$show_title)){
                if(rval_input$show_title){
                  top <- rval_plot$title
                }
              }

              g <- try(gridExtra::marrangeGrob(plot_list(), 
                                           nrow = rval_mod$nrow_display, 
                                           ncol = rval_mod$ncol, 
                                           top = top),
                       silent = TRUE)

              if("try-error" %in% class(g)){
                showModal(modalDialog(
                  title = "Error",
                  print(g),
                  easyClose = TRUE,
                  footer = NULL
                ))
              }
              return(g)
            }else if(n == 1){
              
                plot_list()[[1]]
              
              
            }else{
              plot_list()
            }
            
          }else{
            plot_list()
          }
        }
         
     }else{
       p <- plot_list()
       if("facet" %in% names(p)){
         
         facet_layout <- p$facet$compute_layout(p, p$facet$params)
         
         if(!is.null(facet_layout)){
           rval_mod$ncol_facet <- max(facet_layout$COL)
           rval_mod$nrow_facet <- max(facet_layout$ROW)
         }
       }
       
       plot_list()
     }
     
   })
  
  ### Control plot rendering (when use_plotly == TRUE) #######################################
  
  observeEvent(rval_input$max_height, {
    rval_mod$update_render <- TRUE
  })
  
  observeEvent(c(rval_input$height), {
    rval_mod$update_render <- FALSE
  })
  
  ### Render plot ###########################################################################
  
  output$plot_render  <- renderPlot({
    validate(need(plot_display(), "No plot to display"))
    if("graphNEL" %in% class(plot_display())){
      Rgraphviz::renderGraph(plot_display())
    }else{
      plot_display()
    }
  })

  output$plot_render_ly  <- renderPlotly({
    rval_mod$update_render <- TRUE
    rval_input$height
    rval_input$width
    rval_input$zoom
    plot_display()
  })
  
  ### Build UI for plot options #############################################################
  
  output$ui_options <- renderUI({
    ns <- session$ns
    display_items <- list()
    
      if(multirow){
        display_items[["nrow"]] <- numericInput(ns("nrow"),
                                                      label = "Number of rows", value = rval_plot$nrow)
      }
      display_items[["zoom"]] <- sliderInput(ns("zoom"), 
                                             label = "Zoom", min = 0, max = 200, step = 1, value = rval_plot$zoom)
      display_items[["height"]] <- numericInput(ns("height"), 
                                                  label = "plot height (px)", value = rval_plot$height)
      display_items[["width"]] <- numericInput(ns("width"), 
                                                  label = "plot width (px)", value = rval_plot$width)
      display_items[["max_height"]] <- numericInput(ns("max_height"), 
                                                    label = "max height (px)", value = rval_plot$max_height)
      if("title" %in% names(params) ){
        display_items[["show_title"]] <- checkboxInput(ns("show_title"), 
                                                       label = "show title", value = rval_plot$show_title)
      }
    
    return( tagList( display_items) )
  })
  
  output$ui_save <- renderUI({
    ns <- session$ns
    x <- list()
    x[[1]] <- downloadButton(ns("download_plot"), "Save plot")
    x[[2]] <- downloadButton(ns("download_plot_data"), "Save plot data")
    fluidRow(tagList(x))
  })
  
  output$ui_options_all <- renderUI({
    ns <- session$ns
    x <- list()
    
    x[[1]] <- box(title = "Display options", width = ifelse(save, 6, 12), 
                  collapsible = TRUE, collapsed = TRUE,
        uiOutput(ns("ui_options"))
    )
    if(save){
      x[[2]] <- box(title = "Save", width = 6, collapsible = TRUE, collapsed = TRUE,
                    uiOutput(ns("ui_save"))
      )
    }
    
    tagList(x)
  })
  
  ### Build UI for plot display ##########################################################################

  
  output$ui_plot <- renderUI({

    x <- list()
    ns <- session$ns
    
    if(rval_plot$use_plotly){
      if(rval_mod$update_render){
        
        width <- rval_input$width * rval_input$zoom/100
        height <- rval_input$height * rval_input$zoom/100
        width <- max(width, min_size = rval_plot$min_size)
        height <- max(height, min_size = rval_plot$min_size)
        
        x[[1]] <- div(
          style = paste("overflow-y: scroll; overflow-x: scroll; height:", 
                        min(height, rval_input$max_height) + 20, 'px', sep=""),
          plotlyOutput(ns("plot_render_ly"), width = width, height = height)
        )
        
      }else{
        x[[1]] <- plotlyOutput(ns("plot_render_ly"))
      }
      
    }else{
      
      width <- rval_input$width * rval_input$zoom/100
      height <- rval_input$height * rval_input$zoom/100
      width <- max(width, min_size = rval_plot$min_size)
      height <- max(height, min_size = rval_plot$min_size)
      height <- rval_mod$nrow_display * rval_mod$nrow_facet * height
      width <- rval_mod$ncol * rval_mod$ncol_facet * width

      x[[1]] <- div(
        style = paste("overflow-y: scroll; overflow-x: scroll; height:", 
                      min(height, rval_input$max_height) + 20, 'px',sep=""),
        plotOutput(ns("plot_render"),
                   height = height,
                   width = width,
                   brush = ns("plot_brush"),
                   click = ns("plot_click"),
                   dblclick = ns("plot_dblclick")
        )
        
      )
       
    }
    return(tagList(x))
  })


  ### Save plot ############################################################################
  
  output$download_plot <- downloadHandler(
    filename = "plot.pdf",
    content = function(file) {
      width <- rval_input$width * rval_input$zoom/100
      height <- rval_input$height * rval_input$zoom/100
      width <- max(width, min_size = rval_plot$min_size)
      height <- max(height, min_size = rval_plot$min_size)
      height <- rval_mod$nrow_display * rval_mod$nrow_facet * height
      width <- rval_mod$ncol * rval_mod$ncol_facet * width


      pdf(file, width = width * 5/400, height = height * 5/400)
      if("graphNEL" %in% class(plot_display())){
        Rgraphviz::renderGraph(plot_display())
      }else{
        print(plot_display())
      }
      dev.off()
    }
  )
  
  output$download_plot_data <- downloadHandler(
    filename = "plot_data.rda",
    content = function(file) {
      plot_data <- plot_list()
      save(plot_data, file = file)
    }
  )
  
  return( list( plot = plot_display, params = input) )
}



### Tests ###################################################################################
# library(shiny)
# library(shinydashboard)
# library(gridExtra)
# library(ggplot2)
# library(plotly)
# 
# if (interactive()){
# 
#   ui <- dashboardPage(
#     dashboardHeader(title = "simpleDisplay"),
#     sidebar = dashboardSidebar(disable = TRUE),
#     body = dashboardBody(
#       fluidRow(
#         column(6, box(width = NULL, simpleDisplayUI("simple_display_module"))),
#         column(6, box(width = NULL, plotOutput("plot")))
#       )
#     )
#   )
# 
#   server <- function(input, output, session) {
# 
#     params <- reactiveValues(use_plotly = FALSE,
#                              width = 300,
#                              height = 300,
#                              max_height = 500,
#                              min_size = 200,
#                              nrow = 2,
#                              title = "samples")
# 
#     plot_list <- reactive({
# 
#       load("~/ownCloud/FlowR_project/flowR_utils/demo-data/Rafa2Gui/analysis/cluster.rda")
#       fs <- build_flowset_from_df(df = res$cluster$data)
#       gs <- GatingSet(fs)
#       add_gates_flowCore(gs, res$cluster$gates)
#       plot_gh(gs, plot_type = "dots")
# 
#         # gates <- get_gates_from_ws(
#         #      "~/ownCloud/FlowR_project/flowR_utils/demo-data/2019-Exp-Tumor-042 (Lung Carcinoma)/Classical analysis 06012020.wsp")
#         # p <- plot_tree(gates, fontsize = 40, rankdir = NULL, shape = "ellipse", fixedsize = TRUE)
#         # p
# 
# 
#       # plist <- list()
#       # plist[[1]] <- ggplot(iris, aes(x=Sepal.Length, y = Sepal.Width, color = Species)) +
#       #   geom_point(alpha = 0.5)+
#       #   facet_wrap(~Species)
#       # 
#       #  plist[[2]] <- ggplot(iris, aes(x=Species, y = Sepal.Length, fill = Species)) +
#       #    geom_col(alpha = 0.5)
#       # 
#       # return(plist)
# 
#       # df <- get_data_gs(gs)
#       # df_cluster <- get_cluster(df, yvar = names(df)[4:7], y_trans = logicle_trans() )
#       # fSOM <- df_cluster$fSOM
#       # graphics::plot.new()
#       # print("plot")
#       # PlotPies(fSOM, cellTypes=as.factor(df$name))
# 
#       #heatmaply(matrix(runif(100), 50, 2))
# 
#     })
# 
#     # output$plot <- renderPlot({
#     #   #plot_list()
#     #   res$plot()
#     # })
# 
#     res <- callModule(simpleDisplay, "simple_display_module",
#                plot_list = plot_list,
#                params = params,
#                save = TRUE,
#                multirow = FALSE)
# 
#   }
# 
#   shinyApp(ui, server)
# 
# }
VoisinneG/flowR documentation built on June 1, 2021, 6:42 p.m.