Compare commits

...

66 Commits

Author SHA1 Message Date
Winston Chang
6e7e8eb44a Bump version to 0.4.0 2013-02-21 16:50:01 -06:00
Winston Chang
308c583254 setRatePolicy based on effectiveId. Fixes #110
Previously when getType() was defined for a type of object, shiny.js would
send updates immediately instead of applying the rate policy.
2013-02-20 11:39:22 -06:00
Winston Chang
97b2f7e5ca Fix call to manageHiddenOutputs in timer callback. Fixes #112 2013-02-19 12:20:49 -06:00
Winston Chang
3ea88a07d9 sliderInputBinding inherits from text instead of number. Fixes #110 2013-02-18 22:25:38 -06:00
Winston Chang
588f8bb96a Merge pull request #107 from wch/numeric-na
Empty numericInput gets converted to NA
2013-02-18 14:01:04 -08:00
Winston Chang
c93c0dd721 Update NEWS 2013-02-18 16:00:25 -06:00
Winston Chang
fc59c254fd Merge pull request #108 from wch/unused-hidden
Treat unused outputs as hidden
2013-02-18 13:56:49 -08:00
Winston Chang
2f8b6a150f Treat unused outputs as hidden 2013-02-18 15:53:31 -06:00
Winston Chang
db60ac5c17 Empty numericInput gets converted to NA 2013-02-18 15:11:41 -06:00
Winston Chang
e1f09853c5 Make shiny.deprecation.messages option actually work 2013-02-17 16:17:41 -06:00
Winston Chang
24656713a5 Remove unnecessary function() in renderXX 2013-02-17 12:02:00 -06:00
Winston Chang
7dd0269292 Update NEWS 2013-02-14 14:09:42 -06:00
Winston Chang
8b87cea7aa Merge pull request #104 from wch/reactive-exp
Change reactive() and observe() to take expressions
2013-02-14 12:08:18 -08:00
Winston Chang
c7559a6946 Suspend overwritten output objects 2013-02-14 12:14:08 -06:00
Winston Chang
945c6080ad Export exprToFunction 2013-02-14 11:48:01 -06:00
Winston Chang
44590965d1 Add renderXX Rd files 2013-02-14 11:48:01 -06:00
Winston Chang
7ab64d678f reactivePlot: call height and width properly 2013-02-14 11:48:01 -06:00
Winston Chang
e406a76b62 Update documentation for renderXX 2013-02-14 11:48:01 -06:00
Winston Chang
e26f175a8f Change reactiveXX to renderXX 2013-02-13 12:11:39 -06:00
Winston Chang
d4ab84745d Make function for expr-to-function conversion 2013-02-12 15:55:51 -06:00
Winston Chang
32dbc3101e Add shinyDeprecated function 2013-02-12 15:24:50 -06:00
Winston Chang
0a924eb718 Fix deprecation message for observe() 2013-02-12 15:24:50 -06:00
Winston Chang
a284327bfc Re-roxygenize 2013-02-12 15:24:50 -06:00
Winston Chang
2ea38d6ecc Clean up instances of reactive() and observe() 2013-02-12 15:24:50 -06:00
Winston Chang
6a34bbfddd Add label argument to reactive and observe 2013-02-12 15:24:50 -06:00
Winston Chang
58323ada4b Change references of reactive 'functions' to 'expressions' 2013-02-12 15:24:49 -06:00
Winston Chang
5fd723cb80 reactive() and observe() now take expressions 2013-02-12 15:24:49 -06:00
Winston Chang
5c626e6957 Documentation fixes 2013-02-12 15:24:39 -06:00
Winston Chang
5d949842eb Add garbage collection tests 2013-02-11 20:26:23 -06:00
Winston Chang
b595c17d78 observe: add option to start suspended 2013-02-11 19:48:22 -06:00
Winston Chang
b84973ba2b Remove leftover testing string 2013-02-11 19:36:06 -06:00
Winston Chang
61be49e7b2 Merge pull request #97 from wch/suspend-hidden
Suspend hidden outputs. Fixes #24
2013-02-11 16:48:39 -08:00
Winston Chang
8faf5659ee Re-roxygenize 2013-02-11 18:47:53 -06:00
Winston Chang
cc9267a646 manageHiddenOutputs: check that output object exists 2013-02-11 18:45:45 -06:00
Winston Chang
55838bb032 Call manageHiddenOutputs after timer callbacks 2013-02-11 18:37:18 -06:00
Winston Chang
67619ac5e8 Don't allow another flush if currently in one 2013-02-11 18:35:32 -06:00
Winston Chang
952b342859 Better checks for hidden output objects 2013-02-11 18:31:44 -06:00
Winston Chang
c7149c460d Add documentation for suspendWhenHidden option 2013-02-11 16:08:30 -06:00
Winston Chang
fd0613ea0e Call manageHiddenOutputs when suspendWhenHidden is set 2013-02-11 15:16:04 -06:00
Winston Chang
36d2dddc59 Run manageHiddenOutputs on app init 2013-02-09 00:02:52 -06:00
Joe Cheng
63c5b05584 Stop extra update message from occurring on startup 2013-02-08 16:37:55 -08:00
Winston Chang
4b235e5b87 Send output hidden state on init 2013-02-07 14:29:03 -06:00
Winston Chang
6c51fffdaa Fix tests 2013-02-07 14:29:03 -06:00
Winston Chang
5d6d638c85 Clarify suspend description 2013-02-07 14:29:03 -06:00
Winston Chang
90eb515167 Observer: .flushCallbacks to .invalidateCallbacks 2013-02-07 14:29:03 -06:00
Joe Cheng
17526711a2 Change resume behavior for Observer
Eliminate multiple runs when resumed multiple times
2013-02-07 14:29:03 -06:00
Winston Chang
cf0118e090 Add tests for suspended observers 2013-02-07 14:29:03 -06:00
Winston Chang
868d6fec42 Add suspended option to Observer 2013-02-07 14:29:03 -06:00
Winston Chang
851f5854bf Add outputOptions function 2013-02-07 14:29:03 -06:00
Winston Chang
eb5428c971 Suspend hidden outputs 2013-02-07 14:29:03 -06:00
Winston Chang
81188df7ef Update runUrl help and re-document 2013-02-07 10:46:20 -06:00
Winston Chang
9fd365cc41 isolate help: mention debugging use and fix typos 2013-02-06 14:38:12 -06:00
Winston Chang
999df6e40f httpResponse: make sure headers is a list. Fixes #102 2013-02-06 12:29:24 -06:00
Winston Chang
076d069568 runGist: accept new URL format with username 2013-02-06 12:06:14 -06:00
Joe Cheng
2738648197 Merge pull request #101 from jcheng5/chrome-frame
Chrome Frame compatibility
2013-02-05 15:18:03 -08:00
Joe Cheng
36013009a1 Chrome Frame compatibility 2013-02-05 15:15:03 -08:00
Winston Chang
1b60233862 Fix closing brace in isolate help 2013-02-05 10:56:54 -06:00
Winston Chang
2cba10dd05 Follow redirects with curl for http
The previous logic added the -L option to curl when downloading https, but
    not for http.
2013-02-04 13:06:15 -06:00
Winston Chang
b3944127ea Add note about using local() with isolate() 2013-02-01 15:16:33 -05:00
Winston Chang
f1674378ca Remove unneeded reactive() wrappers 2013-01-31 15:47:02 -05:00
Winston Chang
6f0191e1cf Block some operators for shinyoutput objects 2013-01-31 15:45:31 -05:00
Winston Chang
1848844be6 Cleaner method for creating objects with class 2013-01-30 15:06:17 -05:00
Winston Chang
8b6362c749 Add section markers 2013-01-30 15:04:55 -05:00
Winston Chang
d860d13361 Add comments to test 2013-01-30 15:04:50 -05:00
Winston Chang
4b077dbf4c Observers can be suspended/resumed 2013-01-30 14:47:19 -05:00
Winston Chang
40f73bbfe2 Bump version to 3.1.99 for development 2013-01-30 13:51:54 -05:00
51 changed files with 1721 additions and 561 deletions

View File

@@ -1,7 +1,7 @@
Package: shiny
Type: Package
Title: Web Application Framework for R
Version: 0.3.1
Version: 0.4.0
Date: 2013-01-23
Author: RStudio, Inc.
Maintainer: Winston Chang <winston@rstudio.com>

View File

@@ -1,9 +1,13 @@
S3method("$",reactivevalues)
S3method("$",shinyoutput)
S3method("$<-",reactivevalues)
S3method("$<-",shinyoutput)
S3method("[",reactivevalues)
S3method("[",shinyoutput)
S3method("[<-",reactivevalues)
S3method("[<-",shinyoutput)
S3method("[[",reactivevalues)
S3method("[[",shinyoutput)
S3method("[[<-",reactivevalues)
S3method("[[<-",shinyoutput)
S3method("names<-",reactivevalues)
@@ -15,8 +19,6 @@ S3method(format,shiny.tag.list)
S3method(names,reactivevalues)
S3method(print,shiny.tag)
S3method(print,shiny.tag.list)
S3method(reactive,"function")
S3method(reactive,default)
export(HTML)
export(a)
export(addResourcePath)
@@ -32,6 +34,7 @@ export(downloadButton)
export(downloadHandler)
export(downloadLink)
export(em)
export(exprToFunction)
export(fileInput)
export(h1)
export(h2)
@@ -51,6 +54,7 @@ export(isolate)
export(mainPanel)
export(numericInput)
export(observe)
export(outputOptions)
export(p)
export(pageWithSidebar)
export(plotOutput)
@@ -65,6 +69,11 @@ export(reactiveTimer)
export(reactiveUI)
export(reactiveValues)
export(reactiveValuesToList)
export(renderPlot)
export(renderPrint)
export(renderTable)
export(renderText)
export(renderUI)
export(repeatable)
export(runApp)
export(runExample)

19
NEWS
View File

@@ -1,3 +1,22 @@
shiny 0.4.0
--------------------------------------------------------------------------------
* Added suspend/resume capability to observers.
* Output objects are automatically suspended when they are hidden on the user's
web browser.
* `runGist()` accepts GitHub's new URL format, which includes the username.
* `reactive()` and `observe()` now take expressions instead of functions.
* `reactiveText()`, `reactivePlot()`, and so on, have been renamed to
`renderText()`, `renderPlot()`, etc. They also now take expressions instead
of functions.
* Fixed a bug where empty values in a numericInput were sent to the R process
as 0. They are now sent as NA.
shiny 0.3.1
--------------------------------------------------------------------------------

View File

