|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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() |
|
|