Skip to content

Commit 4824e27

Browse files
averissimom7pr
andauthored
Updates "Decorators" to use name-based execution and new wrappers (#812)
#### Modules ##### 1 object - [x] tm_a_pca - [x] tm_g_bivariate - [x] tm_g_response - [x] tm_g_scatterplot - [x] tm_g_scatterplotmatrix - [x] tm_a_regression - [x] tm_t_crosstable - [x] tm_data_table - [x] tm_g_association ##### 2 objects ##### 3 objects - [x] tm_g_distribution - [x] tm_outliers ##### 4 objects - [x] tm_missing_data ##### Not applicable - [x] ~~tm_file_viewer~~ - [x] ~~tm_front_page~~ - [x] ~~tm_variable_browser~~ #### Changes description - Allow named-based decorators - Use `ui_decorate_teal_data` and `srv_decorate_teal_data` wrapper to simplify code - [x] New function to normalize `decorators` argument in module See [this comment](#812 (comment)) #### App with all modules (WIP) <details><summary>Working example</summary> ```r pkgload::load_all("../teal") pkgload::load_all(".") # ###################################################### # # _____ _ # | __ \ | | # | | | | ___ ___ ___ _ __ __ _| |_ ___ _ __ ___ # | | | |/ _ \/ __/ _ \| '__/ _` | __/ _ \| '__/ __| # | |__| | __/ (_| (_) | | | (_| | || (_) | | \__ \ # |_____/ \___|\___\___/|_| \__,_|\__\___/|_| |___/ # # # # Decorators # ##################################################### plot_grob_decorator <- function(default_footnote = "I am a good decorator", .var_to_replace = "plot") { teal_transform_module( label = "Caption (grob)", ui = function(id) shiny::textInput(shiny::NS(id, "footnote"), "Footnote", value = default_footnote), server = function(id, data) { moduleServer(id, function(input, output, session) { logger::log_info("🟠 plot_grob with default: {default_footnote}!", namespace = "teal.modules.general") reactive({ req(data(), input$footnote) logger::log_info("changing the footnote {default_footnote}", namespace = "teal.modules.general") teal.code::eval_code(data(), substitute( { footnote_grob <- grid::textGrob(footnote, x = 0, hjust = 0, gp = grid::gpar(fontsize = 10, fontface = "italic", col = "gray50")) # Arrange the plot and footnote .var_to_replace <- gridExtra::arrangeGrob( .var_to_replace, footnote_grob, ncol = 1, heights = grid::unit.c(grid::unit(1, "npc") - grid::unit(1, "lines"), grid::unit(1, "lines")) ) }, env = list( footnote = input$footnote, .var_to_replace = as.name(.var_to_replace) ))) }) }) } ) } caption_decorator <- function(default_caption = "I am a good decorator", .var_to_replace = "plot") { teal_transform_module( label = "Caption", ui = function(id) shiny::textInput(shiny::NS(id, "footnote"), "Footnote", value = default_caption), server = make_teal_transform_server( substitute({ .var_to_replace <- .var_to_replace + ggplot2::labs(caption = footnote) }, env = list(.var_to_replace = as.name(.var_to_replace))) ) ) } table_decorator <- function(.color1 = "#f9f9f9", .color2 = "#f0f0f0", .var_to_replace = "table") { teal_transform_module( label = "Table color", ui = function(id) { selectInput( NS(id, "style"), "Table Style", choices = c("Default", "Color1", "Color2"), selected = "Default" ) }, server = function(id, data) { moduleServer(id, function(input, output, session) { logger::log_info("🔵 Table row color called to action!", namespace = "teal.modules.general") reactive({ req(data(), input$style) logger::log_info("changing the Table row color '{input$style}'", namespace = "teal.modules.general") teal.code::eval_code(data(), substitute({ .var_to_replace <- switch( style, "Color1" = DT::formatStyle( .var_to_replace, columns = attr(.var_to_replace$x, "colnames")[-1], target = "row", backgroundColor = .color1 ), "Color2" = DT::formatStyle( .var_to_replace, columns = attr(.var_to_replace$x, "colnames")[-1], target = "row", backgroundColor = .color2 ), .var_to_replace ) }, env = list( style = input$style, .var_to_replace = as.name(.var_to_replace), .color1 = .color1, .color2 = .color2 ))) }) }) } ) } head_decorator <- function(default_value = 6, .var_to_replace = "object") { teal_transform_module( label = "Head", ui = function(id) shiny::numericInput(shiny::NS(id, "n"), "Footnote", value = default_value), server = make_teal_transform_server( substitute({ .var_to_replace <- utils::head(.var_to_replace, n = n) }, env = list(.var_to_replace = as.name(.var_to_replace))) ) ) } treelis_subtitle_decorator <- function(default_caption = "I am a good decorator", .var_to_replace = "plot") { teal_transform_module( label = "Caption", ui = function(id) shiny::textInput(shiny::NS(id, "footnote"), "Footnote", value = default_caption), server = make_teal_transform_server( substitute({ .var_to_replace <- update(.var_to_replace, sub = footnote) }, env = list(.var_to_replace = as.name(.var_to_replace))) ) ) } insert_rrow_decorator <- function(default_caption = "I am a good new row", .var_to_replace = "table") { teal_transform_module( label = "New row", ui = function(id) shiny::textInput(shiny::NS(id, "new_row"), "New row", value = default_caption), server = make_teal_transform_server( substitute({ .var_to_replace <- rtables::insert_rrow(.var_to_replace, rtables::rrow(new_row)) }, env = list(.var_to_replace = as.name(.var_to_replace))) ) ) } do_nothing_decorator <- teal_transform_module(server = function(id, data) moduleServer(id, function(input, output, session) data)) # ########################################## # # _ _ _ _ # | | | | | | | | # | |_ ___ __ _| | __| | __ _| |_ __ _ # | __/ _ \/ _` | | / _` |/ _` | __/ _` | # | || __/ (_| | || (_| | (_| | || (_| | # \__\___|\__,_|_| \__,_|\__,_|\__\__,_| # ______ # |______| # # teal_data # ######################################### data <- teal_data(join_keys = default_cdisc_join_keys[c("ADSL", "ADRS")]) data <- within(data, { require(nestcolor) ADSL <- rADSL ADRS <- rADRS }) # For tm_outliers fact_vars_adsl <- names(Filter(isTRUE, sapply(data[["ADSL"]], is.factor))) vars <- choices_selected(variable_choices(data[["ADSL"]], fact_vars_adsl)) init( data = data, modules = modules( ###################################### # # _ _ _ # | | | (_) # ___ _ _| |_| |_ ___ _ __ ___ # / _ \| | | | __| | |/ _ \ '__/ __| # | (_) | |_| | |_| | | __/ | \__ \ # \___/ \__,_|\__|_|_|\___|_| |___/ # # # # outliers # ##################################### tm_outliers( outlier_var = list( data_extract_spec( dataname = "ADSL", select = select_spec( label = "Select variable:", choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1")), selected = "AGE", multiple = FALSE, fixed = FALSE ) ) ), categorical_var = list( data_extract_spec( dataname = "ADSL", filter = filter_spec( vars = vars, choices = value_choices(data[["ADSL"]], vars$selected), selected = value_choices(data[["ADSL"]], vars$selected), multiple = TRUE ) ) ), decorators = list( box_plot = caption_decorator("I am a good decorator", "box_plot"), density_plot = caption_decorator("I am a good decorator", "density_plot"), cumulative_plot = caption_decorator("I am a good decorator", "cumulative_plot"), table = table_decorator("#FFA500", "#800080") ) ), # ####################################################### # # _ _ _ # (_) | | (_) # __ _ ___ ___ ___ ___ _ __ _| |_ _ ___ _ __ # / _` / __/ __|/ _ \ / __| |/ _` | __| |/ _ \| '_ \ # | (_| \__ \__ \ (_) | (__| | (_| | |_| | (_) | | | | # \__,_|___/___/\___/ \___|_|\__,_|\__|_|\___/|_| |_| # # # # association # ###################################################### tm_g_association( ref = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices( data[["ADSL"]], c("SEX", "RACE", "COUNTRY", "ARM", "STRATA1", "STRATA2", "ITTFL", "BMRKR2") ), selected = "RACE" ) ), vars = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices( data[["ADSL"]], c("SEX", "RACE", "COUNTRY", "ARM", "STRATA1", "STRATA2", "ITTFL", "BMRKR2") ), selected = "BMRKR2", multiple = TRUE ) ), decorators = list(plot_grob_decorator("I am a good grob (association)")) ), # ################################################ # # _ _ _ _ _ # | | | | | | | | | | # __| | __ _| |_ __ _ | |_ __ _| |__ | | ___ # / _` |/ _` | __/ _` || __/ _` | '_ \| |/ _ \ # | (_| | (_| | || (_| || || (_| | |_) | | __/ # \__,_|\__,_|\__\__,_| \__\__,_|_.__/|_|\___| # ______ # |______| # # data_table # ############################################### tm_data_table( variables_selected = list( iris = c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "Species") ), dt_args = list(caption = "IRIS Table Caption"), decorators = list(table_decorator()) ), # ######################################################## # # _ _ _ # | | | | | | # ___ _ __ ___ ___ ___ ______| |_ __ _| |__ | | ___ # / __| '__/ _ \/ __/ __|______| __/ _` | '_ \| |/ _ \ # | (__| | | (_) \__ \__ \ | || (_| | |_) | | __/ # \___|_| \___/|___/___/ \__\__,_|_.__/|_|\___| # # # # cross-table # ####################################################### tm_t_crosstable( label = "Cross Table", x = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], subset = function(data) { idx <- !vapply(data, inherits, logical(1), c("Date", "POSIXct", "POSIXlt")) return(names(data)[idx]) }), selected = "COUNTRY", multiple = TRUE, ordered = TRUE ) ), y = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], subset = function(data) { idx <- vapply(data, is.factor, logical(1)) return(names(data)[idx]) }), selected = "SEX" ) ), decorators = list(insert_rrow_decorator("I am a good new row")) ), # ####################################################################################### # # _ _ _ _ _ _ # | | | | | | | | | | (_) # ___ ___ __ _| |_| |_ ___ _ __ _ __ | | ___ | |_ _ __ ___ __ _| |_ _ __ ___ __ # / __|/ __/ _` | __| __/ _ \ '__| '_ \| |/ _ \| __| | '_ ` _ \ / _` | __| '__| \ \/ / # \__ \ (_| (_| | |_| || __/ | | |_) | | (_) | |_ | | | | | | (_| | |_| | | |> < # |___/\___\__,_|\__|\__\___|_| | .__/|_|\___/ \__| |_| |_| |_|\__,_|\__|_| |_/_/\_\ # | | # |_| # # scatterplot matrix # ###################################################################################### tm_g_scatterplotmatrix( label = "Scatterplot matrix", variables = list( data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]]), selected = c("AGE", "RACE", "SEX"), multiple = TRUE, ordered = TRUE ) ), data_extract_spec( dataname = "ADRS", filter = filter_spec( label = "Select endpoints:", vars = c("PARAMCD", "AVISIT"), choices = value_choices(data[["ADRS"]], c("PARAMCD", "AVISIT"), c("PARAM", "AVISIT")), selected = "INVET - END OF INDUCTION", multiple = TRUE ), select = select_spec( choices = variable_choices(data[["ADRS"]]), selected = c("AGE", "AVAL", "ADY"), multiple = TRUE, ordered = TRUE ) ) ), decorators = list(treelis_subtitle_decorator("I am a Scatterplot matrix", "plot")) ), # ############################################# # # # # _ __ ___ ___ _ __ ___ _ __ ___ ___ # | '__/ _ \/ __| '_ \ / _ \| '_ \/ __|/ _ \ # | | | __/\__ \ |_) | (_) | | | \__ \ __/ # |_| \___||___/ .__/ \___/|_| |_|___/\___| # | | # |_| # # response # ############################################ tm_g_response( label = "Response", response = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]], c("BMRKR2", "COUNTRY"))) ), x = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]], c("SEX", "RACE")), selected = "RACE") ), decorators = list(caption_decorator("I am a Response", "plot")) ), # ############################################ # # _ _ _ _ # | | (_) (_) | | # | |__ ___ ____ _ _ __ _ __ _| |_ ___ # | '_ \| \ \ / / _` | '__| |/ _` | __/ _ \ # | |_) | |\ V / (_| | | | | (_| | || __/ # |_.__/|_| \_/ \__,_|_| |_|\__,_|\__\___| # # # # bivariate # ########################################### tm_g_bivariate( x = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]]), selected = "AGE") ), y = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]]), selected = "SEX") ), row_facet = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]]), selected = "ARM") ), col_facet = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]]), selected = "COUNTRY") ), decorators = list(caption_decorator("I am a Bivariate", "plot")) ), # #################### # # # # _ __ ___ __ _ # | '_ \ / __/ _` | # | |_) | (_| (_| | # | .__/ \___\__,_| # | | # |_| # # pca # ################### tm_a_pca( "PCA", dat = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data = data[["ADSL"]], c("BMRKR1", "AGE", "EOSDY")), selected = c("BMRKR1", "AGE") ) ), decorators = list(caption_decorator("I am a PCA", "plot")) ), ##################################################### # # _ _ _ _ # | | | | | | | | # ___ ___ __ _| |_| |_ ___ _ __ _ __ | | ___ | |_ # / __|/ __/ _` | __| __/ _ \ '__| '_ \| |/ _ \| __| # \__ \ (_| (_| | |_| || __/ | | |_) | | (_) | |_ # |___/\___\__,_|\__|\__\___|_| | .__/|_|\___/ \__| # | | # |_| # # scatterplot # #################################################### tm_g_scatterplot( label = "Scatterplot", x = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1", "BMRKR2"))) ), y = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1", "BMRKR2")), selected = "BMRKR1" ) ), color_by = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1", "BMRKR2", "RACE", "REGION1")), selected = NULL ) ), size_by = data_extract_spec( dataname = "ADSL", select = select_spec(choices = variable_choices(data[["ADSL"]], c("AGE", "BMRKR1"))) ), row_facet = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], c("BMRKR2", "RACE", "REGION1")), selected = NULL ) ), col_facet = data_extract_spec( dataname = "ADSL", select = select_spec( choices = variable_choices(data[["ADSL"]], c("BMRKR2", "RACE", "REGION1")), selected = NULL ) ), decorators = list(caption_decorator("I am a scatterplot", "plot")) ), # ############################################################## # # _ _ _ _ # (_) (_) | | | | # _ __ ___ _ ___ ___ _ _ __ __ _ __| | __ _| |_ __ _ # | '_ ` _ \| / __/ __| | '_ \ / _` | / _` |/ _` | __/ _` | # | | | | | | \__ \__ \ | | | | (_| | | (_| | (_| | || (_| | # |_| |_| |_|_|___/___/_|_| |_|\__, | \__,_|\__,_|\__\__,_| # __/ |_____ # |___/______| # # missing_data # ############################################################# tm_missing_data( label = "Missing data", decorators = list( summary_plot = plot_grob_decorator("A", "summary_plot"), combination_plot = plot_grob_decorator("B", "combination_plot"), summary_table = table_decorator("table", .color1 = "#f0000055"), by_subject_plot = caption_decorator("by_subject_plot") ) ), example_module(decorators = list(head_decorator(6))) ) ) |> shiny::runApp() ``` </details> --------- Signed-off-by: André Veríssimo <[email protected]> Co-authored-by: Marcin <[email protected]>
1 parent 7e81203 commit 4824e27

34 files changed

+757
-424
lines changed

R/roxygen2_templates.R

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# nocov start
2+
roxygen_decorators_param <- function(module_name) {
3+
paste(
4+
sep = " ",
5+
lifecycle::badge("experimental"),
6+
" (`list` of `teal_transform_module`, named `list` of `teal_transform_module` or",
7+
"`NULL`) optional, if not `NULL`, decorator for tables or plots included in the module.",
8+
"When a named list of `teal_transform_module`, the decorators are applied to the",
9+
"respective output objects.\n\n",
10+
"Otherwise, the decorators are applied to all objects, which is equivalent as using the name `default`.\n\n",
11+
sprintf("See section \"Decorating `%s`\"", module_name),
12+
"below for more details."
13+
)
14+
}
15+
16+
roxygen_ggplot2_args_param <- function(...) {
17+
paste(
18+
sep = " ",
19+
"(`ggplot2_args`) optional, object created by [`teal.widgets::ggplot2_args()`]",
20+
"with settings for all the plots or named list of `ggplot2_args` objects for plot-specific settings.",
21+
"The argument is merged with options variable `teal.ggplot2_args` and default module setup.\n\n",
22+
sprintf(
23+
"List names should match the following: `c(\"default\", %s)`.\n\n",
24+
paste("\"", unlist(rlang::list2(...)), "\"", collapse = ", ", sep = "")
25+
),
26+
"For more details see the vignette: `vignette(\"custom-ggplot2-arguments\", package = \"teal.widgets\")`."
27+
)
28+
}
29+
30+
# nocov end

R/tm_a_pca.R

Lines changed: 103 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,40 @@
1313
#' It controls the font size for plot titles, axis labels, and legends.
1414
#' - If vector of `length == 1` then the font sizes will have a fixed size.
1515
#' - while vector of `value`, `min`, and `max` allows dynamic adjustment.
16-
#' @templateVar ggnames "Elbow plot", "Circle plot", "Biplot", "Eigenvector plot"
17-
#' @template ggplot2_args_multi
16+
#' @param ggplot2_args `r roxygen_ggplot2_args_param("Elbow plot", "Circle plot", "Biplot", "Eigenvector plot")`
17+
#' @param decorators `r roxygen_decorators_param("tm_a_pca")`
1818
#'
1919
#' @inherit shared_params return
2020
#'
2121
#' @section Decorating `tm_a_pca`:
2222
#'
2323
#' This module generates the following objects, which can be modified in place using decorators:
24-
#' - `plot` (`ggplot2`)
24+
#' - `elbow_plot` (`ggplot2`)
25+
#' - `circle_plot` (`ggplot2`)
26+
#' - `biplot` (`ggplot2`)
27+
#' - `eigenvector_plot` (`ggplot2`)
28+
#'
29+
#' Decorators can be applied to all outputs or only to specific objects using a
30+
#' named list of `teal_transform_module` objects.
31+
#' The `"default"` name is reserved for decorators that are applied to all outputs.
32+
#' See code snippet below:
33+
#'
34+
#' ```
35+
#' tm_a_pca(
36+
#' ..., # arguments for module
37+
#' decorators = list(
38+
#' default = list(teal_transform_module(...)), # applied to all outputs
39+
#' elbow_plot = list(teal_transform_module(...)), # applied only to `elbow_plot` output
40+
#' circle_plot = list(teal_transform_module(...)) # applied only to `circle_plot` output
41+
#' biplot = list(teal_transform_module(...)) # applied only to `biplot` output
42+
#' eigenvector_plot = list(teal_transform_module(...)) # applied only to `eigenvector_plot` output
43+
#' )
44+
#' )
45+
#' ```
2546
#'
2647
#' For additional details and examples of decorators, refer to the vignette
2748
#' `vignette("decorate-modules-output", package = "teal")` or the [`teal_transform_module()`] documentation.
2849
#'
29-
#'
3050
#' @examplesShinylive
3151
#' library(teal.modules.general)
3252
#' interactive <- function() TRUE
@@ -165,7 +185,9 @@ tm_a_pca <- function(label = "Principal Component Analysis",
165185
checkmate::assert_multi_class(pre_output, c("shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE)
166186
checkmate::assert_multi_class(post_output, c("shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE)
167187

168-
checkmate::assert_list(decorators, "teal_transform_module", null.ok = TRUE)
188+
available_decorators <- c("elbow_plot", "circle_plot", "biplot", "eigenvector_plot")
189+
decorators <- normalize_decorators(decorators)
190+
assert_decorators(decorators, null.ok = TRUE, available_decorators)
169191
# End of assertions
170192

171193
# Make UI args
@@ -240,7 +262,34 @@ ui_a_pca <- function(id, ...) {
240262
choices = args$plot_choices,
241263
selected = args$plot_choices[1]
242264
),
243-
ui_transform_teal_data(ns("decorate"), transformators = args$decorators)
265+
conditionalPanel(
266+
condition = sprintf("input['%s'] == 'Elbow plot'", ns("plot_type")),
267+
ui_decorate_teal_data(
268+
ns("d_elbow_plot"),
269+
decorators = select_decorators(args$decorators, "elbow_plot")
270+
)
271+
),
272+
conditionalPanel(
273+
condition = sprintf("input['%s'] == 'Circle plot'", ns("plot_type")),
274+
ui_decorate_teal_data(
275+
ns("d_circle_plot"),
276+
decorators = select_decorators(args$decorators, "circle_plot")
277+
)
278+
),
279+
conditionalPanel(
280+
condition = sprintf("input['%s'] == 'Biplot'", ns("plot_type")),
281+
ui_decorate_teal_data(
282+
ns("d_biplot"),
283+
decorators = select_decorators(args$decorators, "biplot")
284+
)
285+
),
286+
conditionalPanel(
287+
condition = sprintf("input['%s'] == 'Eigenvector plot'", ns("plot_type")),
288+
ui_decorate_teal_data(
289+
ns("d_eigenvector_plot"),
290+
decorators = select_decorators(args$decorators, "eigenvector_plot")
291+
)
292+
)
244293
),
245294
teal.widgets::panel_item(
246295
title = "Pre-processing",
@@ -565,7 +614,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
565614
)
566615

567616
cols <- c(getOption("ggplot2.discrete.colour"), c("lightblue", "darkred", "black"))[1:3]
568-
plot <- ggplot(mapping = aes_string(x = "component", y = "value")) +
617+
elbow_plot <- ggplot(mapping = aes_string(x = "component", y = "value")) +
569618
geom_bar(
570619
aes(fill = "Single variance"),
571620
data = dplyr::filter(elb_dat, metric == "Proportion of Variance"),
@@ -642,7 +691,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
642691
y = sin(seq(0, 2 * pi, length.out = 100))
643692
)
644693

645-
plot <- ggplot(pca_rot) +
694+
circle_plot <- ggplot(pca_rot) +
646695
geom_point(aes_string(x = x_axis, y = y_axis)) +
647696
geom_label(
648697
aes_string(x = x_axis, y = y_axis, label = "label"),
@@ -874,7 +923,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
874923
qenv,
875924
substitute(
876925
expr = {
877-
plot <- plot_call
926+
biplot <- plot_call
878927
},
879928
env = list(
880929
plot_call = Reduce(function(x, y) call("+", x, y), pca_plot_biplot_expr)
@@ -883,8 +932,8 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
883932
)
884933
}
885934

886-
# plot pc_var ----
887-
plot_pc_var <- function(base_q) {
935+
# plot eigenvector_plot ----
936+
plot_eigenvector <- function(base_q) {
888937
pc <- input$pc
889938
ggtheme <- input$ggtheme
890939

@@ -950,7 +999,7 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
950999
expr = {
9511000
pca_rot <- pca$rotation[, pc, drop = FALSE] %>%
9521001
dplyr::as_tibble(rownames = "Variable")
953-
plot <- plot_call
1002+
eigenvector_plot <- plot_call
9541003
},
9551004
env = list(
9561005
pc = pc,
@@ -960,27 +1009,54 @@ srv_a_pca <- function(id, data, reporter, filter_panel_api, dat, plot_height, pl
9601009
)
9611010
}
9621011

963-
# plot final ----
964-
output_q <- reactive({
965-
req(computation())
966-
teal::validate_inputs(iv_r())
967-
teal::validate_inputs(iv_extra, header = "Plot settings are required")
1012+
# qenvs ---
1013+
output_q <- lapply(
1014+
list(
1015+
elbow_plot = plot_elbow,
1016+
circle_plot = plot_circle,
1017+
biplot = plot_biplot,
1018+
eigenvector_plot = plot_eigenvector
1019+
),
1020+
function(fun) {
1021+
reactive({
1022+
req(computation())
1023+
teal::validate_inputs(iv_r())
1024+
teal::validate_inputs(iv_extra, header = "Plot settings are required")
1025+
fun(computation())
1026+
})
1027+
}
1028+
)
1029+
1030+
decorated_q <- mapply(
1031+
function(obj_name, q) {
1032+
srv_decorate_teal_data(
1033+
id = sprintf("d_%s", obj_name),
1034+
data = q,
1035+
decorators = select_decorators(decorators, obj_name),
1036+
expr = reactive({
1037+
substitute(print(.plot), env = list(.plot = as.name(obj_name)))
1038+
}),
1039+
expr_is_reactive = TRUE
1040+
)
1041+
},
1042+
names(output_q),
1043+
output_q
1044+
)
9681045

969-
switch(input$plot_type,
970-
"Elbow plot" = plot_elbow(computation()),
971-
"Circle plot" = plot_circle(computation()),
972-
"Biplot" = plot_biplot(computation()),
973-
"Eigenvector plot" = plot_pc_var(computation()),
1046+
# plot final ----
1047+
decorated_output_q <- reactive({
1048+
switch(req(input$plot_type),
1049+
"Elbow plot" = decorated_q$elbow_plot(),
1050+
"Circle plot" = decorated_q$circle_plot(),
1051+
"Biplot" = decorated_q$biplot(),
1052+
"Eigenvector plot" = decorated_q$eigenvector_plot(),
9741053
stop("Unknown plot")
9751054
)
9761055
})
9771056

978-
decorated_output_q_no_print <- srv_transform_teal_data("decorate", data = output_q, transformators = decorators)
979-
decorated_output_q <- reactive(within(decorated_output_q_no_print(), expr = print(plot)))
980-
9811057
plot_r <- reactive({
982-
req(output_q())
983-
decorated_output_q()[["plot"]]
1058+
plot_name <- gsub(" ", "_", tolower(req(input$plot_type)))
1059+
req(decorated_output_q())[[plot_name]]
9841060
})
9851061

9861062
pws <- teal.widgets::plot_with_settings_srv(

R/tm_a_regression.R

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@
3737
#' It takes the form of `c(value, min, max)` and it is passed to the `value_min_max`
3838
#' argument in `teal.widgets::optionalSliderInputValMinMax`.
3939
#'
40-
#' @templateVar ggnames `r regression_names`
41-
#' @template ggplot2_args_multi
40+
# nolint start: line_length.
41+
#' @param ggplot2_args `r roxygen_ggplot2_args_param("Response vs Regressor", "Residuals vs Fitted", "Scale-Location", "Cook's distance", "Residuals vs Leverage", "Cook's dist vs Leverage")`
42+
# nolint end: line_length.
43+
#' @param decorators `r roxygen_decorators_param("tm_a_regression")`
4244
#'
4345
#' @inherit shared_params return
4446
#'
@@ -1034,8 +1036,3 @@ srv_a_regression <- function(id,
10341036
###
10351037
})
10361038
}
1037-
1038-
regression_names <- paste0(
1039-
'"Response vs Regressor", "Residuals vs Fitted", ',
1040-
'"Scale-Location", "Cook\'s distance", "Residuals vs Leverage"", "Cook\'s dist vs Leverage"'
1041-
)

R/tm_data_table.R

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ tm_data_table <- function(label = "Data Table",
131131
checkmate::assert_multi_class(pre_output, c("shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE)
132132
checkmate::assert_multi_class(post_output, c("shiny.tag", "shiny.tag.list", "html"), null.ok = TRUE)
133133

134-
checkmate::assert_list(decorators, "teal_transform_module", null.ok = TRUE)
134+
decorators <- normalize_decorators(decorators)
135+
assert_decorators(decorators, null.ok = TRUE, "table")
135136
# End of assertions
136137

137138
ans <- module(
@@ -296,7 +297,7 @@ ui_data_table <- function(id,
296297
tagList(
297298
teal.widgets::get_dt_rows(ns("data_table"), ns("dt_rows")),
298299
fluidRow(
299-
ui_transform_teal_data(ns("decorate"), transformators = decorators),
300+
ui_decorate_teal_data(ns("decorator"), decorators = select_decorators(decorators, "table")),
300301
teal.widgets::optionalSelectInput(
301302
ns("variables"),
302303
"Select variables:",
@@ -365,13 +366,15 @@ srv_data_table <- function(id,
365366
)
366367
})
367368

368-
decorated_data_table_data <-
369-
srv_transform_teal_data("decorate", data = data_table_data, transformators = decorators)
369+
decorated_data_table_data <- srv_decorate_teal_data(
370+
id = "decorator",
371+
data = data_table_data,
372+
decorators = select_decorators(decorators, "table")
373+
)
370374

371375
output$data_table <- DT::renderDataTable(server = server_rendering, {
372-
req(data_table_data())
373376
teal::validate_inputs(iv)
374-
decorated_data_table_data()[["table"]]
377+
req(decorated_data_table_data())[["table"]]
375378
})
376379
})
377380
}

R/tm_g_association.R

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,15 @@
2020
#' @param distribution_theme,association_theme (`character`) optional, `ggplot2` themes to be used by default.
2121
#' Default to `"gray"`.
2222
#'
23-
#' @templateVar ggnames "Bivariate1", "Bivariate2"
24-
#' @template ggplot2_args_multi
23+
#' @param ggplot2_args `r roxygen_ggplot2_args_param("Bivariate1", "Bivariate2")`
24+
#' @param decorators `r roxygen_decorators_param("tm_")`
2525
#'
2626
#' @inherit shared_params return
2727
#'
2828
#' @section Decorating `tm_g_association`:
2929
#'
3030
#' This module generates the following objects, which can be modified in place using decorators:
31-
#' - `plot_top` (`ggplot2`)
32-
#' - `plot_bottom` (`ggplot2`)
31+
#' - `plot` (`grob` created with [ggplot2::ggplotGrob()])
3332
#'
3433
#' For additional details and examples of decorators, refer to the vignette
3534
#' `vignette("decorate-modules-output", package = "teal")` or the [`teal_transform_module()`] documentation.
@@ -176,7 +175,10 @@ tm_g_association <- function(label = "Association",
176175
plot_choices <- c("Bivariate1", "Bivariate2")
177176
checkmate::assert_list(ggplot2_args, types = "ggplot2_args")
178177
checkmate::assert_subset(names(ggplot2_args), c("default", plot_choices))
179-
checkmate::assert_list(decorators, "teal_transform_module", null.ok = TRUE)
178+
179+
decorators <- normalize_decorators(decorators)
180+
assert_decorators(decorators, null.ok = TRUE, "plot")
181+
180182
# End of assertions
181183

182184
# Make UI args
@@ -247,7 +249,7 @@ ui_tm_g_association <- function(id, ...) {
247249
"Log transformed",
248250
value = FALSE
249251
),
250-
ui_transform_teal_data(ns("decorate"), transformators = args$decorators),
252+
ui_decorate_teal_data(ns("decorator"), decorators = select_decorators(args$decorators, "plot")),
251253
teal.widgets::panel_group(
252254
teal.widgets::panel_item(
253255
title = "Plot settings",
@@ -405,8 +407,6 @@ srv_tm_g_association <- function(id,
405407
# association
406408
ref_class_cov <- ifelse(association, ref_class, "NULL")
407409

408-
print_call <- quote(print(p))
409-
410410
var_calls <- lapply(vars_names, function(var_i) {
411411
var_class <- class(ANL[[var_i]])[1]
412412
if (is.numeric(ANL[[var_i]]) && log_transformation) {
@@ -488,6 +488,7 @@ srv_tm_g_association <- function(id,
488488
expr = {
489489
plot_top <- plot_calls[[1]]
490490
plot_bottom <- plot_calls[[1]]
491+
plot <- tern::stack_grobs(grobs = lapply(list(plot_top, plot_bottom), ggplotGrob))
491492
},
492493
env = list(
493494
plot_calls = do.call(
@@ -500,23 +501,19 @@ srv_tm_g_association <- function(id,
500501
)
501502
})
502503

503-
decorated_output_q <- srv_transform_teal_data("decorate", data = output_q, transformators = decorators)
504-
decorated_output_grob_q <- reactive({
505-
within(
506-
decorated_output_q(),
507-
{
508-
plot <- tern::stack_grobs(grobs = lapply(list(plot_top, plot_bottom), ggplotGrob))
509-
grid::grid.newpage()
510-
grid::grid.draw(plot)
511-
}
512-
)
513-
})
514-
504+
decorated_output_grob_q <- srv_decorate_teal_data(
505+
id = "decorator",
506+
data = output_q,
507+
decorators = select_decorators(decorators, "plot"),
508+
expr = {
509+
grid::grid.newpage()
510+
grid::grid.draw(plot)
511+
}
512+
)
515513

516514
plot_r <- reactive({
517515
req(iv_r()$is_valid())
518-
req(output_q())
519-
decorated_output_grob_q()[["plot"]]
516+
req(decorated_output_grob_q())[["plot"]]
520517
})
521518

522519
pws <- teal.widgets::plot_with_settings_srv(

0 commit comments

Comments
 (0)