@@ -708,7 +708,7 @@ tabsetPanel <- function(..., id = NULL) {
#' @param outputId output variable to read the value from
#' @return A text output element that can be included in a panel
#' @details Text is HTML-escaped prior to rendering. This element is often used
#' to dispaly \link{reactiveText} output variables.
#' to display \link{renderText} output variables.
#' @examples
#' h3(textOutput("caption"))
#' @export
@@ -723,7 +723,7 @@ textOutput <- function(outputId) {
#' @param outputId output variable to read the value from
#' @return A verbatim text output element that can be included in a panel
#' @details Text is HTML-escaped prior to rendering. This element is often used
#' with the \link{reactivePrint} function to preserve fixed-width formatting
#' with the \link{renderPrint} function to preserve fixed-width formatting
#' of printed objects.
#' @examples
#' mainPanel(
@@ -740,7 +740,7 @@ verbatimTextOutput <- function(outputId) {
#' Create a plot output element
#'
#' Render a \link{reactivePlot} within an application page.
#' Render a \link{renderPlot} within an application page.
#' @param outputId output variable to read the plot from
#' @param width Plot width. Must be a valid CSS unit (like \code{"100\%"},
#' \code{"400px"}, \code{"auto"}) or a number, which will be coerced to a
@@ -761,7 +761,7 @@ plotOutput <- function(outputId, width = "100%", height="400px") {
#' Create a table output element
#'
#' Render a \link{reactiveTable} within an application page.
#' Render a \link{renderTable} within an application page.
#' @param outputId output variable to read the table from
#' @return A table output element that can be included in a panel
#' @examples
@@ -779,7 +779,7 @@ tableOutput <- function(outputId) {
#' text will be included within an HTML \code{div} tag, and is presumed to
#' contain HTML content which should not be escaped.
#'
#' \code{uiOutput} is intended to be used with \code{reactiveUI} on the
#' \code{uiOutput} is intended to be used with \code{renderUI} on the
#' server side. It is currently just an alias for \code{htmlOutput}.
#'
#' @param outputId output variable to read the value from

View File

@@ -68,12 +68,18 @@ Context <- setRefClass(
ReactiveEnvironment <- setRefClass(
'ReactiveEnvironment',
fields = c('.currentContext', '.nextId', '.pendingFlush'),
fields = list(
.currentContext = 'ANY',
.nextId = 'integer',
.pendingFlush = 'list',
.inFlush = 'logical'
),
methods = list(
initialize = function() {
.currentContext <<- NULL
.nextId <<- 0L
.pendingFlush <<- list()
.inFlush <<- FALSE
},
nextId = function() {
.nextId <<- .nextId + 1L
@@ -96,6 +102,11 @@ ReactiveEnvironment <- setRefClass(
.pendingFlush <<- c(ctx, .pendingFlush)
},
flush = function() {
# If already in a flush, don't start another one
if (.inFlush) return()
.inFlush <<- TRUE
on.exit(.inFlush <<- FALSE)
while (length(.pendingFlush) > 0) {
ctx <- .pendingFlush[[1]]
.pendingFlush <<- .pendingFlush[-1]

View File

@@ -26,6 +26,8 @@ Dependents <- setRefClass(
)
# ReactiveValues ------------------------------------------------------------
ReactiveValues <- setRefClass(
'ReactiveValues',
fields = list(
@@ -112,13 +114,14 @@ ReactiveValues <- setRefClass(
)
# reactivevalues: S3 wrapper class for Values class -----------------------
# reactivevalues ------------------------------------------------------------
# S3 wrapper class for ReactiveValues reference class
#' Create an object for storing reactive values
#'
#' This function returns an object for storing reactive values. It is similar
#' to a list, but with special capabilities for reactive programming. When you
#' read a value from it, the calling reactive function takes a reactive
#' read a value from it, the calling reactive expression takes a reactive
#' dependency on that value, and when you write to it, it notifies any reactive
#' functions that depend on that value.
#'
@@ -168,10 +171,7 @@ reactiveValues <- function(...) {
# @param values A ReactiveValues object
# @param readonly Should this object be read-only?
.createReactiveValues <- function(values = NULL, readonly = FALSE) {
acc <- list(impl=values)
class(acc) <- 'reactivevalues'
attr(acc, 'readonly') <- readonly
return(acc)
structure(list(impl=values), class='reactivevalues', readonly=readonly)
}
#' @S3method $ reactivevalues
@@ -219,7 +219,7 @@ names.reactivevalues <- function(x) {
#' @S3method as.list reactivevalues
as.list.reactivevalues <- function(x, all.names=FALSE, ...) {
.Deprecated("reactiveValuesToList",
shinyDeprecated("reactiveValuesToList",
msg = paste("'as.list.reactivevalues' is deprecated. ",
"Use reactiveValuesToList instead.",
"\nPlease see ?reactiveValuesToList for more information.",
@@ -254,13 +254,15 @@ reactiveValuesToList <- function(x, all.names=FALSE) {
.subset2(x, 'impl')$toList(all.names)
}
# Observable ----------------------------------------------------------------
Observable <- setRefClass(
'Observable',
fields = list(
.func = 'function',
.label = 'character',
.dependents = 'Dependents',
.dirty = 'logical',
.invalidated = 'logical',
.running = 'logical',
.value = 'ANY',
.visible = 'logical',
@@ -269,11 +271,11 @@ Observable <- setRefClass(
methods = list(
initialize = function(func, label=deparse(substitute(func))) {
if (length(formals(func)) > 0)
stop("Can't make a reactive function from a function that takes one ",
stop("Can't make a reactive expression from a function that takes one ",
"or more parameters; only functions without parameters can be ",
"reactive.")
.func <<- func
.dirty <<- TRUE
.invalidated <<- TRUE
.running <<- FALSE
.label <<- label
.execCount <<- 0L
@@ -281,7 +283,7 @@ Observable <- setRefClass(
getValue = function() {
.dependents$register()
if (.dirty || .running) {
if (.invalidated || .running) {
.self$.updateValue()
}
@@ -296,12 +298,12 @@ Observable <- setRefClass(
.updateValue = function() {
ctx <- Context$new(.label)
ctx$onInvalidate(function() {
.dirty <<- TRUE
.invalidated <<- TRUE
.dependents$invalidate()
})
.execCount <<- .execCount + 1L
.dirty <<- FALSE
.invalidated <<- FALSE
wasRunning <- .running
.running <<- TRUE
@@ -316,41 +318,60 @@ Observable <- setRefClass(
)
)
#' Create a Reactive Function
#'
#' Wraps a normal function to create a reactive function. Conceptually, a
#' reactive function is a function whose result will change over time.
#'
#' Reactive functions are functions that can read reactive values and call other
#' reactive functions. Whenever a reactive value changes, any reactive functions
#' that depended on it are marked as "invalidated" and will automatically
#' re-execute if necessary. If a reactive function is marked as invalidated, any
#' other reactive functions that recently called it are also marked as
#' invalidated. In this way, invalidations ripple through the functions that
#' Create a reactive expression
#'
#' Wraps a normal expression to create a reactive expression. Conceptually, a
#' reactive expression is a expression whose result will change over time.
#'
#' Reactive expressions are expressions that can read reactive values and call other
#' reactive expressions. Whenever a reactive value changes, any reactive expressions
#' that depended on it are marked as "invalidated" and will automatically
#' re-execute if necessary. If a reactive expression is marked as invalidated, any
#' other reactive expressions that recently called it are also marked as
#' invalidated. In this way, invalidations ripple through the expressions that
#' depend on each other.
#'
#' See the \href{http://rstudio.github.com/shiny/tutorial/}{Shiny tutorial} for
#' more information about reactive functions.
#'
#' @param x The value or function to make reactive. The function must not have
#' any parameters.
#' @return A reactive function. (Note that reactive functions can only be called
#' from within other reactive functions.)
#'
#'
#' See the \href{http://rstudio.github.com/shiny/tutorial/}{Shiny tutorial} for
#' more information about reactive expressions.
#'
#' @param x An expression (quoted or unquoted).
#' @param env The parent environment for the reactive expression. By default, this
#' is the calling environment, the same as when defining an ordinary
#' non-reactive expression.
#' @param quoted Is the expression quoted? By default, this is \code{FALSE}.
#' This is useful when you want to use an expression that is stored in a
#' variable; to do so, it must be quoted with `quote()`.
#' @param label A label for the reactive expression, useful for debugging.
#'
#' @examples
#' values <- reactiveValues(A=1)
#'
#' reactiveB <- reactive({
#' values$A + 1
#' })
#'
#' # Can use quoted expressions
#' reactiveC <- reactive(quote({ values$A + 2 }), quoted = TRUE)
#'
#' # To store expressions for later conversion to reactive, use quote()
#' expr_q <- quote({ values$A + 3 })
#' reactiveD <- reactive(expr_q, quoted = TRUE)
#'
#' # View the values from the R console with isolate()
#' isolate(reactiveB())
#' isolate(reactiveC())
#' isolate(reactiveD())
#'
#' @export
reactive <- function(x) {
UseMethod("reactive")
}
#' @S3method reactive function
reactive.function <- function(x) {
return(Observable$new(x, deparse(substitute(x)))$getValue)
}
#' @S3method reactive default
reactive.default <- function(x) {
stop("Don't know how to make this object reactive!")
reactive <- function(x, env = parent.frame(), quoted = FALSE, label = NULL) {
fun <- exprToFunction(x, env, quoted)
if (is.null(label))
label <- deparse(body(fun))
Observable$new(fun, label)$getValue
}
# Return the number of times that a reactive function or observer has been run
# Return the number of times that a reactive expression or observer has been run
execCount <- function(x) {
if (is.function(x))
return(environment(x)$.execCount)
@@ -360,16 +381,20 @@ execCount <- function(x) {
stop('Unexpected argument to execCount')
}
# Observer ------------------------------------------------------------------
Observer <- setRefClass(
'Observer',
fields = list(
.func = 'function',
.label = 'character',
.flushCallbacks = 'list',
.execCount = 'integer'
.invalidateCallbacks = 'list',
.execCount = 'integer',
.onResume = 'function',
.suspended = 'logical'
),
methods = list(
initialize = function(func, label) {
initialize = function(func, label, suspended = FALSE) {
if (length(formals(func)) > 0)
stop("Can't make an observer from a function that takes parameters; ",
"only functions without parameters can be reactive.")
@@ -377,68 +402,133 @@ Observer <- setRefClass(
.func <<- func
.label <<- label
.execCount <<- 0L
.suspended <<- suspended
.onResume <<- function() NULL
# Defer the first running of this until flushReact is called
ctx <- Context$new(.label)
ctx$onFlush(function() {
run()
})
ctx$addPendingFlush()
.createContext()$invalidate()
},
run = function() {
.createContext = function() {
ctx <- Context$new(.label)
ctx$onInvalidate(function() {
lapply(.flushCallbacks, function(func) {
lapply(.invalidateCallbacks, function(func) {
func()
NULL
})
ctx$addPendingFlush()
})
continue <- function() {
ctx$addPendingFlush()
}
if (.suspended == FALSE)
continue()
else
.onResume <<- continue
})
ctx$onFlush(function() {
run()
})
return(ctx)
},
run = function() {
ctx <- .createContext()
.execCount <<- .execCount + 1L
ctx$run(.func)
},
onInvalidate = function(func) {
.flushCallbacks <<- c(.flushCallbacks, func)
"Register a function to run when this observer is invalidated"
.invalidateCallbacks <<- c(.invalidateCallbacks, func)
},
suspend = function() {
"Causes this observer to stop scheduling flushes (re-executions) in
response to invalidations. If the observer was invalidated prior to this
call but it has not re-executed yet (because it waits until onFlush is
called) then that re-execution will still occur, becasue the flush is
already scheduled."
.suspended <<- TRUE
},
resume = function() {
"Causes this observer to start re-executing in response to invalidations.
If the observer was invalidated while suspended, then it will schedule
itself for re-execution (pending flush)."
if (.suspended) {
.suspended <<- FALSE
.onResume()
.onResume <<- function() NULL
}
invisible()
}
)
)
#' Create a reactive observer
#'
#' Creates an observer from the given function. An observer is like a reactive
#' function in that it can read reactive values and call reactive functions, and
#' Creates an observer from the given expression An observer is like a reactive
#' expression in that it can read reactive values and call reactive expressions, and
#' will automatically re-execute when those dependencies change. But unlike
#' reactive functions, it doesn't yield a result and can't be used as an input
#' to other reactive functions. Thus, observers are only useful for their side
#' reactive expression, it doesn't yield a result and can't be used as an input
#' to other reactive expressions. Thus, observers are only useful for their side
#' effects (for example, performing I/O).
#'
#' Another contrast between reactive functions and observers is their execution
#' strategy. Reactive functions use lazy evaluation; that is, when their
#' Another contrast between reactive expressions and observers is their execution
#' strategy. Reactive expressions use lazy evaluation; that is, when their
#' dependencies change, they don't re-execute right away but rather wait until
#' they are called by someone else. Indeed, if they are not called then they
#' will never re-execute. In contrast, observers use eager evaluation; as soon
#' as their dependencies change, they schedule themselves to re-execute.
#'
#' @param func The function to observe. It must not have any parameters. Any
#' return value from this function will be ignored.
#'
#' @param x An expression (quoted or unquoted). Any return value will be ignored.
#' @param env The parent environment for the reactive expression. By default, this
#' is the calling environment, the same as when defining an ordinary
#' non-reactive expression.
#' @param quoted Is the expression quoted? By default, this is \code{FALSE}.
#' This is useful when you want to use an expression that is stored in a
#' variable; to do so, it must be quoted with `quote()`.
#' @param label A label for the observer, useful for debugging.
#' @param suspended If \code{TRUE}, start the observer in a suspended state.
#' If \code{FALSE} (the default), start in a non-suspended state.
#'
#' @examples
#' values <- reactiveValues(A=1)
#'
#' obsB <- observe({
#' print(values$A + 1)
#' })
#'
#' # Can use quoted expressions
#' obsC <- observe(quote({ print(values$A + 2) }), quoted = TRUE)
#'
#' # To store expressions for later conversion to observe, use quote()
#' expr_q <- quote({ print(values$A + 3) })
#' obsD <- observe(expr_q, quoted = TRUE)
#'
#' # In a normal Shiny app, the web client will trigger flush events. If you
#' # are at the console, you can force a flush with flushReact()
#' shiny:::flushReact()
#'
#' @export
observe <- function(func) {
invisible(Observer$new(func, deparse(substitute(func))))
observe <- function(x, env=parent.frame(), quoted=FALSE, label=NULL,
suspended=FALSE) {
fun <- exprToFunction(x, env, quoted)
if (is.null(label))
label <- deparse(body(fun))
invisible(Observer$new(fun, label=label, suspended=suspended))
}
# ---------------------------------------------------------------------------
#' Timer
#'
#' Creates a reactive timer with the given interval. A reactive timer is like a
#' reactive value, except reactive values are triggered when they are set, while
#' reactive timers are triggered simply by the passage of time.
#'
#' \link[=reactive]{Reactive functions} and observers that want to be
#' \link[=reactive]{Reactive expressions} and observers that want to be
#' invalidated by the timer need to call the timer function that
#' \code{reactiveTimer} returns, even if the current time value is not actually
#' needed.
@@ -492,22 +582,32 @@ invalidateLater <- function(millis) {
#' Create a non-reactive scope for an expression
#'
#' Executes the given expression in a scope where reactive values or functions
#' Executes the given expression in a scope where reactive values or expression
#' can be read, but they cannot cause the reactive scope of the caller to be
#' re-evaluated when they change.
#'
#' Ordinarily, the simple act of reading a reactive value causes a relationship
#' to be established between the caller and the reactive value, where a change
#' to the reactive value will cause the caller to re-execute. (The same applies
#' for the act of getting a reactive function's value.) The \code{isolate}
#' function lets you read a reactive value or function without establishing this
#' for the act of getting a reactive expression's value.) The \code{isolate}
#' function lets you read a reactive value or expression without establishing this
#' relationship.
#'
#' @param expr An expression that can access reactive values or functions.
#' The expression given to \code{isolate()} is evaluated in the calling
#' environment. This means that if you assign a variable inside the
#' \code{isolate()}, its value will be visible outside of the \code{isolate()}.
#' If you want to avoid this, you can use \code{\link{local}()} inside the
#' \code{isolate()}.
#'
#' This function can also be useful for calling reactive expression at the
#' console, which can be useful for debugging. To do so, simply wrap the
#' calls to the reactive expression with \code{isolate()}.
#'
#' @param expr An expression that can access reactive values or expressions.
#'
#' @examples
#' \dontrun{
#' observer(function() {
#' observe({
#' input$saveButton # Do take a dependency on input$saveButton
#'
#' # isolate a simple expression
@@ -515,7 +615,7 @@ invalidateLater <- function(millis) {
#' writeToDatabase(data)
#' })
#'
#' observer(function() {
#' observe({
#' input$saveButton # Do take a dependency on input$saveButton
#'
#' # isolate a whole block
@@ -526,7 +626,30 @@ invalidateLater <- function(millis) {
#' })
#' writeToDatabase(data)
#' })
#'
#' observe({
#' x <- 1
#' # x outside of isolate() is affected
#' isolate(x <- 2)
#' print(x) # 2
#'
#' y <- 1
#' # Use local() to avoid affecting calling environment
#' isolate(local(y <- 2))
#' print(y) # 1
#' })
#'
#' }
#'
#' # Can also use isolate to call reactive expressions from the R console
#' values <- reactiveValues(A=1)
#' fun <- reactive({ as.character(values$A) })
#' isolate(fun())
#' # "1"
#'
#' # isolate also works if the reactive expression accesses values from the
#' # input object, like input$x
#'
#' @export
isolate <- function(expr) {
ctx <- Context$new('[isolate]')

View File

@@ -3,8 +3,9 @@
#' Download and launch a Shiny application that is hosted on GitHub as a gist.
#'
#' @param gist The identifier of the gist. For example, if the gist is
#' https://gist.github.com/3239667, then \code{3239667}, \code{'3239667'}, and
#' \code{'https://gist.github.com/3239667'} are all valid values.
#' https://gist.github.com/jcheng5/3239667, then \code{3239667},
#' \code{'3239667'}, and \code{'https://gist.github.com/jcheng5/3239667'}
#' are all valid values.
#' @param port The TCP port that the application should listen on. Defaults to
#' port 8100.
#' @param launch.browser If true, the system's default web browser will be
@@ -13,8 +14,11 @@
#'
#' @examples
#' \dontrun{
#' runGist(4034323)
#' runGist("https://gist.github.com/4034323")
#' runGist(3239667)
#' runGist("https://gist.github.com/jcheng5/3239667")
#'
#' # Old URL format without username
#' runGist("https://gist.github.com/3239667")
#' }
#'
#' @export
@@ -25,7 +29,7 @@ runGist <- function(gist,
gistUrl <- if (is.numeric(gist) || grepl('^[0-9a-f]+$', gist)) {
sprintf('https://gist.github.com/%s/download', gist)
} else if(grepl('^https://gist.github.com/([0-9a-f]+)$', gist)) {
} else if(grepl('^https://gist.github.com/([^/]+/)?([0-9a-f]+)$', gist)) {
paste(gist, '/download', sep='')
} else {
stop('Unrecognized gist identifier format')
@@ -87,6 +91,9 @@ runGitHub <- function(repo, username = getOption("github.user"),
#'
#' Download and launch a Shiny application that is hosted at a downloadable
#' URL. The Shiny application must be saved in a .zip, .tar, or .tar.gz file.
#' The Shiny application files must be contained in a subdirectory in the
#' archive. For example, the files might be \code{myapp/server.r} and
#' \code{myapp/ui.r}.
#'
#' @param url URL of the application.
#' @param filetype The file type (\code{".zip"}, \code{".tar"}, or

153
R/shiny.R
View File

@@ -18,6 +18,8 @@ ShinyApp <- setRefClass(
.websocket = 'list',
.invalidatedOutputValues = 'Map',
.invalidatedOutputErrors = 'Map',
.outputs = 'list', # Keeps track of all the output observer objects
.outputOptions = 'list', # Options for each of the output observer objects
.progressKeys = 'character',
.fileUploadContext = 'FileUploadContext',
session = 'ReactiveValues',
@@ -37,6 +39,8 @@ ShinyApp <- setRefClass(
session <<- ReactiveValues$new()
token <<- createUniqueId(16)
.outputs <<- list()
.outputOptions <<- list()
allowDataUriScheme <<- TRUE
},
@@ -49,6 +53,11 @@ ShinyApp <- setRefClass(
# jcheng 08/31/2012: User submitted an example of a dynamically calculated
# name not working unless name was eagerly evaluated. Yikes!
force(name)
# If overwriting an output object, suspend the previous copy of it
if (!is.null(.outputs[[name]])) {
.outputs[[name]]$suspend()
}
if (is.function(func)) {
if (length(formals(func)) != 0) {
@@ -58,7 +67,7 @@ ShinyApp <- setRefClass(
}
}
obs <- Observer$new(function() {
obs <- observe({
value <- try(func(), silent=FALSE)
@@ -74,11 +83,15 @@ ShinyApp <- setRefClass(
}
else
.invalidatedOutputValues$set(name, value)
}, label)
}, label=label, suspended=TRUE)
obs$onInvalidate(function() {
showProgress(name)
})
.outputs[[name]] <<- obs
# Default is to suspend when hidden
.outputOptions[[name]][['suspendWhenHidden']] <<- TRUE
}
else {
stop(paste("Unexpected", class(func), "output for", name))
@@ -283,25 +296,126 @@ ShinyApp <- setRefClass(
return(sprintf('session/%s/download/%s',
URLencode(token, TRUE),
URLencode(name, TRUE)))
},
# This function suspends observers for hidden outputs and resumes observers
# for un-hidden outputs.
manageHiddenOutputs = function() {
# Find hidden state for each output, and suspend/resume accordingly
for (outputName in names(.outputs)) {
# Find corresponding hidden state input variable, with the format
# ".shinyout_foo_hidden".
# Some tricky stuff: instead of accessing names using session$names(),
# get the names directly via session$.values, to avoid triggering reactivity.
# Need to handle cases where the output object isn't actually used
# in the web page; in these cases, there's no .shinyout_foo_hidden flag,
# and hidden should be TRUE. In other words, NULL and TRUE should map
# to TRUE, FALSE should map to FALSE.
hidden <- session$.values[[paste(".shinyout_", outputName, "_hidden", sep="")]]
if (is.null(hidden)) hidden <- TRUE
if (hidden && .outputOptions[[outputName]][['suspendWhenHidden']]) {
.outputs[[outputName]]$suspend()
} else {
.outputs[[outputName]]$resume()
}
}
},
outputOptions = function(name, ...) {
# If no name supplied, return the list of options for all outputs
if (is.null(name))
return(.outputOptions)
if (! name %in% names(.outputs))
stop(name, " is not in list of output objects")
opts <- list(...)
# If no options are set, return the options for the specified output
if (length(opts) == 0)
return(.outputOptions[[name]])
# Set the appropriate option
validOpts <- "suspendWhenHidden"
for (optname in names(opts)) {
if (! optname %in% validOpts)
stop(optname, " is not a valid option")
.outputOptions[[name]][[optname]] <<- opts[[optname]]
}
# If any changes to suspendWhenHidden, need to re-run manageHiddenOutputs
if ("suspendWhenHidden" %in% names(opts)) {
manageHiddenOutputs()
}
invisible()
}
)
)
.createOutputWriter <- function(shinyapp) {
ow <- list(impl=shinyapp)
class(ow) <- 'shinyoutput'
return(ow)
structure(list(impl=shinyapp), class='shinyoutput')
}
#' @S3method $<- shinyoutput
`$<-.shinyoutput` <- function(x, name, value) {
x[['impl']]$defineOutput(name, value, deparse(substitute(value)))
.subset2(x, 'impl')$defineOutput(name, value, deparse(substitute(value)))
return(invisible(x))
}
#' @S3method [[<- shinyoutput
`[[<-.shinyoutput` <- `$<-.shinyoutput`
#' @S3method $ shinyoutput
`$.shinyoutput` <- function(x, name) {
stop("Reading objects from shinyoutput object not allowed.")
}
#' @S3method [[ shinyoutput
`[[.shinyoutput` <- `$.shinyoutput`
#' @S3method [ shinyoutput
`[.shinyoutput` <- function(values, name) {
stop("Single-bracket indexing of shinyoutput object is not allowed.")
}
#' @S3method [<- shinyoutput
`[<-.shinyoutput` <- function(values, name, value) {
stop("Single-bracket indexing of shinyoutput object is not allowed.")
}
#' Set options for an output object.
#'
#' These are the available options for an output object:
#' \itemize{
#' \item suspendWhenHidden. When \code{TRUE} (the default), the output object
#' will be suspended (not execute) when it is hidden on the web page. When
#' \code{FALSE}, the output object will not suspend when hidden, and if it
#' was already hidden and suspended, then it will resume immediately.
#' }
#'
#' @examples
#' \dontrun{
#' # Get the list of options for all observers within output
#' outputOptions(output)
#'
#' # Disable suspend for output$myplot
#' outputOptions(output, "myplot", suspendWhenHidden = FALSE)
#'
#' # Get the list of options for output$myplot
#' outputOptions(output, "myplot")
#' }
#'
#' @param x A shinyoutput object (typically \code{output}).
#' @param name The name of an output observer in the shinyoutput object.
#' @param ... Options to set for the output observer.
#' @export
outputOptions <- function(x, name, ...) {
if (!inherits(x, "shinyoutput"))
stop("x must be a shinyoutput object.")
.subset2(x, 'impl')$outputOptions(name, ...)
}
resolve <- function(dir, relpath) {
abs.path <- file.path(dir, relpath)
if (!file.exists(abs.path))
@@ -320,7 +434,11 @@ resolve <- function(dir, relpath) {
httpResponse <- function(status = 200,
content_type = "text/html; charset=UTF-8",
content = "",
headers = c()) {
headers = list()) {
# Make sure it's a list, not a vector
headers <- as.list(headers)
if (is.null(headers$`X-UA-Compatible`))
headers$`X-UA-Compatible` <- "chrome=1"
resp <- list(status = status, content_type = content_type, content = content,
headers = headers)
class(resp) <- 'httpResponse'
@@ -584,7 +702,7 @@ resourcePathHandler <- function(ws, header) {
#' # A very simple Shiny app that takes a message from the user
#' # and outputs an uppercase version of it.
#' shinyServer(function(input, output) {
#' output$uppercase <- reactiveText(function() {
#' output$uppercase <- renderText({
#' toupper(input$message)
#' })
#' })
@@ -730,6 +848,7 @@ startApp <- function(port=8101L) {
msg$data[[ splitName[[1]] ]] <- switch(
splitName[[2]],
matrix = unpackMatrix(val),
number = ifelse(is.null(val), NA, val),
stop('Unknown type specified for ', name)
)
}
@@ -766,7 +885,6 @@ startApp <- function(port=8101L) {
shinyapp$allowDataUriScheme <- msg$data[['__allowDataUriScheme']]
msg$data[['__allowDataUriScheme']] <- NULL
shinyapp$session$mset(msg$data)
flushReact()
local({
serverFunc(input=.createReactiveValues(shinyapp$session, readonly=TRUE),
output=.createOutputWriter(shinyapp))
@@ -777,6 +895,7 @@ startApp <- function(port=8101L) {
},
shinyapp$dispatch(msg)
)
shinyapp$manageHiddenOutputs()
flushReact()
lapply(apps$values(), function(shinyapp) {
shinyapp$flushOutput()
@@ -796,11 +915,15 @@ startApp <- function(port=8101L) {
# @param ws_env The return value from \code{\link{startApp}}.
serviceApp <- function(ws_env) {
if (timerCallbacks$executeElapsed()) {
for (shinyapp in apps$values()) {
shinyapp$manageHiddenOutputs()
}
flushReact()
lapply(apps$values(), function(shinyapp) {
shinyapp$flushOutput()
NULL
})
for (shinyapp in apps$values()) {
shinyapp$flushOutput()
}
}
# If this R session is interactive, then call service() with a short timeout
@@ -902,8 +1025,8 @@ runExample <- function(example=NA,
# The only difference is that, if the protocol is https, it changes the
# download settings, depending on platform.
download <- function(url, ...) {
# First, check protocol. If https, check platform:
if (grepl('^https://', url)) {
# First, check protocol. If http or https, check platform:
if (grepl('^https?://', url)) {
# If Windows, call setInternet2, then use download.file with defaults.
if (.Platform$OS.type == "windows") {

View File

@@ -5,7 +5,7 @@ suppressPackageStartupMessages({
#' Plot Output
#'
#' Creates a reactive plot that is suitable for assigning to an \code{output}
#' Renders a reactive plot that is suitable for assigning to an \code{output}
#' slot.
#'
#' The corresponding HTML output tag should be \code{div} or \code{img} and have
@@ -17,7 +17,7 @@ suppressPackageStartupMessages({
#' output. Notably, plain \code{png} output on Linux and Windows may not
#' antialias some point shapes, resulting in poor quality output.
#'
#' @param func A function that generates a plot.
#' @param expr An expression that generates a plot.
#' @param width The width of the rendered plot, in pixels; or \code{'auto'} to
#' use the \code{offsetWidth} of the HTML element that is bound to this plot.
#' You can also pass in a function that returns the width in pixels or
@@ -30,15 +30,28 @@ suppressPackageStartupMessages({
#' values and functions.
#' @param ... Arguments to be passed through to \code{\link[grDevices]{png}}.
#' These can be used to set the width, height, background color, etc.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that generates a plot (deprecated; use \code{expr}
#' instead).
#'
#' @export
reactivePlot <- function(func, width='auto', height='auto', ...) {
renderPlot <- function(expr, width='auto', height='auto', ...,
env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderPlot: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
func <- exprToFunction(expr, env, quoted)
}
args <- list(...)
if (is.function(width))
width <- reactive(width)
width <- reactive({ width() })
if (is.function(height))
height <- reactive(height)
height <- reactive({ height() })
return(function(shinyapp, name, ...) {
png.file <- tempfile(fileext='.png')
@@ -102,14 +115,25 @@ reactivePlot <- function(func, width='auto', height='auto', ...) {
#' The corresponding HTML output tag should be \code{div} and have the CSS class
#' name \code{shiny-html-output}.
#'
#' @param func A function that returns an R object that can be used with
#' @param expr An expression that returns an R object that can be used with
#' \code{\link[xtable]{xtable}}.
#' @param ... Arguments to be passed through to \code{\link[xtable]{xtable}} and
#' \code{\link[xtable]{print.xtable}}.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that returns an R object that can be used with
#' \code{\link[xtable]{xtable}} (deprecated; use \code{expr} instead).
#'
#' @export
reactiveTable <- function(func, ...) {
reactive(function() {
renderTable <- function(expr, ..., env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderTable: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
func <- exprToFunction(expr, env, quoted)
}
function() {
classNames <- getOption('shiny.table.class', 'data table table-bordered table-condensed')
data <- func()
@@ -125,7 +149,7 @@ reactiveTable <- function(func, ...) {
'"',
sep=''), ...)),
collapse="\n"))
})
}
}
#' Printable Output
@@ -146,23 +170,33 @@ reactiveTable <- function(func, ...) {
#' returns \code{NULL} then \code{NULL} will actually be visible in the output.
#' To display nothing, make your function return \code{\link{invisible}()}.
#'
#' @param func A function that may print output and/or return a printable R
#' @param expr An expression that may print output and/or return a printable R
#' object.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' @param func A function that may print output and/or return a printable R
#' object (deprecated; use \code{expr} instead).
#'
#' @seealso \code{\link{reactiveText}} for displaying the value returned from a
#' @seealso \code{\link{renderText}} for displaying the value returned from a
#' function, instead of the printed output.
#'
#' @example res/text-example.R
#'
#' @export
reactivePrint <- function(func) {
reactive(function() {
renderPrint <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderPrint: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
func <- exprToFunction(expr, env, quoted)
}
function() {
return(paste(capture.output({
result <- withVisible(func())
if (result$visible)
print(result$value)
}), collapse="\n"))
})
}
}
#' Text Output
@@ -178,20 +212,31 @@ reactivePrint <- function(func) {
#' The result of executing \code{func} will passed to \code{cat}, inside a
#' \code{\link[utils]{capture.output}} call.
#'
#' @param func A function that returns an R object that can be used as an
#' @param expr An expression that returns an R object that can be used as an
#' argument to \code{cat}.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that returns an R object that can be used as an
#' argument to \code{cat}.(deprecated; use \code{expr} instead).
#'
#' @seealso \code{\link{reactivePrint}} for capturing the print output of a
#' @seealso \code{\link{renderPrint}} for capturing the print output of a
#' function, rather than the returned text value.
#'
#' @example res/text-example.R
#'
#' @export
reactiveText <- function(func) {
reactive(function() {
renderText <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderText: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
func <- exprToFunction(expr, env, quoted)
}
function() {
value <- func()
return(paste(capture.output(cat(value)), collapse="\n"))
})
}
}
#' UI Output
@@ -202,28 +247,39 @@ reactiveText <- function(func) {
#' The corresponding HTML output tag should be \code{div} and have the CSS class
#' name \code{shiny-html-output} (or use \code{\link{uiOutput}}).
#'
#' @param func A function that returns a Shiny tag object, \code{\link{HTML}},
#' @param expr An expression that returns a Shiny tag object, \code{\link{HTML}},
#' or a list of such objects.
#' @param env The environment in which to evaluate \code{expr}.
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
#' is useful if you want to save an expression in a variable.
#' @param func A function that returns a Shiny tag object, \code{\link{HTML}},
#' or a list of such objects (deprecated; use \code{expr} instead).
#'
#' @seealso conditionalPanel
#'
#' @export
#' @examples
#' \dontrun{
#' output$moreControls <- reactiveUI(function() {
#' output$moreControls <- renderUI({
#' list(
#'
#' )
#' })
#' }
reactiveUI <- function(func) {
reactive(function() {
renderUI <- function(expr, env=parent.frame(), quoted=FALSE, func=NULL) {
if (!is.null(func)) {
shinyDeprecated(msg="renderUI: argument 'func' is deprecated. Please use 'expr' instead.")
} else {
func <- exprToFunction(expr, env, quoted)
}
function() {
result <- func()
if (is.null(result) || length(result) == 0)
return(NULL)
# Wrap result in tagList in case it is an ordinary list
return(as.character(tagList(result)))
})
}
}
#' File Downloads
@@ -271,4 +327,61 @@ downloadHandler <- function(filename, content, contentType=NA) {
return(function(shinyapp, name, ...) {
shinyapp$registerDownload(name, filename, contentType, content)
})
}
}
# Deprecated functions ------------------------------------------------------
#' Plot output (deprecated)
#'
#' See \code{\link{renderPlot}}.
#' @param func A function.
#' @param width Width.
#' @param height Height.
#' @param ... Other arguments to pass on.
#' @export
reactivePlot <- function(func, width='auto', height='auto', ...) {
shinyDeprecated(new="renderPlot")
renderPlot({ func() }, width='auto', height='auto', ...)
}
#' Table output (deprecated)
#'
#' See \code{\link{renderTable}}.
#' @param func A function.
#' @param ... Other arguments to pass on.
#' @export
reactiveTable <- function(func, ...) {
shinyDeprecated(new="renderTable")
renderTable({ func() })
}
#' Print output (deprecated)
#'
#' See \code{\link{renderPrint}}.
#' @param func A function.
#' @export
reactivePrint <- function(func) {
shinyDeprecated(new="renderPrint")
renderPrint({ func() })
}
#' UI output (deprecated)
#'
#' See \code{\link{renderUI}}.
#' @param func A function.
#' @export
reactiveUI <- function(func) {
shinyDeprecated(new="renderUI")
renderUI({ func() })
}
#' Text output (deprecated)
#'
#' See \code{\link{renderText}}.
#' @param func A function.
#' @export
reactiveText <- function(func) {
shinyDeprecated(new="renderText")
renderText({ func() })
}

110
R/utils.R
View File

@@ -101,4 +101,112 @@ knownContentTypes$mset(
getContentType <- function(ext, defaultType='application/octet-stream') {
knownContentTypes$get(tolower(ext)) %OR% defaultType
}
}
# Create a zero-arg function from a quoted expression and environment
# @examples
# makeFunction(body=quote(print(3)))
makeFunction <- function(args = pairlist(), body, env = parent.frame()) {
eval(call("function", args, body), env)
}
#' Convert an expression or quoted expression to a function
#'
#' This is to be called from another function, because it will attempt to get
#' an unquoted expression from two calls back.
#'
#' If expr is a quoted expression, then this just converts it to a function.
#' If expr is a function, then this simply returns expr (and prints a
#' deprecation message.
#' If expr was a non-quoted expression from two calls back, then this will
#' quote the original expression and convert it to a function.
#
#' @param expr A quoted or unquoted expression, or a function.
#' @param env The desired environment for the function. Defaults to the
#' calling environment two steps back.
#' @param quoted Is the expression quoted?
#'
#' @examples
#' # Example of a new renderer, similar to renderText
#' # This is something that toolkit authors will do
#' renderTriple <- function(expr, env=parent.frame(), quoted=FALSE) {
#' # Convert expr to a function
#' func <- shiny::exprToFunction(expr, env, quoted)
#'
#' function() {
#' value <- func()
#' paste(rep(value, 3), collapse=", ")
#' }
#' }
#'
#'
#' # Example of using the renderer.
#' # This is something that app authors will do.
#' values <- reactiveValues(A="text")
#'
#' \dontrun{
#' # Create an output object
#' output$tripleA <- renderTriple({
#' values$A
#' })
#' }
#'
#' # At the R console, you can experiment with the renderer using isolate()
#' tripleA <- renderTriple({
#' values$A
#' })
#'
#' isolate(tripleA())
#' # "text, text, text"
#'
#' @export
exprToFunction <- function(expr, env=parent.frame(2), quoted=FALSE) {
# Get the quoted expr from two calls back
expr_sub <- eval(substitute(substitute(expr)), parent.frame())
# Check if expr is a function, making sure not to evaluate expr, in case it
# is actually an unquoted expression.
# If expr is a single token, then indexing with [[ will error; if it has multiple
# tokens, then [[ works. In the former case it will be a name object; in the
# latter, it will be a language object.
if (!is.name(expr_sub) && expr_sub[[1]] == as.name('function')) {
# Get name of function that called this function
called_fun <- sys.call(-1)[[1]]
shinyDeprecated(msg = paste("Passing functions to '", called_fun,
"' is deprecated. Please use expressions instead. See ?", called_fun,
" for more information.", sep=""))
return(expr)
}
if (quoted) {
# expr is a quoted expression
makeFunction(body=expr, env=env)
} else {
# expr is an unquoted expression
makeFunction(body=expr_sub, env=env)
}
}
#' Print message for deprecated functions in Shiny
#'
#' To disable these messages, use \code{options(shiny.deprecation.messages=FALSE)}.
#'
#' @param new Name of replacement function.
#' @param msg Message to print. If used, this will override the default message.
#' @param old Name of deprecated function.
shinyDeprecated <- function(new=NULL, msg=NULL,
old=as.character(sys.call(sys.parent()))[1L]) {
if (getOption("shiny.deprecation.messages", default=TRUE) == FALSE)
return(invisible())
if (is.null(msg)) {
msg <- paste(old, "is deprecated.")
if (!is.null(new))
msg <- paste(msg, "Please use", new, "instead.",
"To disable this message, run options(shiny.deprecation.messages=FALSE)")
}
# Similar to .Deprecated(), but print a message instead of warning
message(msg)
}

View File

@@ -3,14 +3,14 @@ library(shiny)
# Define server logic required to generate and plot a random distribution
shinyServer(function(input, output) {
# Function that generates a plot of the distribution. The function
# is wrapped in a call to reactivePlot to indicate that:
# Expression that generates a plot of the distribution. The expression
# is wrapped in a call to renderPlot to indicate that:
#
# 1) It is "reactive" and therefore should be automatically
# re-executed when inputs change
# 2) Its output type is a plot
#
output$distPlot <- reactivePlot(function() {
output$distPlot <- renderPlot({
# generate an rnorm distribution and plot it
dist <- rnorm(input$obs)

View File

@@ -5,7 +5,7 @@ library(datasets)
shinyServer(function(input, output) {
# Return the requested dataset
datasetInput <- reactive(function() {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
@@ -13,13 +13,13 @@ shinyServer(function(input, output) {
})
# Generate a summary of the dataset
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations
output$view <- reactiveTable(function() {
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})

View File

@@ -4,47 +4,47 @@ library(datasets)
# Define server logic required to summarize and view the selected dataset
shinyServer(function(input, output) {
# By declaring databaseInput as a reactive function we ensure that:
# By declaring databaseInput as a reactive expression we ensure that:
#
# 1) It is only called when the inputs it depends on changes
# 2) The computation and result are shared by all the callers (it
# only executes a single time)
# 3) When the inputs change and the function is re-executed, the
# 3) When the inputs change and the expression is re-executed, the
# new result is compared to the previous result; if the two are
# identical, then the callers are not notified
#
datasetInput <- reactive(function() {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# The output$caption is computed based on a reactive function that
# The output$caption is computed based on a reactive expression that
# returns input$caption. When the user changes the "caption" field:
#
# 1) This function is automatically called to recompute the output
# 2) The new caption is pushed back to the browser for re-display
#
# Note that because the data-oriented reactive functions below don't
# depend on input$caption, those functions are NOT called when
# Note that because the data-oriented reactive expressions below don't
# depend on input$caption, those expressions are NOT called when
# input$caption changes.
output$caption <- reactiveText(function() {
output$caption <- renderText({
input$caption
})
# The output$summary depends on the datasetInput reactive function,
# The output$summary depends on the datasetInput reactive expression,
# so will be re-executed whenever datasetInput is re-executed
# (i.e. whenever the input$dataset changes)
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# The output$view depends on both the databaseInput reactive function
# The output$view depends on both the databaseInput reactive expression
# and input$obs, so will be re-executed whenever input$dataset or
# input$obs is changed.
output$view <- reactiveTable(function() {
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})

View File

@@ -11,20 +11,20 @@ mpgData$am <- factor(mpgData$am, labels = c("Automatic", "Manual"))
# Define server logic required to plot various variables against mpg
shinyServer(function(input, output) {
# Compute the forumla text in a reactive function since it is
# Compute the forumla text in a reactive expression since it is
# shared by the output$caption and output$mpgPlot functions
formulaText <- reactive(function() {
formulaText <- reactive({
paste("mpg ~", input$variable)
})
# Return the formula text for printing as a caption
output$caption <- reactiveText(function() {
output$caption <- renderText({
formulaText()
})
# Generate a plot of the requested variable against mpg and only
# include outliers if requested
output$mpgPlot <- reactivePlot(function() {
output$mpgPlot <- renderPlot({
boxplot(as.formula(formulaText()),
data = mpgData,
outline = input$outliers)

View File

@@ -3,8 +3,8 @@ library(shiny)
# Define server logic for slider examples
shinyServer(function(input, output) {
# Reactive function to compose a data frame containing all of the values
sliderValues <- reactive(function() {
# Reactive expression to compose a data frame containing all of the values
sliderValues <- reactive({
# Compose data frame
data.frame(
@@ -22,7 +22,7 @@ shinyServer(function(input, output) {
})
# Show the values using an HTML table
output$values <- reactiveTable(function() {
output$values <- renderTable({
sliderValues()
})
})

View File

@@ -3,10 +3,10 @@ library(shiny)
# Define server logic for random distribution application
shinyServer(function(input, output) {
# Reactive function to generate the requested distribution. This is
# Reactive expression to generate the requested distribution. This is
# called whenever the inputs change. The output functions defined
# below then all use the value computed from this function
data <- reactive(function() {
# below then all use the value computed from this expression
data <- reactive({
dist <- switch(input$dist,
norm = rnorm,
unif = runif,
@@ -19,9 +19,9 @@ shinyServer(function(input, output) {
# Generate a plot of the data. Also uses the inputs to build the
# plot label. Note that the dependencies on both the inputs and
# the data reactive function are both tracked, and all functions
# the data reactive expression are both tracked, and all expressions
# are called in the sequence implied by the dependency graph
output$plot <- reactivePlot(function() {
output$plot <- renderPlot({
dist <- input$dist
n <- input$n
@@ -30,12 +30,12 @@ shinyServer(function(input, output) {
})
# Generate a summary of the data
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
summary(data())
})
# Generate an HTML table view of the data
output$table <- reactiveTable(function() {
output$table <- renderTable({
data.frame(x=data())
})

View File

@@ -5,7 +5,7 @@ library(datasets)
shinyServer(function(input, output) {
# Return the requested dataset
datasetInput <- reactive(function() {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
@@ -13,13 +13,13 @@ shinyServer(function(input, output) {
})
# Generate a summary of the dataset
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations
output$view <- reactiveTable(function() {
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})
})

View File

@@ -3,10 +3,10 @@ library(shiny)
# Define server logic for random distribution application
shinyServer(function(input, output) {
# Reactive function to generate the requested distribution. This is
# called whenever the inputs change. The output functions defined
# below then all used the value computed from this function
data <- reactive(function() {
# Reactive expression to generate the requested distribution. This is
# called whenever the inputs change. The output expressions defined
# below then all used the value computed from this expression
data <- reactive({
dist <- switch(input$dist,
norm = rnorm,
unif = runif,
@@ -19,9 +19,9 @@ shinyServer(function(input, output) {
# Generate a plot of the data. Also uses the inputs to build the
# plot label. Note that the dependencies on both the inputs and
# the data reactive function are both tracked, and all functions
# the data reactive expression are both tracked, and all expressions
# are called in the sequence implied by the dependency graph
output$plot <- reactivePlot(function() {
output$plot <- renderPlot({
dist <- input$dist
n <- input$n
@@ -30,12 +30,12 @@ shinyServer(function(input, output) {
})
# Generate a summary of the data
output$summary <- reactivePrint(function() {
output$summary <- renderPrint({
summary(data())
})
# Generate an HTML table view of the data
output$table <- reactiveTable(function() {
output$table <- renderTable({
data.frame(x=data())
})

View File

@@ -1,7 +1,7 @@
library(shiny)
shinyServer(function(input, output) {
output$contents <- reactiveTable(function() {
output$contents <- renderTable({
# input$file1 will be NULL initially. After the user selects and uploads a
# file, it will be a data frame with 'name', 'size', 'type', and 'data'

View File

@@ -1,12 +1,12 @@
shinyServer(function(input, output) {
datasetInput <- reactive(function() {
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
output$table <- reactiveTable(function() {
output$table <- renderTable({
datasetInput()
})

73
inst/tests/test-gc.r Normal file
View File

@@ -0,0 +1,73 @@
context("garbage collection")
test_that("unreferenced observers are garbage collected", {
vals_removed <- FALSE
obs_removed <- FALSE
vals <- reactiveValues(A=1)
obs <- observe({ vals$A })
# These are called when the objects are garbage-collected
reg.finalizer(attr(.subset2(vals,'impl'), ".xData"),
function(e) vals_removed <<- TRUE)
reg.finalizer(attr(obs, ".xData"),
function(e) obs_removed <<- TRUE)
flushReact()
# Removing this reference to obs doesn't delete it because vals still has a
# reference to it
rm(obs)
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(FALSE, FALSE))
# Updating vals$A and flushing won't make obs go away because it creates a new
# context, and vals$A's context tracks obs's context as a dependent
vals$A <- 2
flushReact()
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(FALSE, FALSE))
# Removing vals will result in vals and obs being garbage collected since
# there are no other references to them
rm(vals)
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(TRUE, TRUE))
})
test_that("suspended observers are garbage collected", {
vals_removed <- FALSE
obs_removed <- FALSE
vals <- reactiveValues(A=1)
obs <- observe({ vals$A })
# These are called when the objects are garbage-collected
reg.finalizer(attr(.subset2(vals,'impl'), ".xData"),
function(e) vals_removed <<- TRUE)
reg.finalizer(attr(obs, ".xData"),
function(e) obs_removed <<- TRUE)
flushReact()
vals$A <- 2
flushReact()
invisible(gc())
# Simply suspending and removing our reference to obs doesn't result in GC,
# because vals's context still has a reference to obs's context, as a dependent
obs$suspend()
rm(obs)
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(FALSE, FALSE))
# Next time we update vals$A and flush, there's no more reference to obs
vals$A <- 3
flushReact()
invisible(gc())
expect_equal(c(vals_removed, obs_removed), c(FALSE, TRUE))
# Deleting vals should work immediately now
rm(vals)
invisible(gc()) # Removes vals object
expect_equal(c(vals_removed, obs_removed), c(TRUE, TRUE))
})

View File

@@ -59,16 +59,16 @@ test_that("Functions are not over-reactive", {
values <- reactiveValues(A=10)
funcA <- reactive(function() {
funcA <- reactive({
values$A
})
funcB <- reactive(function() {
funcB <- reactive({
funcA()
values$A
})
obsC <- observe(function() {
obsC <- observe({
funcB()
})
@@ -100,13 +100,13 @@ test_that("overreactivity2", {
observed_value2 <- NA
values <- reactiveValues(A=1)
funcB <- reactive(function() {
funcB <- reactive({
values$A + 5
})
obsC <- observe(function() {
obsC <- observe({
observed_value1 <<- funcB() * values$A
})
obsD <- observe(function() {
obsD <- observe({
observed_value2 <<- funcB() * values$A
})
@@ -135,15 +135,15 @@ test_that("overreactivity2", {
test_that("isolation", {
values <- reactiveValues(A=10, C=NULL)
obsB <- observe(function() {
obsB <- observe({
values$C <- values$A > 0
})
funcD <- reactive(function() {
funcD <- reactive({
values$C
})
obsE <- observe(function() {
obsE <- observe({
funcD()
})
@@ -163,15 +163,15 @@ test_that("laziness", {
values <- reactiveValues(A=10)
funcA <- reactive(function() {
funcA <- reactive({
values$A > 0
})
funcB <- reactive(function() {
funcB <- reactive({
funcA()
})
obsC <- observe(function() {
obsC <- observe({
if (values$A > 10)
return()
funcB()
@@ -203,10 +203,10 @@ test_that("order of evaluation", {
observed_value <- NA
values <- reactiveValues(A=1)
funcB <- reactive(function() {
funcB <- reactive({
values$A + 5
})
obsC <- observe(function() {
obsC <- observe({
observed_value <<- values$A * funcB()
})
@@ -230,10 +230,10 @@ test_that("order of evaluation", {
observed_value <- NA
values <- reactiveValues(A=1)
funcB <- reactive(function() {
funcB <- reactive({
values$A + 5
})
obsC <- observe(function() {
obsC <- observe({
observed_value <<- funcB() * values$A
})
@@ -259,18 +259,18 @@ test_that("isolate() blocks invalidations from propagating", {
obsD_value <- NA
values <- reactiveValues(A=1, B=10)
funcB <- reactive(function() {
funcB <- reactive({
values$B + 100
})
# References to valueB and funcB are isolated
obsC <- observe(function() {
obsC <- observe({
obsC_value <<-
values$A + isolate(values$B) + isolate(funcB())
})
# In contrast with obsC, this has a non-isolated reference to funcB
obsD <- observe(function() {
obsD <- observe({
obsD_value <<-
values$A + isolate(values$B) + funcB()
})
@@ -309,11 +309,29 @@ test_that("isolate() blocks invalidations from propagating", {
expect_equal(execCount(obsD), 4)
})
test_that("isolate() evaluates expressions in calling environment", {
outside <- 1
inside <- 1
loc <- 1
outside <- isolate(2) # Assignment outside isolate
isolate(inside <- 2) # Assignment inside isolate
# Should affect vars in the calling environment
expect_equal(outside, 2)
expect_equal(inside, 2)
isolate(local(loc <<- 2)) # <<- inside isolate(local)
isolate(local(loc <- 3)) # <- inside isolate(local) - should have no effect
expect_equal(loc, 2)
})
test_that("Circular refs/reentrancy in reactive functions work", {
values <- reactiveValues(A=3)
funcB <- reactive(function() {
funcB <- reactive({
# Each time fB executes, it reads and then writes valueA,
# effectively invalidating itself--until valueA becomes 0.
if (values$A == 0)
@@ -322,7 +340,7 @@ test_that("Circular refs/reentrancy in reactive functions work", {
return(values$A)
})
obsC <- observe(function() {
obsC <- observe({
funcB()
})
@@ -339,14 +357,14 @@ test_that("Circular refs/reentrancy in reactive functions work", {
test_that("Simple recursion", {
values <- reactiveValues(A=5)
funcB <- reactive(function() {
funcB <- reactive({
if (values$A == 0)
return(0)
values$A <- values$A - 1
funcB()
})
obsC <- observe(function() {
obsC <- observe({
funcB()
})
@@ -359,13 +377,13 @@ test_that("Non-reactive recursion", {
nonreactiveA <- 3
outputD <- NULL
funcB <- reactive(function() {
funcB <- reactive({
if (nonreactiveA == 0)
return(0)
nonreactiveA <<- nonreactiveA - 1
return(funcB())
})
obsC <- observe(function() {
obsC <- observe({
outputD <<- funcB()
})
@@ -377,7 +395,7 @@ test_that("Non-reactive recursion", {
test_that("Circular dep with observer only", {
values <- reactiveValues(A=3)
obsB <- observe(function() {
obsB <- observe({
if (values$A == 0)
return()
values$A <- values$A - 1
@@ -390,12 +408,12 @@ test_that("Circular dep with observer only", {
test_that("Writing then reading value is not circular", {
values <- reactiveValues(A=3)
funcB <- reactive(function() {
funcB <- reactive({
values$A <- isolate(values$A) - 1
values$A
})
obsC <- observe(function() {
obsC <- observe({
funcB()
})
@@ -413,17 +431,17 @@ test_that("names() and reactiveValuesToList()", {
values <- reactiveValues(A=1, .B=2)
# Dependent on names
depNames <- observe(function() {
depNames <- observe({
names(values)
})
# Dependent on all non-hidden objects
depValues <- observe(function() {
depValues <- observe({
reactiveValuesToList(values)
})
# Dependent on all objects, including hidden
depAllValues <- observe(function() {
depAllValues <- observe({
reactiveValuesToList(values, all.names = TRUE)
})
@@ -441,27 +459,194 @@ test_that("names() and reactiveValuesToList()", {
expect_equal(execCount(depValues), 1)
expect_equal(execCount(depAllValues), 1)
# Update existing variable
values$A <- 2
flushReact()
expect_equal(execCount(depNames), 1)
expect_equal(execCount(depValues), 2)
expect_equal(execCount(depAllValues), 2)
# Update existing hidden variable
values$.B <- 3
flushReact()
expect_equal(execCount(depNames), 1)
expect_equal(execCount(depValues), 2)
expect_equal(execCount(depAllValues), 3)
# Add new variable
values$C <- 1
flushReact()
expect_equal(execCount(depNames), 2)
expect_equal(execCount(depValues), 3)
expect_equal(execCount(depAllValues), 4)
# Add new hidden variable
values$.D <- 1
flushReact()
expect_equal(execCount(depNames), 3)
expect_equal(execCount(depValues), 3)
expect_equal(execCount(depAllValues), 5)
})
test_that("Observer pausing works", {
values <- reactiveValues(a=1)
funcA <- reactive({
values$a
})
obsB <- observe({
funcA()
})
# Important: suspend() only affects observer at invalidation time
# Observers are invalidated at creation time, so it will run once regardless
# of being suspended
obsB$suspend()
flushReact()
expect_equal(execCount(funcA), 1)
expect_equal(execCount(obsB), 1)
# When resuming, if nothing changed, don't do anything
obsB$resume()
flushReact()
expect_equal(execCount(funcA), 1)
expect_equal(execCount(obsB), 1)
# Make sure suspended observers do not flush, but do invalidate
obsB_invalidated <- FALSE
obsB$onInvalidate(function() {obsB_invalidated <<- TRUE})
obsB$suspend()
values$a <- 2
flushReact()
expect_equal(obsB_invalidated, TRUE)
expect_equal(execCount(funcA), 1)
expect_equal(execCount(obsB), 1)
obsB$resume()
values$a <- 2.5
obsB$suspend()
flushReact()
expect_equal(execCount(funcA), 2)
expect_equal(execCount(obsB), 2)
values$a <- 3
flushReact()
expect_equal(execCount(funcA), 2)
expect_equal(execCount(obsB), 2)
# If onInvalidate() is added _after_ obsB is suspended and the values$a
# changes, then it shouldn't get run (onInvalidate runs on invalidation, not
# on flush)
values$a <- 4
obsB_invalidated2 <- FALSE
obsB$onInvalidate(function() {obsB_invalidated2 <<- TRUE})
obsB$resume()
flushReact()
expect_equal(execCount(funcA), 3)
expect_equal(execCount(obsB), 3)
expect_equal(obsB_invalidated2, FALSE)
})
test_that("suspended/resumed observers run at most once", {
values <- reactiveValues(A=1)
obs <- observe(function() {
values$A
})
expect_equal(execCount(obs), 0)
# First flush should run obs once
flushReact()
expect_equal(execCount(obs), 1)
# Modify the dependency at each stage of suspend/resume/flush should still
# only result in one run of obs()
values$A <- 2
obs$suspend()
values$A <- 3
obs$resume()
values$A <- 4
flushReact()
expect_equal(execCount(obs), 2)
})
test_that("reactive() accepts quoted and unquoted expressions", {
vals <- reactiveValues(A=1)
# Unquoted expression, with curly braces
fun <- reactive({ vals$A + 1 })
expect_equal(isolate(fun()), 2)
# Unquoted expression, no curly braces
fun <- reactive(vals$A + 1)
expect_equal(isolate(fun()), 2)
# Quoted expression
fun <- reactive(quote(vals$A + 1), quoted = TRUE)
expect_equal(isolate(fun()), 2)
# Quoted expression, saved in a variable
q_expr <- quote(vals$A + 1)
fun <- reactive(q_expr, quoted = TRUE)
expect_equal(isolate(fun()), 2)
# If function is used, work, but print message
expect_message(fun <- reactive(function() { vals$A + 1 }))
expect_equal(isolate(fun()), 2)
# Check that environment is correct - parent environment should be this one
this_env <- environment()
fun <- reactive(environment())
expect_identical(isolate(parent.env(fun())), this_env)
# Sanity check: environment structure for a reactive() should be the same as for
# a normal function
fun <- function() environment()
expect_identical(parent.env(fun()), this_env)
})
test_that("observe() accepts quoted and unquoted expressions", {
vals <- reactiveValues(A=0)
valB <- 0
# Unquoted expression, with curly braces
observe({ valB <<- vals$A + 1})
flushReact()
expect_equal(valB, 1)
# Unquoted expression, no curly braces
observe({ valB <<- vals$A + 2})
flushReact()
expect_equal(valB, 2)
# Quoted expression
observe(quote(valB <<- vals$A + 3), quoted = TRUE)
flushReact()
expect_equal(valB, 3)
# Quoted expression, saved in a variable
q_expr <- quote(valB <<- vals$A + 4)
fun <- observe(q_expr, quoted = TRUE)
flushReact()
expect_equal(valB, 4)
# If function is used, work, but print message
expect_message(observe(function() { valB <<- vals$A + 5 }))
flushReact()
expect_equal(valB, 5)
# Check that environment is correct - parent environment should be this one
this_env <- environment()
inside_env <- NULL
fun <- observe(inside_env <<- environment())
flushReact()
expect_identical(parent.env(inside_env), this_env)
})

View File

@@ -1,33 +1,33 @@
context("text")
test_that("reactivePrint and reactiveText behavior is correct", {
expect_equal(isolate(reactivePrint(function() "foo")()),
test_that("renderPrint and renderText behavior is correct", {
expect_equal(isolate(renderPrint({ "foo" })()),
'[1] "foo"')
expect_equal(isolate(reactivePrint(function() invisible("foo"))()),
expect_equal(isolate(renderPrint({ invisible("foo") })()),
'')
expect_equal(isolate(reactivePrint(function() { print("foo"); "bar"})()),
expect_equal(isolate(renderPrint({ print("foo"); "bar"})()),
'[1] "foo"\n[1] "bar"')
expect_equal(isolate(reactivePrint(function() NULL)()),
expect_equal(isolate(renderPrint({ NULL })()),
'NULL')
expect_equal(isolate(reactivePrint(function() invisible())()),
expect_equal(isolate(renderPrint({ invisible() })()),
'')
expect_equal(isolate(reactivePrint(function() 1:5)()),
expect_equal(isolate(renderPrint({ 1:5 })()),
'[1] 1 2 3 4 5')
expect_equal(isolate(reactiveText(function() "foo")()),
expect_equal(isolate(renderText({ "foo" })()),
'foo')
expect_equal(isolate(reactiveText(function() invisible("foo"))()),
expect_equal(isolate(renderText({ invisible("foo") })()),
'foo')
# Capture the print output so it's not shown on console during test, and
# also check that it is correct
print_out <- capture.output(ret <- isolate(reactiveText(function() { print("foo"); "bar"})()))
print_out <- capture.output(ret <- isolate(renderText({ print("foo"); "bar"})()))
expect_equal(ret, 'bar')
expect_equal(print_out, '[1] "foo"')
expect_equal(isolate(reactiveText(function() NULL)()),
expect_equal(isolate(renderText({ NULL })()),
'')
expect_equal(isolate(reactiveText(function() invisible())()),
expect_equal(isolate(renderText({ invisible() })()),
'')
expect_equal(isolate(reactiveText(function() 1:5)()),
expect_equal(isolate(renderText({ 1:5 })()),
'1 2 3 4 5')
})
@@ -35,22 +35,22 @@ test_that("reactive functions save visibility state", {
# Call each function twice - should be no change in state with second call
# invisible NULL
f <- reactive(function() invisible())
f <- reactive({ invisible() })
expect_identical(withVisible(isolate(f())), list(value=NULL, visible=FALSE))
expect_identical(withVisible(isolate(f())), list(value=NULL, visible=FALSE))
# visible NULL
f <- reactive(function() NULL)
f <- reactive({ NULL })
expect_identical(withVisible(isolate(f())), list(value=NULL, visible=TRUE))
expect_identical(withVisible(isolate(f())), list(value=NULL, visible=TRUE))
# invisible non-NULL value
f <- reactive(function() invisible(10))
f <- reactive({ invisible(10)})
expect_identical(withVisible(isolate(f())), list(value=10, visible=FALSE))
expect_identical(withVisible(isolate(f())), list(value=10, visible=FALSE))
# visible non-NULL value
f <- reactive(function() 10)
f <- reactive({ 10 })
expect_identical(withVisible(isolate(f())), list(value=10, visible=TRUE))
expect_identical(withVisible(isolate(f())), list(value=10, visible=TRUE))
})

View File

@@ -278,8 +278,13 @@
this.lastSentValues[name] = jsonValue;
this.target.setInput(name, value);
};
this.reset = function() {
this.lastSentValues = {};
this.reset = function(values) {
values = values || {};
var strValues = {};
$.each(values, function(key, value) {
strValues[key] = JSON.stringify(value);
});
this.lastSentValues = strValues;
};
}).call(InputNoResendDecorator.prototype);
@@ -1007,17 +1012,22 @@
},
getValue: function(el) {
var numberVal = $(el).val();
if (!isNaN(numberVal))
if (/^\s*$/.test(numberVal)) // Return null if all whitespace
return null;
else if (!isNaN(numberVal)) // If valid Javascript number string, coerce to number
return +numberVal;
else
return numberVal;
return numberVal; // If other string like "1e6", send it unchanged
},
getType: function(el) {
return "number"
}
});
inputBindings.register(numberInputBinding, 'shiny.numberInput');
var sliderInputBinding = {};
$.extend(sliderInputBinding, numberInputBinding, {
$.extend(sliderInputBinding, textInputBinding, {
find: function(scope) {
// Check if jslider plugin is loaded
if (!$.fn.slider)
@@ -1278,6 +1288,7 @@
// Send later in case DOM layout isn't final yet.
setTimeout(sendPlotSize, 0);
setTimeout(sendOutputHiddenState, 0);
}
function unbindOutputs(scope) {
@@ -1302,9 +1313,9 @@
return $(el).val();
}
var inputs = new InputNoResendDecorator(new InputBatchSender(shinyapp));
var inputsRate = new InputRateDecorator(inputs);
var inputsDefer = new InputDeferDecorator(inputs);
var inputsNoResend = new InputNoResendDecorator(new InputBatchSender(shinyapp));
var inputsRate = new InputRateDecorator(inputsNoResend);
var inputsDefer = new InputDeferDecorator(inputsNoResend);
inputs = inputsRate;
$('input[type="submit"], button[type="submit"]').each(function() {
@@ -1372,7 +1383,7 @@
var ratePolicy = binding.getRatePolicy();
if (ratePolicy != null) {
inputsRate.setRatePolicy(
id,
effectiveId,
ratePolicy.policy,
ratePolicy.delay);
}
@@ -1490,15 +1501,37 @@
// The server needs to know the size of each plot output element, in case
// the plot is auto-sizing
$('.shiny-plot-output').each(function() {
var width = this.offsetWidth;
var height = this.offsetHeight;
initialValues['.shinyout_' + this.id + '_width'] = width;
initialValues['.shinyout_' + this.id + '_height'] = height;
if (this.offsetWidth !== 0 || this.offsetHeight !== 0) {
initialValues['.shinyout_' + this.id + '_width'] = this.offsetWidth;
initialValues['.shinyout_' + this.id + '_height'] = this.offsetHeight;
}
});
function sendPlotSize() {
$('.shiny-plot-output').each(function() {
inputs.setInput('.shinyout_' + this.id + '_width', this.offsetWidth);
inputs.setInput('.shinyout_' + this.id + '_height', this.offsetHeight);
if (this.offsetWidth !== 0 || this.offsetHeight !== 0) {
inputs.setInput('.shinyout_' + this.id + '_width', this.offsetWidth);
inputs.setInput('.shinyout_' + this.id + '_height', this.offsetHeight);
}
});
}
// Set initial state of outputs to hidden, if needed
$('.shiny-bound-output').each(function() {
if (this.offsetWidth === 0 && this.offsetHeight === 0) {
initialValues['.shinyout_' + this.id + '_hidden'] = true;
} else {
initialValues['.shinyout_' + this.id + '_hidden'] = false;
}
});
// Send update when hidden state changes
function sendOutputHiddenState() {
$('.shiny-bound-output').each(function() {
// Assume that the object is hidden when width and height are 0
if (this.offsetWidth === 0 && this.offsetHeight === 0) {
inputs.setInput('.shinyout_' + this.id + '_hidden', true);
} else {
inputs.setInput('.shinyout_' + this.id + '_hidden', false);
}
});
}
// The size of each plot may change either because the browser window was
@@ -1506,9 +1539,12 @@
// of 0x0). It's OK to over-report sizes because the input pipeline will
// filter out values that haven't changed.
$(window).resize(debounce(500, sendPlotSize));
$('body').on('shown.sendPlotSize hidden.sendPlotSize', '*', sendPlotSize);
$('body').on('shown.sendPlotSize', '*', sendPlotSize);
$('body').on('shown.sendOutputHiddenState hidden.sendOutputHiddenState', '*',
sendOutputHiddenState);
// We've collected all the initial values--start the server process!
inputsNoResend.reset(initialValues);
shinyapp.connect(initialValues);
} // function initShiny()

63
man/exprToFunction.Rd Normal file
View File

@@ -0,0 +1,63 @@
\name{exprToFunction}
\alias{exprToFunction}
\title{Convert an expression or quoted expression to a function}
\usage{
exprToFunction(expr, env = parent.frame(2),
quoted = FALSE)
}
\arguments{
\item{expr}{A quoted or unquoted expression, or a
function.}
\item{env}{The desired environment for the function.
Defaults to the calling environment two steps back.}
\item{quoted}{Is the expression quoted?}
}
\description{
This is to be called from another function, because it
will attempt to get an unquoted expression from two calls
back.
}
\details{
If expr is a quoted expression, then this just converts
it to a function. If expr is a function, then this simply
returns expr (and prints a deprecation message. If expr
was a non-quoted expression from two calls back, then
this will quote the original expression and convert it to
a function.
}
\examples{
# Example of a new renderer, similar to renderText
# This is something that toolkit authors will do
renderTriple <- function(expr, env=parent.frame(), quoted=FALSE) {
# Convert expr to a function
func <- shiny::exprToFunction(expr, env, quoted)
function() {
value <- func()
paste(rep(value, 3), collapse=", ")
}
}
# Example of using the renderer.
# This is something that app authors will do.
values <- reactiveValues(A="text")
\dontrun{
# Create an output object
output$tripleA <- renderTriple({
values$A
})
}
# At the R console, you can experiment with the renderer using isolate()
tripleA <- renderTriple({
values$A
})
isolate(tripleA())
# "text, text, text"
}

View File

@@ -21,8 +21,8 @@
}
\details{
\code{uiOutput} is intended to be used with
\code{reactiveUI} on the server side. It is currently
just an alias for \code{htmlOutput}.
\code{renderUI} on the server side. It is currently just
an alias for \code{htmlOutput}.
}
\examples{
htmlOutput("summary")

View File

@@ -6,11 +6,11 @@
}
\arguments{
\item{expr}{An expression that can access reactive values
or functions.}
or expressions.}
}
\description{
Executes the given expression in a scope where reactive
values or functions can be read, but they cannot cause
values or expression can be read, but they cannot cause
the reactive scope of the caller to be re-evaluated when
they change.
}
@@ -19,14 +19,26 @@
causes a relationship to be established between the
caller and the reactive value, where a change to the
reactive value will cause the caller to re-execute. (The
same applies for the act of getting a reactive function's
value.) The \code{isolate} function lets you read a
reactive value or function without establishing this
relationship.
same applies for the act of getting a reactive
expression's value.) The \code{isolate} function lets you
read a reactive value or expression without establishing
this relationship.
The expression given to \code{isolate()} is evaluated in
the calling environment. This means that if you assign a
variable inside the \code{isolate()}, its value will be
visible outside of the \code{isolate()}. If you want to
avoid this, you can use \code{\link{local}()} inside the
\code{isolate()}.
This function can also be useful for calling reactive
expression at the console, which can be useful for
debugging. To do so, simply wrap the calls to the
reactive expression with \code{isolate()}.
}
\examples{
\dontrun{
observer(function() {
observe({
input$saveButton # Do take a dependency on input$saveButton
# isolate a simple expression
@@ -34,7 +46,7 @@ observer(function() {
writeToDatabase(data)
})
observer(function() {
observe({
input$saveButton # Do take a dependency on input$saveButton
# isolate a whole block
@@ -45,6 +57,28 @@ observer(function() {
})
writeToDatabase(data)
})
}
observe({
x <- 1
# x outside of isolate() is affected
isolate(x <- 2)
print(x) # 2
y <- 1
# Use local() to avoid affecting calling environment
isolate(local(y <- 2))
print(y) # 1
})
}
# Can also use isolate to call reactive expressions from the R console
values <- reactiveValues(A=1)
fun <- reactive({ as.character(values$A) })
isolate(fun())
# "1"
# isolate also works if the reactive expression accesses values from the
# input object, like input$x
}

View File

@@ -2,32 +2,67 @@
\alias{observe}
\title{Create a reactive observer}
\usage{
observe(func)
observe(x, env = parent.frame(), quoted = FALSE,
label = NULL, suspended = FALSE)
}
\arguments{
\item{func}{The function to observe. It must not have any
parameters. Any return value from this function will be
ignored.}
\item{x}{An expression (quoted or unquoted). Any return
value will be ignored.}
\item{env}{The parent environment for the reactive
expression. By default, this is the calling environment,
the same as when defining an ordinary non-reactive
expression.}
\item{quoted}{Is the expression quoted? By default, this
is \code{FALSE}. This is useful when you want to use an
expression that is stored in a variable; to do so, it
must be quoted with `quote()`.}
\item{label}{A label for the observer, useful for
debugging.}
\item{suspended}{If \code{TRUE}, start the observer in a
suspended state. If \code{FALSE} (the default), start in
a non-suspended state.}
}
\description{
Creates an observer from the given function. An observer
is like a reactive function in that it can read reactive
values and call reactive functions, and will
Creates an observer from the given expression An observer
is like a reactive expression in that it can read
reactive values and call reactive expressions, and will
automatically re-execute when those dependencies change.
But unlike reactive functions, it doesn't yield a result
But unlike reactive expression, it doesn't yield a result
and can't be used as an input to other reactive
functions. Thus, observers are only useful for their side
effects (for example, performing I/O).
expressions. Thus, observers are only useful for their
side effects (for example, performing I/O).
}
\details{
Another contrast between reactive functions and observers
is their execution strategy. Reactive functions use lazy
evaluation; that is, when their dependencies change, they
don't re-execute right away but rather wait until they
are called by someone else. Indeed, if they are not
called then they will never re-execute. In contrast,
observers use eager evaluation; as soon as their
dependencies change, they schedule themselves to
re-execute.
Another contrast between reactive expressions and
observers is their execution strategy. Reactive
expressions use lazy evaluation; that is, when their
dependencies change, they don't re-execute right away but
rather wait until they are called by someone else.
Indeed, if they are not called then they will never
re-execute. In contrast, observers use eager evaluation;
as soon as their dependencies change, they schedule
themselves to re-execute.
}
\examples{
values <- reactiveValues(A=1)
obsB <- observe({
print(values$A + 1)
})
# Can use quoted expressions
obsC <- observe(quote({ print(values$A + 2) }), quoted = TRUE)
# To store expressions for later conversion to observe, use quote()
expr_q <- quote({ print(values$A + 3) })
obsD <- observe(expr_q, quoted = TRUE)
# In a normal Shiny app, the web client will trigger flush events. If you
# are at the console, you can force a flush with flushReact()
shiny:::flushReact()
}

36
man/outputOptions.Rd Normal file
View File

@@ -0,0 +1,36 @@
\name{outputOptions}
\alias{outputOptions}
\title{Set options for an output object.}
\usage{
outputOptions(x, name, ...)
}
\arguments{
\item{x}{A shinyoutput object (typically \code{output}).}
\item{name}{The name of an output observer in the
shinyoutput object.}
\item{...}{Options to set for the output observer.}
}
\description{
These are the available options for an output object:
\itemize{ \item suspendWhenHidden. When \code{TRUE} (the
default), the output object will be suspended (not
execute) when it is hidden on the web page. When
\code{FALSE}, the output object will not suspend when
hidden, and if it was already hidden and suspended, then
it will resume immediately. }
}
\examples{
\dontrun{
# Get the list of options for all observers within output
outputOptions(output)
# Disable suspend for output$myplot
outputOptions(output, "myplot", suspendWhenHidden = FALSE)
# Get the list of options for output$myplot
outputOptions(output, "myplot")
}
}

View File

@@ -18,7 +18,7 @@
A plot output element that can be included in a panel
}
\description{
Render a \link{reactivePlot} within an application page.
Render a \link{renderPlot} within an application page.
}
\examples{
# Show a plot of the generated distribution

View File

@@ -1,35 +1,65 @@
\name{reactive}
\alias{reactive}
\title{Create a Reactive Function}
\title{Create a reactive expression}
\usage{
reactive(x)
reactive(x, env = parent.frame(), quoted = FALSE,
label = NULL)
}
\arguments{
\item{x}{The value or function to make reactive. The
function must not have any parameters.}
}
\value{
A reactive function. (Note that reactive functions can
only be called from within other reactive functions.)
\item{x}{An expression (quoted or unquoted).}
\item{env}{The parent environment for the reactive
expression. By default, this is the calling environment,
the same as when defining an ordinary non-reactive
expression.}
\item{quoted}{Is the expression quoted? By default, this
is \code{FALSE}. This is useful when you want to use an
expression that is stored in a variable; to do so, it
must be quoted with `quote()`.}
\item{label}{A label for the reactive expression, useful
for debugging.}
}
\description{
Wraps a normal function to create a reactive function.
Conceptually, a reactive function is a function whose
result will change over time.
Wraps a normal expression to create a reactive
expression. Conceptually, a reactive expression is a
expression whose result will change over time.
}
\details{
Reactive functions are functions that can read reactive
values and call other reactive functions. Whenever a
reactive value changes, any reactive functions that
depended on it are marked as "invalidated" and will
automatically re-execute if necessary. If a reactive
function is marked as invalidated, any other reactive
functions that recently called it are also marked as
invalidated. In this way, invalidations ripple through
the functions that depend on each other.
Reactive expressions are expressions that can read
reactive values and call other reactive expressions.
Whenever a reactive value changes, any reactive
expressions that depended on it are marked as
"invalidated" and will automatically re-execute if
necessary. If a reactive expression is marked as
invalidated, any other reactive expressions that recently
called it are also marked as invalidated. In this way,
invalidations ripple through the expressions that depend
on each other.
See the
\href{http://rstudio.github.com/shiny/tutorial/}{Shiny
tutorial} for more information about reactive functions.
tutorial} for more information about reactive
expressions.
}
\examples{
values <- reactiveValues(A=1)
reactiveB <- reactive({
values$A + 1
})
# Can use quoted expressions
reactiveC <- reactive(quote({ values$A + 2 }), quoted = TRUE)
# To store expressions for later conversion to reactive, use quote()
expr_q <- quote({ values$A + 3 })
reactiveD <- reactive(expr_q, quoted = TRUE)
# View the values from the R console with isolate()
isolate(reactiveB())
isolate(reactiveC())
isolate(reactiveD())
}

View File

@@ -1,45 +1,19 @@
\name{reactivePlot}
\alias{reactivePlot}
\title{Plot Output}
\title{Plot output (deprecated)}
\usage{
reactivePlot(func, width = "auto", height = "auto", ...)
}
\arguments{
\item{func}{A function that generates a plot.}
\item{func}{A function.}
\item{width}{The width of the rendered plot, in pixels;
or \code{'auto'} to use the \code{offsetWidth} of the
HTML element that is bound to this plot. You can also
pass in a function that returns the width in pixels or
\code{'auto'}; in the body of the function you may
reference reactive values and functions.}
\item{width}{Width.}
\item{height}{The height of the rendered plot, in pixels;
or \code{'auto'} to use the \code{offsetHeight} of the
HTML element that is bound to this plot. You can also
pass in a function that returns the width in pixels or
\code{'auto'}; in the body of the function you may
reference reactive values and functions.}
\item{height}{Height.}
\item{...}{Arguments to be passed through to
\code{\link[grDevices]{png}}. These can be used to set
the width, height, background color, etc.}
\item{...}{Other arguments to pass on.}
}
\description{
Creates a reactive plot that is suitable for assigning to
an \code{output} slot.
}
\details{
The corresponding HTML output tag should be \code{div} or
\code{img} and have the CSS class name
\code{shiny-plot-output}.
For output, it will try to use the following devices, in
this order: quartz (via \code{\link[grDevices]{png}}),
then \code{\link[Cairo]{CairoPNG}}, and finally
\code{\link[grDevices]{png}}. This is in order of quality
of output. Notably, plain \code{png} output on Linux and
Windows may not antialias some point shapes, resulting in
poor quality output.
See \code{\link{renderPlot}}.
}

View File

@@ -1,101 +1,13 @@
\name{reactivePrint}
\alias{reactivePrint}
\title{Printable Output}
\title{Print output (deprecated)}
\usage{
reactivePrint(func)
}
\arguments{
\item{func}{A function that may print output and/or
return a printable R object.}
\item{func}{A function.}
}
\description{
Makes a reactive version of the given function that
captures any printed output, and also captures its
printable result (unless \code{\link{invisible}}), into a
string. The resulting function is suitable for assigning
to an \code{output} slot.
}
\details{
The corresponding HTML output tag can be anything (though
\code{pre} is recommended if you need a monospace font
and whitespace preserved) and should have the CSS class
name \code{shiny-text-output}.
The result of executing \code{func} will be printed
inside a \code{\link[utils]{capture.output}} call.
Note that unlike most other Shiny output functions, if
the given function returns \code{NULL} then \code{NULL}
will actually be visible in the output. To display
nothing, make your function return
\code{\link{invisible}()}.
}
\examples{
isolate({
# reactivePrint captures any print output, converts it to a string, and
# returns it
visFun <- reactivePrint(function() "foo")
visFun()
# '[1] "foo"'
invisFun <- reactivePrint(function() invisible("foo"))
invisFun()
# ''
multiprintFun <- reactivePrint(function() {
print("foo");
"bar"
})
multiprintFun()
# '[1] "foo"\\n[1] "bar"'
nullFun <- reactivePrint(function() NULL)
nullFun()
# 'NULL'
invisNullFun <- reactivePrint(function() invisible(NULL))
invisNullFun()
# ''
vecFun <- reactivePrint(function() 1:5)
vecFun()
# '[1] 1 2 3 4 5'
# Contrast with reactiveText, which takes the value returned from the function
# and uses cat() to convert it to a string
visFun <- reactiveText(function() "foo")
visFun()
# 'foo'
invisFun <- reactiveText(function() invisible("foo"))
invisFun()
# 'foo'
multiprintFun <- reactiveText(function() {
print("foo");
"bar"
})
multiprintFun()
# 'bar'
nullFun <- reactiveText(function() NULL)
nullFun()
# ''
invisNullFun <- reactiveText(function() invisible(NULL))
invisNullFun()
# ''
vecFun <- reactiveText(function() 1:5)
vecFun()
# '1 2 3 4 5'
})
}
\seealso{
\code{\link{reactiveText}} for displaying the value
returned from a function, instead of the printed output.
See \code{\link{renderPrint}}.
}

View File

@@ -1,23 +1,15 @@
\name{reactiveTable}
\alias{reactiveTable}
\title{Table Output}
\title{Table output (deprecated)}
\usage{
reactiveTable(func, ...)
}
\arguments{
\item{func}{A function that returns an R object that can
be used with \code{\link[xtable]{xtable}}.}
\item{func}{A function.}
\item{...}{Arguments to be passed through to
\code{\link[xtable]{xtable}} and
\code{\link[xtable]{print.xtable}}.}
\item{...}{Other arguments to pass on.}
}
\description{
Creates a reactive table that is suitable for assigning
to an \code{output} slot.
}
\details{
The corresponding HTML output tag should be \code{div}
and have the CSS class name \code{shiny-html-output}.
See \code{\link{renderTable}}.
}

View File

@@ -1,95 +1,13 @@
\name{reactiveText}
\alias{reactiveText}
\title{Text Output}
\title{Text output (deprecated)}
\usage{
reactiveText(func)
}
\arguments{
\item{func}{A function that returns an R object that can
be used as an argument to \code{cat}.}
\item{func}{A function.}
}
\description{
Makes a reactive version of the given function that also
uses \code{\link[base]{cat}} to turn its result into a
single-element character vector.
}
\details{
The corresponding HTML output tag can be anything (though
\code{pre} is recommended if you need a monospace font
and whitespace preserved) and should have the CSS class
name \code{shiny-text-output}.
The result of executing \code{func} will passed to
\code{cat}, inside a \code{\link[utils]{capture.output}}
call.
}
\examples{
isolate({
# reactivePrint captures any print output, converts it to a string, and
# returns it
visFun <- reactivePrint(function() "foo")
visFun()
# '[1] "foo"'
invisFun <- reactivePrint(function() invisible("foo"))
invisFun()
# ''
multiprintFun <- reactivePrint(function() {
print("foo");
"bar"
})
multiprintFun()
# '[1] "foo"\\n[1] "bar"'
nullFun <- reactivePrint(function() NULL)
nullFun()
# 'NULL'
invisNullFun <- reactivePrint(function() invisible(NULL))
invisNullFun()
# ''
vecFun <- reactivePrint(function() 1:5)
vecFun()
# '[1] 1 2 3 4 5'
# Contrast with reactiveText, which takes the value returned from the function
# and uses cat() to convert it to a string
visFun <- reactiveText(function() "foo")
visFun()
# 'foo'
invisFun <- reactiveText(function() invisible("foo"))
invisFun()
# 'foo'
multiprintFun <- reactiveText(function() {
print("foo");
"bar"
})
multiprintFun()
# 'bar'
nullFun <- reactiveText(function() NULL)
nullFun()
# ''
invisNullFun <- reactiveText(function() invisible(NULL))
invisNullFun()
# ''
vecFun <- reactiveText(function() 1:5)
vecFun()
# '1 2 3 4 5'
})
}
\seealso{
\code{\link{reactivePrint}} for capturing the print
output of a function, rather than the returned text
value.
See \code{\link{renderText}}.
}

View File

@@ -21,7 +21,7 @@
timers are triggered simply by the passage of time.
}
\details{
\link[=reactive]{Reactive functions} and observers that
\link[=reactive]{Reactive expressions} and observers that
want to be invalidated by the timer need to call the
timer function that \code{reactiveTimer} returns, even if
the current time value is not actually needed.

View File

@@ -1,33 +1,13 @@
\name{reactiveUI}
\alias{reactiveUI}
\title{UI Output}
\title{UI output (deprecated)}
\usage{
reactiveUI(func)
}
\arguments{
\item{func}{A function that returns a Shiny tag object,
\code{\link{HTML}}, or a list of such objects.}
\item{func}{A function.}
}
\description{
\bold{Experimental feature.} Makes a reactive version of
a function that generates HTML using the Shiny UI
library.
}
\details{
The corresponding HTML output tag should be \code{div}
and have the CSS class name \code{shiny-html-output} (or
use \code{\link{uiOutput}}).
}
\examples{
\dontrun{
output$moreControls <- reactiveUI(function() {
list(
)
})
}
}
\seealso{
conditionalPanel
See \code{\link{renderUI}}.
}

View File

@@ -13,7 +13,7 @@
This function returns an object for storing reactive
values. It is similar to a list, but with special
capabilities for reactive programming. When you read a
value from it, the calling reactive function takes a
value from it, the calling reactive expression takes a
reactive dependency on that value, and when you write to
it, it notifies any reactive functions that depend on
that value.

56
man/renderPlot.Rd Normal file
View File

@@ -0,0 +1,56 @@
\name{renderPlot}
\alias{renderPlot}
\title{Plot Output}
\usage{
renderPlot(expr, width = "auto", height = "auto", ...,
env = parent.frame(), quoted = FALSE, func = NULL)
}
\arguments{
\item{expr}{An expression that generates a plot.}
\item{width}{The width of the rendered plot, in pixels;
or \code{'auto'} to use the \code{offsetWidth} of the
HTML element that is bound to this plot. You can also
pass in a function that returns the width in pixels or
\code{'auto'}; in the body of the function you may
reference reactive values and functions.}
\item{height}{The height of the rendered plot, in pixels;
or \code{'auto'} to use the \code{offsetHeight} of the
HTML element that is bound to this plot. You can also
pass in a function that returns the width in pixels or
\code{'auto'}; in the body of the function you may
reference reactive values and functions.}
\item{...}{Arguments to be passed through to
\code{\link[grDevices]{png}}. These can be used to set
the width, height, background color, etc.}
\item{env}{The environment in which to evaluate
\code{expr}.}
\item{quoted}{Is \code{expr} a quoted expression (with
\code{quote()})? This is useful if you want to save an
expression in a variable.}
\item{func}{A function that generates a plot (deprecated;
use \code{expr} instead).}
}
\description{
Renders a reactive plot that is suitable for assigning to
an \code{output} slot.
}
\details{
The corresponding HTML output tag should be \code{div} or
\code{img} and have the CSS class name
\code{shiny-plot-output}.
For output, it will try to use the following devices, in
this order: quartz (via \code{\link[grDevices]{png}}),
then \code{\link[Cairo]{CairoPNG}}, and finally
\code{\link[grDevices]{png}}. This is in order of quality
of output. Notably, plain \code{png} output on Linux and
Windows may not antialias some point shapes, resulting in
poor quality output.
}

112
man/renderPrint.Rd Normal file
View File

@@ -0,0 +1,112 @@
\name{renderPrint}
\alias{renderPrint}
\title{Printable Output}
\usage{
renderPrint(expr, env = parent.frame(), quoted = FALSE,
func = NULL)
}
\arguments{
\item{expr}{An expression that may print output and/or
return a printable R object.}
\item{env}{The environment in which to evaluate
\code{expr}.}
\item{quoted}{Is \code{expr} a quoted expression (with
\code{quote()})? This}
\item{func}{A function that may print output and/or
return a printable R object (deprecated; use \code{expr}
instead).}
}
\description{
Makes a reactive version of the given function that
captures any printed output, and also captures its
printable result (unless \code{\link{invisible}}), into a
string. The resulting function is suitable for assigning
to an \code{output} slot.
}
\details{
The corresponding HTML output tag can be anything (though
\code{pre} is recommended if you need a monospace font
and whitespace preserved) and should have the CSS class
name \code{shiny-text-output}.
The result of executing \code{func} will be printed
inside a \code{\link[utils]{capture.output}} call.
Note that unlike most other Shiny output functions, if
the given function returns \code{NULL} then \code{NULL}
will actually be visible in the output. To display
nothing, make your function return
\code{\link{invisible}()}.
}
\examples{
isolate({
# renderPrint captures any print output, converts it to a string, and
# returns it
visFun <- renderPrint({ "foo" })
visFun()
# '[1] "foo"'
invisFun <- renderPrint({ invisible("foo") })
invisFun()
# ''
multiprintFun <- renderPrint({
print("foo");
"bar"
})
multiprintFun()
# '[1] "foo"\\n[1] "bar"'
nullFun <- renderPrint({ NULL })
nullFun()
# 'NULL'
invisNullFun <- renderPrint({ invisible(NULL) })
invisNullFun()
# ''
vecFun <- renderPrint({ 1:5 })
vecFun()
# '[1] 1 2 3 4 5'
# Contrast with renderText, which takes the value returned from the function
# and uses cat() to convert it to a string
visFun <- renderText({ "foo" })
visFun()
# 'foo'
invisFun <- renderText({ invisible("foo") })
invisFun()
# 'foo'
multiprintFun <- renderText({
print("foo");
"bar"
})
multiprintFun()
# 'bar'
nullFun <- renderText({ NULL })
nullFun()
# ''
invisNullFun <- renderText({ invisible(NULL) })
invisNullFun()
# ''
vecFun <- renderText({ 1:5 })
vecFun()
# '1 2 3 4 5'
})
}
\seealso{
\code{\link{renderText}} for displaying the value
returned from a function, instead of the printed output.
}

35
man/renderTable.Rd Normal file
View File

@@ -0,0 +1,35 @@
\name{renderTable}
\alias{renderTable}
\title{Table Output}
\usage{
renderTable(expr, ..., env = parent.frame(),
quoted = FALSE, func = NULL)
}
\arguments{
\item{expr}{An expression that returns an R object that
can be used with \code{\link[xtable]{xtable}}.}
\item{...}{Arguments to be passed through to
\code{\link[xtable]{xtable}} and
\code{\link[xtable]{print.xtable}}.}
\item{env}{The environment in which to evaluate
\code{expr}.}
\item{quoted}{Is \code{expr} a quoted expression (with
\code{quote()})? This is useful if you want to save an
expression in a variable.}
\item{func}{A function that returns an R object that can
be used with \code{\link[xtable]{xtable}} (deprecated;
use \code{expr} instead).}
}
\description{
Creates a reactive table that is suitable for assigning
to an \code{output} slot.
}
\details{
The corresponding HTML output tag should be \code{div}
and have the CSS class name \code{shiny-html-output}.
}

106
man/renderText.Rd Normal file
View File

@@ -0,0 +1,106 @@
\name{renderText}
\alias{renderText}
\title{Text Output}
\usage{
renderText(expr, env = parent.frame(), quoted = FALSE,
func = NULL)
}
\arguments{
\item{expr}{An expression that returns an R object that
can be used as an argument to \code{cat}.}
\item{env}{The environment in which to evaluate
\code{expr}.}
\item{quoted}{Is \code{expr} a quoted expression (with
\code{quote()})? This is useful if you want to save an
expression in a variable.}
\item{func}{A function that returns an R object that can
be used as an argument to \code{cat}.(deprecated; use
\code{expr} instead).}
}
\description{
Makes a reactive version of the given function that also
uses \code{\link[base]{cat}} to turn its result into a
single-element character vector.
}
\details{
The corresponding HTML output tag can be anything (though
\code{pre} is recommended if you need a monospace font
and whitespace preserved) and should have the CSS class
name \code{shiny-text-output}.
The result of executing \code{func} will passed to
\code{cat}, inside a \code{\link[utils]{capture.output}}
call.
}
\examples{
isolate({
# renderPrint captures any print output, converts it to a string, and
# returns it
visFun <- renderPrint({ "foo" })
visFun()
# '[1] "foo"'
invisFun <- renderPrint({ invisible("foo") })
invisFun()
# ''
multiprintFun <- renderPrint({
print("foo");
"bar"
})
multiprintFun()
# '[1] "foo"\\n[1] "bar"'
nullFun <- renderPrint({ NULL })
nullFun()
# 'NULL'
invisNullFun <- renderPrint({ invisible(NULL) })
invisNullFun()
# ''
vecFun <- renderPrint({ 1:5 })
vecFun()
# '[1] 1 2 3 4 5'
# Contrast with renderText, which takes the value returned from the function
# and uses cat() to convert it to a string
visFun <- renderText({ "foo" })
visFun()
# 'foo'
invisFun <- renderText({ invisible("foo") })
invisFun()
# 'foo'
multiprintFun <- renderText({
print("foo");
"bar"
})
multiprintFun()
# 'bar'
nullFun <- renderText({ NULL })
nullFun()
# ''
invisNullFun <- renderText({ invisible(NULL) })
invisNullFun()
# ''
vecFun <- renderText({ 1:5 })
vecFun()
# '1 2 3 4 5'
})
}
\seealso{
\code{\link{renderPrint}} for capturing the print output
of a function, rather than the returned text value.
}

45
man/renderUI.Rd Normal file
View File

@@ -0,0 +1,45 @@
\name{renderUI}
\alias{renderUI}
\title{UI Output}
\usage{
renderUI(expr, env = parent.frame(), quoted = FALSE,
func = NULL)
}
\arguments{
\item{expr}{An expression that returns a Shiny tag
object, \code{\link{HTML}}, or a list of such objects.}
\item{env}{The environment in which to evaluate
\code{expr}.}
\item{quoted}{Is \code{expr} a quoted expression (with
\code{quote()})? This is useful if you want to save an
expression in a variable.}
\item{func}{A function that returns a Shiny tag object,
\code{\link{HTML}}, or a list of such objects
(deprecated; use \code{expr} instead).}
}
\description{
\bold{Experimental feature.} Makes a reactive version of
a function that generates HTML using the Shiny UI
library.
}
\details{
The corresponding HTML output tag should be \code{div}
and have the CSS class name \code{shiny-html-output} (or
use \code{\link{uiOutput}}).
}
\examples{
\dontrun{
output$moreControls <- renderUI({
list(
)
})
}
}
\seealso{
conditionalPanel
}

View File

@@ -7,10 +7,10 @@
}
\arguments{
\item{gist}{The identifier of the gist. For example, if
the gist is https://gist.github.com/3239667, then
the gist is https://gist.github.com/jcheng5/3239667, then
\code{3239667}, \code{'3239667'}, and
\code{'https://gist.github.com/3239667'} are all valid
values.}
\code{'https://gist.github.com/jcheng5/3239667'} are all
valid values.}
\item{port}{The TCP port that the application should
listen on. Defaults to port 8100.}
@@ -25,8 +25,11 @@
}
\examples{
\dontrun{
runGist(4034323)
runGist("https://gist.github.com/4034323")
runGist(3239667)
runGist("https://gist.github.com/jcheng5/3239667")
# Old URL format without username
runGist("https://gist.github.com/3239667")
}
}

View File

@@ -27,7 +27,10 @@
\description{
Download and launch a Shiny application that is hosted at
a downloadable URL. The Shiny application must be saved
in a .zip, .tar, or .tar.gz file.
in a .zip, .tar, or .tar.gz file. The Shiny application
files must be contained in a subdirectory in the archive.
For example, the files might be \code{myapp/server.r} and
\code{myapp/ui.r}.
}
\examples{
\dontrun{

20
man/shinyDeprecated.Rd Normal file
View File

@@ -0,0 +1,20 @@
\name{shinyDeprecated}
\alias{shinyDeprecated}
\title{Print message for deprecated functions in Shiny}
\usage{
shinyDeprecated(new = NULL, msg = NULL,
old = as.character(sys.call(sys.parent()))[1L])
}
\arguments{
\item{new}{Name of replacement function.}
\item{msg}{Message to print. If used, this will override
the default message.}
\item{old}{Name of deprecated function.}
}
\description{
To disable these messages, use
\code{options(shiny.deprecation.messages=FALSE)}.
}

View File

@@ -32,7 +32,7 @@
# A very simple Shiny app that takes a message from the user
# and outputs an uppercase version of it.
shinyServer(function(input, output) {
output$uppercase <- reactiveText(function() {
output$uppercase <- renderText({
toupper(input$message)
})
})

View File

@@ -11,7 +11,7 @@
A table output element that can be included in a panel
}
\description{
Render a \link{reactiveTable} within an application page.
Render a \link{renderTable} within an application page.
}
\examples{
mainPanel(

View File

@@ -17,8 +17,7 @@
}
\details{
Text is HTML-escaped prior to rendering. This element is
often used to dispaly \link{reactiveText} output
variables.
often used to display \link{renderText} output variables.
}
\examples{
h3(textOutput("caption"))

View File

@@ -18,7 +18,7 @@
}
\details{
Text is HTML-escaped prior to rendering. This element is
often used with the \link{reactivePrint} function to
often used with the \link{renderPrint} function to
preserve fixed-width formatting of printed objects.
}
\examples{

View File

@@ -1,61 +1,61 @@
isolate({
# reactivePrint captures any print output, converts it to a string, and
# renderPrint captures any print output, converts it to a string, and
# returns it
visFun <- reactivePrint(function() "foo")
visFun <- renderPrint({ "foo" })
visFun()
# '[1] "foo"'
invisFun <- reactivePrint(function() invisible("foo"))
invisFun <- renderPrint({ invisible("foo") })
invisFun()
# ''
multiprintFun <- reactivePrint(function() {
multiprintFun <- renderPrint({
print("foo");
"bar"
})
multiprintFun()
# '[1] "foo"\n[1] "bar"'
nullFun <- reactivePrint(function() NULL)
nullFun <- renderPrint({ NULL })
nullFun()
# 'NULL'
invisNullFun <- reactivePrint(function() invisible(NULL))
invisNullFun <- renderPrint({ invisible(NULL) })
invisNullFun()
# ''
vecFun <- reactivePrint(function() 1:5)
vecFun <- renderPrint({ 1:5 })
vecFun()
# '[1] 1 2 3 4 5'
# Contrast with reactiveText, which takes the value returned from the function
# Contrast with renderText, which takes the value returned from the function
# and uses cat() to convert it to a string
visFun <- reactiveText(function() "foo")
visFun <- renderText({ "foo" })
visFun()
# 'foo'
invisFun <- reactiveText(function() invisible("foo"))
invisFun <- renderText({ invisible("foo") })
invisFun()
# 'foo'
multiprintFun <- reactiveText(function() {
multiprintFun <- renderText({
print("foo");
"bar"
})
multiprintFun()
# 'bar'
nullFun <- reactiveText(function() NULL)
nullFun <- renderText({ NULL })
nullFun()
# ''
invisNullFun <- reactiveText(function() invisible(NULL))
invisNullFun <- renderText({ invisible(NULL) })
invisNullFun()
# ''
vecFun <- reactiveText(function() 1:5)
vecFun <- renderText({ 1:5 })
vecFun()
# '1 2 3 4 5'