Skip to content

Commit

Permalink
Reimplement onDone behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
FrancoisGuillem committed Jul 28, 2017
1 parent 7d1ed3b commit f9dc0f7
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 98 deletions.
14 changes: 12 additions & 2 deletions R/controller.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Controller <- setRefClass(
"Controller",
fields = c("inputList", "envs", "session", "output", "expr", "ncharts", "charts",
"autoUpdate", "renderFunc"),
"autoUpdate", "renderFunc", "useCombineWidgets"),
methods = list(

initialize = function(expr, inputs, autoUpdate = TRUE) {
Expand All @@ -13,13 +13,18 @@ Controller <- setRefClass(
renderFunc <<- NULL
session <<- NULL
output <<- NULL
useCombineWidgets <<- FALSE
charts <<- list()
},

setShinySession = function(output, session) {
session <<- session
output <<- output
inputList$session <<- session
for (env in envs) {
assign(".initial", FALSE, envir = env)
assign(".session", session, envir = env)
}
},

getValue = function(name, chartId = 1) {
Expand Down Expand Up @@ -59,6 +64,9 @@ Controller <- setRefClass(
updateChart = function(chartId = 1) {
catIfDebug("Update chart", chartId)
charts[[chartId]] <<- eval(expr, envir = envs[[chartId]])
if (useCombineWidgets) {
charts[[chartId]] <<- combineWidgets(charts[[chartId]])
}
renderShinyOutput(chartId)
},

Expand All @@ -67,7 +75,8 @@ Controller <- setRefClass(
},

renderShinyOutput = function(chartId) {
if (!is.null(renderFunc) & !is.null(output)) {
if (!is.null(renderFunc) & !is.null(output) &
is(charts[[chartId]], "htmlwidget")) {
outputId <- get(".output", envir = envs[[chartId]])
output[[outputId]] <<- renderFunc(charts[[chartId]])
}
Expand Down Expand Up @@ -104,6 +113,7 @@ Controller <- setRefClass(
)
res$renderFunc <- renderFunc
res$charts <- charts
res$useCombineWidgets <- useCombineWidgets
res
}
)
Expand Down
2 changes: 1 addition & 1 deletion R/inputs.R
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ mwGroup <- function(..., .display = TRUE) {
tags$div(
class="panel-body collapse",
id=paste0("panel-body-", id),
tagList(htmlElements)
shiny::tagList(htmlElements)
)
)
}
Expand Down
10 changes: 9 additions & 1 deletion R/manipulateWidget.R
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ manipulateWidget <- function(.expr, ..., .updateBtn = FALSE,
}

controller$renderFunc <- renderFunction
if (useCombineWidgets) {
controller$useCombineWidgets <- TRUE
controller$charts <- lapply(controller$charts, combineWidgets)
}


dims <- .getRowAndCols(.compareOpts$ncharts, .compareOpts$nrow, .compareOpts$ncol)

Expand All @@ -281,12 +286,15 @@ manipulateWidget <- function(.expr, ..., .updateBtn = FALSE,
controller$setShinySession(output, session)
controller$renderShinyOutputs()

message("Click on the 'OK' button to return to the R session.")

observe({
for (id in names(controller$inputList$inputs)) {
controller$setValueById(id, input[[id]])
}
})

observeEvent(input$done, onDone(controller, .return, dims$nrow, dims$ncol))
}

if (interactive()) {
Expand All @@ -304,6 +312,6 @@ manipulateWidget <- function(.expr, ..., .updateBtn = FALSE,
} else {
# Other cases (Rmarkdown or non interactive execution). We return the initial
# widget to not block the R execution.
mwReturn(controller$widgets, .return, controls$env$ind, dims$nrow, dims$ncol)
mwReturn(controller$charts, .return, controller$envs, dims$nrow, dims$ncol)
}
}
14 changes: 7 additions & 7 deletions R/mwServer_helpers.R
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,14 @@ getUpdateInputFun <- function(type) {
#'
#' @return a htmlwidget
#' @noRd
onDone <- function(.expr, controls, .return = function(w, e) {w}, nrow = NULL, ncol = NULL) {
widgets <- lapply(controls$env$ind, function(e) {
assign(".initial", TRUE, envir = e)
assign(".session", NULL, envir = e)
eval(.expr, envir = e)
})
onDone <- function(controller, .return = function(w, e) {w}, nrow = NULL, ncol = NULL) {
for (env in controller$envs) {
assign(".initial", TRUE, envir = env)
assign(".session", NULL, envir = env)
}
controller$updateCharts()

shiny::stopApp(mwReturn(widgets, .return, controls$env$ind, nrow, ncol))
shiny::stopApp(mwReturn(controller$charts, .return, controls$env$ind, nrow, ncol))
}

#' Function that takes a list of widgets and returns the first one if there is
Expand Down
174 changes: 87 additions & 87 deletions R/zzz.R
Original file line number Diff line number Diff line change
@@ -1,87 +1,87 @@
# Copyright © 2016 RTE Réseau de transport d’électricité

#' @name manipulateWidget-package
#'
#' @title Add even more interactivity to interactive charts
#'
#' @description
#' This package is largely inspired by the \code{manipulate} package from
#' Rstudio. It can be used to easily create graphical interface that let the
#' user modify the data or the parameters of an interactive chart. It also
#' provides the \code{\link{combineWidgets}} function to easily combine multiple
#' interactive charts in a single view.
#'
#' @details
#' \code{\link{manipulateWidget}} is the main function of the package. It
#' accepts an expression that generates an interactive chart (and more precisely
#' an \code{htmlwidget} object. See \url{http://www.htmlwidgets.org/} if you
#' have never heard about it) and a set of controls created with functions
#' \code{mwSlider}, \code{mwCheckbox}... which are used to dynamically change
#' values within the expression. Each time the user modifies the value of a
#' control, the expression is evaluated again and the chart is updated. Consider
#' the following code:
#'
#' \code{manipulateWidget(myPlotFun(country), country = mwSelect(c("BE", "DE", "ES", "FR")))}
#'
#' It will generate a graphical interface with a select input on its left with
#' options "BE", "DE", "ES", "FR". By default, at the beginning the value of the
#' variable \code{country} will be equal to the first choice of the
#' corresponding input. So the function will first execute
#' \code{myPlotFun("BE")} and the result will be displayed in the main panel of
#' the interface. If the user changes the value to "FR", then the expression
#' \code{myPlotFun("FR")} is evaluated and the new result is displayed.
#'
#' The interface also contains a button "Done". When the user clicks on it, the
#' last chart is returned. It can be stored in a variable, be modified by the
#' user, saved as a html file with \code{\link[htmlwidgets]{saveWidget}} from package
#' \code{htmlwidgets} or converted to a static image file with package
#' \code{webshot}.
#'
#' Finally one can easily create complex layouts thanks to function
#' \code{\link{combineWidgets}}. For instance, assume we want to see a map that
#' displays values of some variable for a given year, but on its right side we also
#' want to see the distributions of three variables. Then we could write:
#'
#' \preformatted{
#' myPlotFun <- function(year, variable) {
#' combineWidgets(
#' ncol = 2, colSize = c(3, 1),
#' myMap(year, variable),
#' combineWidgets(
#' ncol = 1,
#' myHist(year, "V1"),
#' myHist(year, "V2"),
#' myHist(year, "V3"),
#' )
#' )
#' }
#'
#' manipulateWidget(
#' myPlotFun(year, variable),
#' year = mwSlider(2000, 2016, value = 2000),
#' variable = mwSelect(c("V1", "V2", "V3"))
#' )
#' }
#'
#' Of course, \code{\link{combineWidgets}} can be used outside of
#' \code{\link{manipulateWidget}}. For instance, it can be used in an
#' Rmarkdown document to easily put together interactive charts.
#'
#' For more concrete examples of usage, you should look at the documentation and
#' especially the examples of \code{\link{manipulateWidget}} and
#' \code{\link{combineWidgets}}.
#'
#' @seealso \code{\link{manipulateWidget}}, \code{\link{combineWidgets}}
#'
#' @rdname manipulateWidget-package
#' @docType package
#' @importFrom shiny tags observe observeEvent reactive isolate icon tagAppendChild
#' @importFrom shiny tagAppendChildren fillPage fillRow
#' @importFrom miniUI miniContentPanel miniPage miniTabPanel miniTabstripPanel gadgetTitleBar
#' @importFrom htmlwidgets getDependency
#' @importFrom methods is
#' @importFrom utils getFromNamespace
#' @importFrom stats runif
NULL
#
globalVariables(c("mod", "multiple", "name", "type"))
# Copyright © 2016 RTE Réseau de transport d’électricité

#' @name manipulateWidget-package
#'
#' @title Add even more interactivity to interactive charts
#'
#' @description
#' This package is largely inspired by the \code{manipulate} package from
#' Rstudio. It can be used to easily create graphical interface that let the
#' user modify the data or the parameters of an interactive chart. It also
#' provides the \code{\link{combineWidgets}} function to easily combine multiple
#' interactive charts in a single view.
#'
#' @details
#' \code{\link{manipulateWidget}} is the main function of the package. It
#' accepts an expression that generates an interactive chart (and more precisely
#' an \code{htmlwidget} object. See \url{http://www.htmlwidgets.org/} if you
#' have never heard about it) and a set of controls created with functions
#' \code{mwSlider}, \code{mwCheckbox}... which are used to dynamically change
#' values within the expression. Each time the user modifies the value of a
#' control, the expression is evaluated again and the chart is updated. Consider
#' the following code:
#'
#' \code{manipulateWidget(myPlotFun(country), country = mwSelect(c("BE", "DE", "ES", "FR")))}
#'
#' It will generate a graphical interface with a select input on its left with
#' options "BE", "DE", "ES", "FR". By default, at the beginning the value of the
#' variable \code{country} will be equal to the first choice of the
#' corresponding input. So the function will first execute
#' \code{myPlotFun("BE")} and the result will be displayed in the main panel of
#' the interface. If the user changes the value to "FR", then the expression
#' \code{myPlotFun("FR")} is evaluated and the new result is displayed.
#'
#' The interface also contains a button "Done". When the user clicks on it, the
#' last chart is returned. It can be stored in a variable, be modified by the
#' user, saved as a html file with \code{\link[htmlwidgets]{saveWidget}} from package
#' \code{htmlwidgets} or converted to a static image file with package
#' \code{webshot}.
#'
#' Finally one can easily create complex layouts thanks to function
#' \code{\link{combineWidgets}}. For instance, assume we want to see a map that
#' displays values of some variable for a given year, but on its right side we also
#' want to see the distributions of three variables. Then we could write:
#'
#' \preformatted{
#' myPlotFun <- function(year, variable) {
#' combineWidgets(
#' ncol = 2, colSize = c(3, 1),
#' myMap(year, variable),
#' combineWidgets(
#' ncol = 1,
#' myHist(year, "V1"),
#' myHist(year, "V2"),
#' myHist(year, "V3"),
#' )
#' )
#' }
#'
#' manipulateWidget(
#' myPlotFun(year, variable),
#' year = mwSlider(2000, 2016, value = 2000),
#' variable = mwSelect(c("V1", "V2", "V3"))
#' )
#' }
#'
#' Of course, \code{\link{combineWidgets}} can be used outside of
#' \code{\link{manipulateWidget}}. For instance, it can be used in an
#' Rmarkdown document to easily put together interactive charts.
#'
#' For more concrete examples of usage, you should look at the documentation and
#' especially the examples of \code{\link{manipulateWidget}} and
#' \code{\link{combineWidgets}}.
#'
#' @seealso \code{\link{manipulateWidget}}, \code{\link{combineWidgets}}
#'
#' @rdname manipulateWidget-package
#' @docType package
#' @importFrom shiny tags observe observeEvent reactive isolate icon tagAppendChild
#' @importFrom shiny tagAppendChildren fillPage fillRow
#' @importFrom miniUI miniContentPanel miniPage miniTabPanel miniTabstripPanel gadgetTitleBar
#' @importFrom htmlwidgets getDependency
#' @importFrom methods is new setRefClass
#' @importFrom utils getFromNamespace
#' @importFrom stats runif
NULL
#
globalVariables(c("mod", "multiple", "name", "type"))

0 comments on commit f9dc0f7

Please sign in to comment.