Reputation: 535
I'm using Rmarkdown in RStudio and including plots that are opened in new graphics windows. If the window is opened directly in a code chunk, then the plot is included in the processed document. But if the window is opened by a separate script that is sourced, then the plot appears during Knitr processing but is not included in the document. Here is a complete minimal example of an .Rmd script to demonstrate:
---
title: "Rmarkdown graph inclusion"
---
# Make a simple plot:
```{r}
plot(0,0,main="Attempt 1")
```
Result of above: Plot is displayed during processing and is included in generated document.
# Make the plot in a separate graphics window:
```{r}
windows() # open new graphics window; x11() on Linux, MacOS
plot(0,0,main="Attempt 2")
```
Result of above: Plot is displayed during processing and is included in generated document.
# Make the plot in a separate graphics window called from another script:
```{r}
writeLines( "windows() ; plot(0,0,main='From File')" ,
con="openWindowScript.R" )
source("openWindowScript.R")
```
Result of above: Plot **is** displayed during Knitr processing but is **NOT** included in the generated document. *Why not?*
I did search stackoverflow and elsewhere for an answer but didn't find one. Thanks in advance for answers or pointers!
Upvotes: 4
Views: 3227
Reputation: 14957
The code suffers from two different issues. One is the code in openWindowScript.R
, the other is how this file is included into the main document.
The code in openWindowScript.R
does not even produce a visible plot when used directly as chunk code:
```{r}
windows(); plot(0,0,main='From File')
```
I think this is due to the difference between top-level expressions and other code (mentioned for example here), but I'm not sure about the details. What does produce a visible plot is the following code:
```{r}
windows()
plot(0,0,main='From File')
```
So with this code in an external file, how could we include it in the document? Not by source
-ing the external file, presumably because then the plotting command again is not a top-level expression. Although Mark Peterson already provided a nice workaround, I would like to suggest a more knitr
-idiomatic solution: Inject the code into a chunk, using the code
option:
```{r}
writeLines( "windows() \n plot(0,0,main='From File')" ,
con="openWindowScript.R" )
```
```{r, code = readLines("openWindowScript.R")}
```
Upvotes: 1
Reputation: 9570
If you add dev.print()
after the source
call, it should print the current device (which should, in your case) be the one called by source. This will then be captured by knit
and included in the document. So, the chunk should look like this:
```{r}
writeLines( "windows() ; plot(0,0,main='From File')" ,
con="openWindowScript.R" )
source("openWindowScript.R")
dev.print()
```
I tested this on Linux, which uses X11 for both opening a device and printing it, but the documentation appears to imply that it should work the same on Windows (as long as the Windows specific version of dev.print
is correctly installed, which it should be by default).
If the dev.print
is causing issues when run interactively (either just a nuisance or causing a crash), you can block it from running outside of a knit document by checking the name of the file being knit. This returns NULL
when run interactively, so can be used as a condition in if
to block execution outside of knitting.
Using the example from the comment about this error, the code chunk becomes:
```{r echo=1:2}
writeLines( "windows() ; plot(0,0,main='Ta-Da!')" ,
con="theScript.R" )
source("theScript.R")
if(!is.null(knitr::current_input())){
deviceInfo <- dev.print()
}
```
A separate approach is to over-write the behavior of windows()
(and/or x11
). At the top of your Rmd document, add
x11 <- windows <- function(...){invisible(NULL)}
Which should catch all of the calls to windows
or x11
and essentially ignore them (the ...
ensures that you shouldn't get "unused argument" errors). If you are using those calls to set size and/or aspect ratio, you would need to use fig.width
or fig.height
instead. This may break other things where you actually want the behavior of x11/windows, but using grDevices::x11
(or similar for windows
) would get you the right function. So, if you are in a pinch, and willing to forgo the reason you used windows
in the first place, this should work.
Upvotes: 2