From dd474e2ecf26639a52419b5de025b097f6c8d581 Mon Sep 17 00:00:00 2001 From: Carson Sievert Date: Fri, 24 May 2019 14:29:32 -0500 Subject: [PATCH 1/3] Wrap user-supplied expression in evalq(), closes #1528 --- R/shiny.R | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/R/shiny.R b/R/shiny.R index 65877d6526..5f52b424b2 100644 --- a/R/shiny.R +++ b/R/shiny.R @@ -38,8 +38,14 @@ plotlyOutput <- function(outputId, width = "100%", height = "400px", #' @export renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) { if (!quoted) { expr <- substitute(expr) } # force quoted - # this makes it possible to pass a ggplot2 object to renderPlotly() - # https://github.com/ramnathv/htmlwidgets/issues/166#issuecomment-153000306 + # Wrap the (user-supplied) expression with a call to evalq() + # This way, if the user-supplied expression contains a return() + # statement, we can capture that return value and pass it along + # to prepareWidget() + expr <- call("evalq", expr, env) + # prepareWidget() makes it possible to pass different non-plotly + # objects to renderPlotly() (e.g., ggplot2, promises). It also is used + # to inform event_data about what events have been registered expr <- as.call(list(call(":::", quote("plotly"), quote("prepareWidget")), expr)) renderFunc <- shinyRenderWidget(expr, plotlyOutput, env, quoted = TRUE) # remove 'internal' plotly attributes that are known to cause false From af1c77085baac8477d4c14afe16232f89d3f7571 Mon Sep 17 00:00:00 2001 From: Carson Date: Mon, 26 Aug 2019 18:49:56 -0500 Subject: [PATCH 2/3] Use shiny::installExprFunction to register debug hooks --- R/shiny.R | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/R/shiny.R b/R/shiny.R index 5f52b424b2..1893f7c111 100644 --- a/R/shiny.R +++ b/R/shiny.R @@ -37,17 +37,21 @@ plotlyOutput <- function(outputId, width = "100%", height = "400px", #' @rdname plotly-shiny #' @export renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) { - if (!quoted) { expr <- substitute(expr) } # force quoted - # Wrap the (user-supplied) expression with a call to evalq() + if (!quoted) { + quoted <- TRUE + expr <- substitute(expr) + } + # Install the (user-supplied) expression as a function # This way, if the user-supplied expression contains a return() # statement, we can capture that return value and pass it along # to prepareWidget() - expr <- call("evalq", expr, env) # prepareWidget() makes it possible to pass different non-plotly # objects to renderPlotly() (e.g., ggplot2, promises). It also is used # to inform event_data about what events have been registered - expr <- as.call(list(call(":::", quote("plotly"), quote("prepareWidget")), expr)) - renderFunc <- shinyRenderWidget(expr, plotlyOutput, env, quoted = TRUE) + shiny::installExprFunction(expr, "func", env, quoted) + renderFunc <- shinyRenderWidget( + plotly:::prepareWidget(func()), plotlyOutput, env, quoted + ) # remove 'internal' plotly attributes that are known to cause false # positive test results in shinytest (snapshotPreprocessOutput was added # in shiny 1.0.3.9002, but we require >= 1.1) From 38677372fe0501d6d1ed79d4fc0038032b8302d8 Mon Sep 17 00:00:00 2001 From: Carson Date: Thu, 29 Aug 2019 16:42:28 -0500 Subject: [PATCH 3/3] Asynchronously register plot events --- R/shiny.R | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/R/shiny.R b/R/shiny.R index 1893f7c111..8dacd9857c 100644 --- a/R/shiny.R +++ b/R/shiny.R @@ -67,13 +67,14 @@ renderPlotly <- function(expr, env = parent.frame(), quoted = FALSE) { # Converts a plot, OR a promise of a plot, to plotly prepareWidget <- function(x) { - p <- if (promises::is.promising(x)) { - promises::then(x, plotly_build) + if (promises::is.promising(x)) { + promises::then( + promises::then(x, plotly_build), + register_plot_events + ) } else { - plotly_build(x) + register_plot_events(plotly_build(x)) } - register_plot_events(p) - p } register_plot_events <- function(p) { @@ -83,6 +84,7 @@ register_plot_events <- function(p) { session$userData$plotlyShinyEventIDs, eventIDs )) + p }