mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-29 16:58:11 -05:00
Add sending images page
This commit is contained in:
165
_includes/tutorial/sending-images.md
Normal file
165
_includes/tutorial/sending-images.md
Normal file
@@ -0,0 +1,165 @@
|
||||
## Sending Images
|
||||
|
||||
When you want to have R generate a plot and send it to the client browser, the `renderPlot()` function will in most cases do the job. But when you need finer control over the process, you might need to use the `renderImage()` function instead.
|
||||
|
||||
### About renderPlot()
|
||||
|
||||
`renderPlot()` is useful for any time where R generates an image using its normal graphical device system. In other words, any plot-generating code that would normally go between `png()` and `dev.off()` can be used in `renderPlot()`. If the following code works from the console, then it should work in `renderPlot()`:
|
||||
|
||||
{% highlight r %}
|
||||
png()
|
||||
# Your plotting code here
|
||||
dev.off()
|
||||
{% endhighlight %}
|
||||
|
||||
{% highlight r %}
|
||||
# This would go in shinyServer()
|
||||
output$myPlot <- renderPlot({
|
||||
# Your plotting code here
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
`renderPlot()` takes care of a number of details automatically: it will resize the image to fit the output window, and it will even increase the resolution of the output image when displaying on high-resolution ("Retina") screens.
|
||||
|
||||
The limitation to `renderPlot()` is that it won't send just any image file to the browser -- the image must be generated by code that uses R's graphical output device system. Other methods of creating images can't be sent by `renderPlot()`. For example, the following won't work:
|
||||
|
||||
* Image files generated by the `writePNG()` function from the png package.
|
||||
* Image files generated by the `rgl.snapshot()` function, which creates images from 3D plots made with the rgl package.
|
||||
* Images generated by an external program.
|
||||
* Pre-rendered images.
|
||||
|
||||
The solution in these cases is the `renderImage()` function.
|
||||
|
||||
|
||||
### Using renderImage()
|
||||
|
||||
Image files can be sent using `renderImage()`. The expression that you pass to `renderImage()` must return a list containing an element named `src`, which is the path to the file. Here is a very basic example of a Shiny app with an output that generates a plot and sends it with `renderImage()`:
|
||||
|
||||
#### server.R
|
||||
|
||||
{% highlight r %}
|
||||
shinyServer(function(input, output, clientData) {
|
||||
output$myImage <- renderImage({
|
||||
# A temp file to save the output.
|
||||
# This file will be removed later by renderImage
|
||||
outfile <- tempfile(fileext='.png')
|
||||
|
||||
# Generate the PNG
|
||||
png(outfile, width=400, height=300)
|
||||
hist(rnorm(input$obs), main="Generated in renderImage()")
|
||||
dev.off()
|
||||
|
||||
# Return a list containing the filename
|
||||
list(src = outfile,
|
||||
contentType = 'image/png',
|
||||
width = 400,
|
||||
height = 300,
|
||||
alt = "This is alternate text")
|
||||
}, deleteFile = TRUE)
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
Each time this output object is re-executed, it creates a new PNG file, saves a plot to it, then returns a list containing the filename along with some other values.
|
||||
|
||||
Because the `deleteFile` argument is `TRUE`, Shiny will delete the file (specified by the `src` element) after it sends the data. This is appropriate for a case like this, where the image is created on-the-fly, but it wouldn't be appropriate when, for example, your app sends pre-rendered images.
|
||||
|
||||
In this particular case, the image file is created with the `png()` function. But it just as well could have been created with `writePNG()` from the png package, or by any other method. If you have the filename of the image, you can send it with `renderImage()`.
|
||||
|
||||
#### Structure of the returned list
|
||||
|
||||
The list returned in the example above contains the following:
|
||||
|
||||
* `src`: The output file path.
|
||||
* `contentType`: The MIME type of the file. If this is missing, Shiny will try to autodetect the MIME type, from the file extension.
|
||||
* `width` and `height`: The desired output size, in pixels.
|
||||
* `alt`: Alternate text for the image.
|
||||
|
||||
Except for `src` and `contentType`, all values are passed through directly to the `<img>` DOM element on the web page. The effect is similar to having an image tag with the following:
|
||||
|
||||
{% highlight xml %}
|
||||
<img src="..." width="400" height="300" alt="This is alternate text">
|
||||
{% endhighlight %}
|
||||
|
||||
Note that the `src="..."` is shorthand for a longer URL. For browsers that support the [data URI scheme](http://en.wikipedia.org/wiki/Data_URI_scheme), the `src` and `contentType` from the returned list are put together to create a special URL that embeds the data, so the result would be similar to something like this:
|
||||
|
||||
{% highlight xml %}
|
||||
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAm0AAAGnCAYAAADlkGDxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAgAElEQVR4nOydd3ic1ZX/P2+ZKmlU"
|
||||
width="400" height="300" alt="This is alternate text">
|
||||
{% endhighlight %}
|
||||
|
||||
For browsers that don't support the data URI scheme, Shiny sends a URL that points to the file.
|
||||
|
||||
|
||||
### Sending pre-rendered images with renderImage()
|
||||
|
||||
If your Shiny app has pre-rendered images saved in a subdirectory, you can send them using `renderImage()`. Suppose the images are in the subdirectory `images/`, and are named `image1.jpeg`, `image2.jpeg`, and so on. The following code would send the appropriate image, depending on the value of `input$n`:
|
||||
|
||||
#### server.R
|
||||
|
||||
{% highlight r %}
|
||||
shinyServer(function(input, output, clientData) {
|
||||
# Send a pre-rendered image, and don't delete the image after sending it
|
||||
output$preImage <- renderImage({
|
||||
# When input$n is 3, filename is ./images/image3.jpeg
|
||||
filename <- normalizePath(file.path('./images',
|
||||
paste('image', input$n, '.jpeg', sep='')))
|
||||
|
||||
# Return a list containing the filename and alt text
|
||||
list(src = filename,
|
||||
alt = paste("Image number", input$n))
|
||||
|
||||
}, deleteFile = FALSE)
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
In this example, `deleteFile` is `FALSE` because the images aren't ephemeral; we don't want Shiny to delete an image after sending it.
|
||||
|
||||
Note that this might be less efficient than putting images in `www/images` and emitting HTML that points to the images, because in the latter case the image will be cached by the browser.
|
||||
|
||||
|
||||
### Using clientData values
|
||||
|
||||
In the first example above, the plot size was fixed at 400 by 300 pixels. For dynamic resizing, it's possible to use values from `clientData` to detect the output size.
|
||||
|
||||
In the example below, the output object is `output$myImage`, and the width and height on the client browser are sent via `clientData$output_myImage_width` and `clientData$output_myImage_height`. This example also uses `clientData$pixelratio` to multiply the resolution of the image, so that it appears sharp on high-resolution (Retina) displays:
|
||||
|
||||
#### server.R
|
||||
|
||||
{% highlight r %}
|
||||
shinyServer(function(input, output, clientData) {
|
||||
|
||||
# A dynamically-sized plot
|
||||
output$myImage <- renderImage({
|
||||
# Read myImage's width and height. These are reactive values, so this
|
||||
# expression will re-run whenever they change.
|
||||
width <- clientData$output_myImage_width
|
||||
height <- clientData$output_myImage_height
|
||||
|
||||
# For high-res displays, this will be greater than 1
|
||||
pixelratio <- clientData$pixelratio
|
||||
|
||||
# A temp file to save the output.
|
||||
outfile <- tempfile(fileext='.png')
|
||||
|
||||
# Generate the image file
|
||||
png(outfile, width=width*pixelratio, height=height*pixelratio,
|
||||
res=72*pixelratio)
|
||||
hist(rnorm(input$obs))
|
||||
dev.off()
|
||||
|
||||
# Return a list containing the filename
|
||||
list(src = outfile,
|
||||
width = width,
|
||||
height = height,
|
||||
alt = "This is alternate text")
|
||||
}, deleteFile = TRUE)
|
||||
|
||||
# This code reimplements many of the features of `renderPlot()`.
|
||||
# The effect of this code is very similar to:
|
||||
# renderPlot({
|
||||
# hist(rnorm(input$obs))
|
||||
# })
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
The `width` and `height` values passed to `png()` specify the pixel dimensions of the saved image. These can differ from the `width` and `height` values in the returned list: those values are the pixel dimensions to used display the image. For high-res displays (where `pixelratio` is 2), a "virtual" pixel in the browser might correspond to 2 x 2 physical pixels, and a double-resolution image will make use of each of the physical pixels.
|
||||
@@ -184,6 +184,9 @@ hljs.initHighlightingOnLoad();
|
||||
<li>
|
||||
<a target="_self" href="#client-data">Client Data</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target="_self" href="#sending-images">Sending Images</a>
|
||||
</li>
|
||||
<li class="nav-header">Understanding Reactivity</li>
|
||||
<li>
|
||||
<a target="_self" href="#reactivity-overview">Reactivity Overview</a>
|
||||
@@ -345,6 +348,14 @@ hljs.initHighlightingOnLoad();
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Sending Images -->
|
||||
<div class="tab-pane" id="sending-images">
|
||||
|
||||
{% capture sending_images %}{% include tutorial/sending-images.md %}{% endcapture %}
|
||||
{{ sending_images | markdownify }}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Reactivity Overview -->
|
||||
<div class="tab-pane" id="reactivity-overview">
|
||||
|
||||
|
||||
Reference in New Issue
Block a user