| | |
| | |
| | |
| |
|
| | |
| | |
| |
|
| | |
| | rtemisxt_version <- "0.0.3" |
| | library(shiny) |
| | library(bslib) |
| | library(htmltools) |
| | library(plotly) |
| | library(rtemis) |
| | library(rtemisbio) |
| | source("globals.R") |
| |
|
| | |
| | source("data.R") |
| |
|
| | |
| | builtin_names <- readLines("data.R") |> |
| | (\(z) grep("<-", x = z, value = TRUE))() |> |
| | (\(z) gsub(" <-.*", "", x = z))() |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | xtlive <- function( |
| | default_theme = "dark", |
| | xt_plotly_height = "900px", |
| | verbosity = 0) { |
| |
|
| | |
| | logo <- base64enc::dataURI( |
| | file = "./www/rtemisxt_gray.png", mime = "image/png" |
| | ) |
| |
|
| | |
| | platform <- sessionInfo()[["platform"]] |
| | xtl <- paste0( |
| | "rtemisXt v", rtemisxt_version, |
| | " | ", "rtemisbio v", utils::packageVersion("rtemisbio"), |
| | " | ", "rtemis v", utils::packageVersion("rtemis"), |
| | " | R v", version$major, ".", version$minor, |
| | " | running on ", platform |
| | ) |
| |
|
| | |
| | shinylive_info <- if (substr(platform, 1, 4) == "wasm") { |
| | paste0( |
| | "<br><br>This application has been compiled to ", |
| | as.character(a("WebAssembly", href = "https://webassembly.org/", target = "_blank")), |
| | " using ", |
| | as.character(a("shinylive", href = "https://posit-dev.github.io/r-shinylive/", target = "_blank")), |
| | "<br>and is best viewed with the latest version of Chrome." |
| | ) |
| | } else { |
| | NULL |
| | } |
| |
|
| | |
| | ui <- function(request) { |
| | bslib::page_navbar( |
| | |
| | title = list( |
| | logo = a( |
| | img( |
| | src = logo, |
| | width = "140px", |
| | height = "auto", |
| | alt = "rtemis" |
| | ), |
| | href = "https://rtemis.org/rtemisxt" |
| | ) |
| | ), |
| | id = "rtemisxt", |
| | selected = "Welcome", |
| | footer = span( |
| | xtl, |
| | " © 2024 EDG", |
| | style = "display: block; text-align: center; margin-top: 1em; margin-bottom: 1em;" |
| | ), |
| | |
| | theme = bslib::bs_theme( |
| | `tooltip-opacity` = 1, |
| | `tooltip-border-radius` = "10px", |
| | `tooltip-padding-x` = "1rem", |
| | `tooltip-padding-y` = "1rem", |
| | `tooltip-font-size` = "1rem" |
| | ) |> |
| | bs_add_rules(sass::sass_file("www/rtemislive.scss")), |
| | |
| | window_title = "rtemis rtemisXt", |
| | |
| | lang = "en", |
| | |
| | |
| | bslib::nav_panel( |
| | title = "Welcome", |
| | icon = bsicons::bs_icon("stars"), |
| | bslib::card( |
| | h4("Welcome to rtemisXt.", style = "text-align: center;"), |
| | card_body( |
| | class = "d-inline text-center", |
| | HTML(paste0( |
| | "rtemisXt is a web interface for ", |
| | as.character(a("rtemis", href = "https://rtemis.org", target = "_blank")), |
| | ", <br>providing interactive visualization of timeseries data.", |
| | "<br><br>", |
| | rthelp_inline( |
| | "To get started, use the navigation tabs at the top.", |
| | title = "Welcome" |
| | ), |
| | shinylive_info |
| | )), |
| | br(), br(), |
| | bslib::card_image( |
| | file = "./www/rtemisxt_splash.png", |
| | alt = "rtemisXt", |
| | align = "center", |
| | border_radius = "all", |
| | fill = FALSE, |
| | width = "40%", |
| | class = "mx-auto" |
| | ) |
| | ) |
| | ) |
| | ), |
| | |
| | bslib::nav_panel( |
| | title = "Timeseries", |
| | icon = bsicons::bs_icon("body-text"), |
| | card( |
| | full_screen = TRUE, |
| | class = "p-0", |
| | |
| | card_header( |
| | class = "d-flex justify-content-end", |
| | |
| | uiOutput("ui_xt_tooltip"), |
| | |
| | uiOutput("ui_xt_popover") |
| | ), |
| | layout_sidebar( |
| | fillable = TRUE, |
| | |
| | sidebar = bslib::sidebar( |
| | uiOutput("ui_xt_load_switch"), |
| | uiOutput("ui_xt_data_load"), |
| | uiOutput("ui_xt_data_info"), |
| | uiOutput("ui_xt_plot_button") |
| | |
| | ), |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | uiOutput("ui_dplot3_xt") |
| | ) |
| | ) |
| | ), |
| | |
| | bslib::nav_panel( |
| | title = "About", |
| | icon = bsicons::bs_icon("info-square"), |
| | bslib::card( |
| | card_image( |
| | file = "./www/rtemisbio.webp", |
| | href = "https://rtemis.org/rtemisbio", |
| | alt = "rtemisbio", |
| | align = "center", |
| | border_radius = "all", |
| | fill = FALSE, |
| | width = "54%", |
| | class = "mx-auto" |
| | ), |
| | div( |
| | class = "d-inline text-center", |
| | HTML(paste0( |
| | "Powered by ", |
| | as.character(a("rtemis", href = "https://rtemis.org", target = "_blank")), |
| | "." |
| | )), |
| | br(), br(), |
| | a( |
| | img( |
| | src = "rtemis_gray.png", |
| | alt = "rtemis", |
| | align = "center", |
| | width = "190px" |
| | ), |
| | href = "https://rtemis.org", |
| | target = "_blank" |
| | ), |
| | align = "center" |
| | ) |
| | ) |
| | ), |
| | bslib::nav_spacer(), |
| | bslib::nav_item(input_dark_mode(id = "dark_mode", mode = default_theme)), |
| | header = list( |
| | |
| | shinybusy::add_busy_spinner( |
| | spin = "orbit", |
| | color = "#00ffff", |
| | timeout = 200, |
| | position = "bottom-left", |
| | onstart = FALSE |
| | ) |
| | ) |
| | ) |
| | } |
| |
|
| | |
| | server <- function(input, output, session) { |
| | |
| | output$ui_xt_load_switch <- shiny::renderUI({ |
| | if (verbosity > 0) { |
| | message("Rendering ui_xt_load_switch") |
| | } |
| | |
| | shiny::radioButtons( |
| | inputId = "xt_load_switch", |
| | label = "Data source", |
| | choices = list( |
| | `Built-in datasets` = "builtin", |
| | `File upload` = "upload" |
| | ), |
| | selected = "builtin" |
| | ) |
| | }) |
| |
|
| | |
| | output$ui_xt_data_load <- shiny::renderUI({ |
| | req(input$xt_load_switch) |
| | if (input$xt_load_switch == "upload") { |
| | |
| | if (verbosity > 0) { |
| | message("Rendering ui_xt_data_load for file upload") |
| | } |
| | shiny::fileInput( |
| | inputId = "xt_file", |
| | label = "Upload xt file", |
| | buttonLabel = "Browse local files...", |
| | ) |
| | } else { |
| | |
| | if (verbosity > 0) { |
| | message("Rendering ui_xt_data_load for built-in data selection") |
| | } |
| | shiny::selectizeInput( |
| | inputId = "xt_builtin_data", |
| | label = "Select built-in xt dataset", |
| | choices = builtin_names, |
| | selected = builtin_names[1] |
| | ) |
| | } |
| | }) |
| |
|
| | |
| | output$ui_xt_data_info <- shiny::renderUI({ |
| | req(xt_obj()) |
| | if (verbosity > 0) { |
| | message("Rendering ui_xt_data_info") |
| | } |
| | |
| | bslib::card( |
| | bslib::card_title("xt Dataset Info", container = htmltools::h6), |
| | bslib::card_body( |
| | summarize_xt(xt_obj()), |
| | fillable = FALSE |
| | ) |
| | ) |
| | }) |
| |
|
| | |
| | output$ui_xt_plot_button <- shiny::renderUI({ |
| | req(xt_obj()) |
| | if (verbosity > 0) { |
| | message("Rendering ui_xt_plot_button") |
| | } |
| | bslib::input_task_button( |
| | "xt_plot_button", |
| | "Plot dataset", |
| | icon = bsicons::bs_icon("magic"), |
| | label_busy = "Drawing...", |
| | icon_busy = bsicons::bs_icon("clock-history"), |
| | type = "primary", |
| | auto_reset = TRUE |
| | ) |
| | }) |
| |
|
| | |
| | xt_obj <- shiny::reactive({ |
| | req(input$xt_load_switch) |
| | if (input$xt_load_switch == "upload") { |
| | |
| | req(input$xt_file$datapath) |
| | if (verbosity > 0) { |
| | message("Loading xt data from file '", input$xt_file$datapath, "'") |
| | } |
| | |
| | ext <- tools::file_ext(input$xt_file$datapath) |
| | dat <- if (ext == "json") { |
| | read.xtjson(input$xt_file$datapath) |
| | } else if (ext == "rds") { |
| | readRDS(input$xt_file$datapath) |
| | } else { |
| | stop("Unsupported file format") |
| | } |
| | if (verbosity > 0) { |
| | message("Loaded dataset of class '", class(dat)[1], "'") |
| | } |
| | return(dat) |
| | } else { |
| | |
| | req(input$xt_builtin_data) |
| | if (verbosity > 0) { |
| | message("Loading built-in xt dataset '", input$xt_builtin_data, "'") |
| | } |
| | |
| | |
| | |
| | dat <- get(input$xt_builtin_data) |
| | if (verbosity > 0) { |
| | message("Loaded dataset of class '", class(dat)[1], "'") |
| | } |
| | return(dat) |
| | } |
| | }) |
| |
|
| | |
| | dplot3_theme <- shiny::reactive({ |
| | req(input$dark_mode) |
| | if (input$dark_mode == "dark") { |
| | "black" |
| | } else { |
| | "white" |
| | } |
| | }) |
| |
|
| | |
| | dplot3_theme <- shiny::reactive({ |
| | req(input$dark_mode) |
| | if (input$dark_mode == "dark") { |
| | "darkgraygrid" |
| | } else { |
| | "whitegrid" |
| | } |
| | }) |
| |
|
| | |
| | output$dplot3_xt <- plotly::renderPlotly({ |
| | req(xt_obj()) |
| | if (verbosity > 0) { |
| | message("Rendering dplot3_xt of object with class '", class(xt_obj())[1], "'") |
| | } |
| | dplot3_xt( |
| | x = xt_obj(), |
| | yline.width = input$xt.linewidth, |
| | y2line.width = input$xt.linewidth, |
| | theme = dplot3_theme(), |
| | bg = input$plot.bg, |
| | plot.bg = input$plot.bg, |
| | legend.x = 0, |
| | legend.xanchor = "left", |
| | show.rangeslider = input$show.rangeslider, |
| | margin = list(l = 75, r = 75, b = 75, t = 75), |
| | ) |
| | }) |> |
| | |
| | bindEvent(input$xt_plot_button, input$xt_plot_update_button) |
| |
|
| | |
| | clicked <- shiny::reactiveVal(FALSE) |
| | shiny::observeEvent(input$xt_plot_button, { |
| | clicked(TRUE) |
| | }) |
| |
|
| | |
| | output$ui_xt_tooltip <- shiny::renderUI({ |
| | if (clicked()) { |
| | bslib::tooltip( |
| | trigger = span( |
| | "Plot help", bsicons::bs_icon("info-circle", class = "text-info"), |
| | style = "text-align: right;", |
| | class = "rtanihi" |
| | ), |
| | div( |
| | rthelplist( |
| | c( |
| | "Hover over plot to see spike lines", |
| | |
| | "Use top-right gear icon to access plot settings.", |
| | htmlEscape("To enter superscripts and subscripts in labs or units, use HTML tags, e.g. '<sup>2</sup>' and '<sub>i</sub>'.") |
| | ) |
| | ), |
| | style = "text-align: left;" |
| | ), |
| | placement = "bottom" |
| | ) |
| | } |
| | }) |
| |
|
| | |
| | output$ui_xt_popover <- shiny::renderUI({ |
| | if (clicked()) { |
| | popover( |
| | trigger = bsicons::bs_icon("gear", class = "ms-auto"), |
| | textInput(inputId = "xt.xlab", label = "X-axis label", value = ""), |
| | textInput(inputId = "xt.ylab", label = "Left Y-axis label", value = ""), |
| | textInput(inputId = "xt.y2lab", label = "Right Y-axis label", value = ""), |
| | textInput(inputId = "xt.xunits", label = "X-axis units", value = ""), |
| | textInput(inputId = "xt.yunits", label = "Left Y-axis units", value = ""), |
| | textInput(inputId = "xt.y2units", label = "Right Y-axis units", value = ""), |
| | textInput(inputId = "xt.firsttick", label = "First X-axis tick", value = "2"), |
| | textInput(inputId = "xt.ticksevery", label = "Show X-axis ticks every this many X values", value = "2"), |
| | sliderInput(inputId = "xt.linewidth", label = "Line width", min = 1, max = 10, value = 3), |
| | bslib::input_switch( |
| | id = "show.rangeslider", |
| | label = "Show rangeslider", |
| | value = TRUE |
| | ), |
| | shinyWidgets::colorPickr( |
| | "plot.bg", |
| | label = "Plot background", |
| | selected = ifelse(input$dark_mode == "dark", "#191919", "#FFFFFF") |
| | ), |
| | bslib::input_task_button( |
| | "xt_plot_update_button", |
| | "Update rendering", |
| | icon = bsicons::bs_icon("arrow-clockwise"), |
| | label_busy = "Drawing...", |
| | icon_busy = bsicons::bs_icon("clock-history"), |
| | type = "primary", |
| | auto_reset = TRUE |
| | ), |
| | title = "Plot settings", |
| | placement = "auto" |
| | ) |
| | } |
| | }) |
| |
|
| |
|
| | |
| | output$ui_dplot3_xt <- renderUI({ |
| | if (clicked() == FALSE || is.null(xt_obj())) { |
| | rthelp( |
| | |
| | "Select Data Source on the left.", |
| | title = "Timeseries Visualization " |
| | ) |
| | } else { |
| | plotly::plotlyOutput( |
| | "dplot3_xt", |
| | width = "100%", |
| | height = xt_plotly_height |
| | ) |
| | } |
| | }) |
| | } |
| |
|
| | |
| | shiny::shinyApp(ui = ui, server = server, enableBookmarking = "url") |
| | } |
| |
|
| | xtlive() |
| |
|