Comments (9)
Thanks, I agree with your analysis. So it's really user error in the document: I shouldn't expect dev.off()
to return me to the previous state. I should do something like
prev <- dev.prev()
dev.off()
dev.set(prev)
and indeed when I do that, things are fine. (I don't know why dev.off()
doesn't already work that way. But I expect it's been this way for a very long time.)
from knitr.
Maybe there's a way for knitr
to make that plot invisible to evaluate
at the start of the run? I think the plot is being captured here:
https://github.com/r-lib/evaluate/blob/00192335c338ac83af200dcee35c56d807b8b28b/R/eval.R#L165-L175
from knitr.
TL;DR;
This happens because dev.off()
is called in a chunk, and it will set dev.cur()
to the next device, which happens to be the one opened before knitting. evaluate will then capture plot in this device, which includes the existing one.
Not sure how to prevent this, without a dev.off()
for any existing device before knitting.
Details about investigations
Definitely something at the evaluate level
> plot(1, main = "Not in the document")
> res = evaluate::evaluate("dev.new()\ndev.off()\nplot.new()\nplot(1:10, 1:10)", new_device = FALSE)
> str(res, max.level = 2)
List of 9
$ :List of 1
..$ src: chr "dev.new()\n"
..- attr(*, "class")= chr "source"
$ : chr "NULL\n"
$ :List of 1
..$ src: chr "dev.off()\n"
..- attr(*, "class")= chr "source"
$ : chr "RStudioGD \n 2 \n"
$ :List of 1
..$ src: chr "plot.new()\n"
..- attr(*, "class")= chr "source"
$ :List of 2
..$ :Dotted pair list of 8
..$ : raw [1:35992] 01 00 00 00 ...
.. ..- attr(*, "pkgName")= chr "graphics"
..- attr(*, "engineVersion")= int 16
..- attr(*, "pid")= int 53184
..- attr(*, "Rversion")=Classes 'R_system_version', 'package_version', 'numeric_version' hidden list of 1
..- attr(*, "load")= chr(0)
..- attr(*, "attach")= chr(0)
..- attr(*, "class")= chr "recordedplot"
$ :List of 2
..$ :Dotted pair list of 2
..$ : raw [1:35992] 01 00 00 00 ...
.. ..- attr(*, "pkgName")= chr "graphics"
..- attr(*, "engineVersion")= int 16
..- attr(*, "pid")= int 53184
..- attr(*, "Rversion")=Classes 'R_system_version', 'package_version', 'numeric_version' hidden list of 1
..- attr(*, "load")= chr(0)
..- attr(*, "attach")= chr(0)
..- attr(*, "class")= chr "recordedplot"
$ :List of 1
..$ src: chr "plot(1:10, 1:10)"
..- attr(*, "class")= chr "source"
$ :List of 2
..$ :Dotted pair list of 8
..$ : raw [1:35992] 01 00 00 00 ...
.. ..- attr(*, "pkgName")= chr "graphics"
..- attr(*, "engineVersion")= int 16
..- attr(*, "pid")= int 53184
..- attr(*, "Rversion")=Classes 'R_system_version', 'package_version', 'numeric_version' hidden list of 1
..- attr(*, "load")= chr(0)
..- attr(*, "attach")= chr(0)
..- attr(*, "class")= chr "recordedplot"
It could even be simplified to this
> plot(1, main = "Not in the document")
> res = evaluate::evaluate("# hello", new_device = FALSE)
> str(res, max.level = 2)
List of 2
$ :List of 1
..$ src: chr "# hello"
..- attr(*, "class")= chr "source"
$ :List of 2
..$ :Dotted pair list of 8
..$ : raw [1:35992] 01 00 00 00 ...
.. ..- attr(*, "pkgName")= chr "graphics"
..- attr(*, "engineVersion")= int 16
..- attr(*, "pid")= int 43180
..- attr(*, "Rversion")=Classes 'R_system_version', 'package_version', 'numeric_version' hidden list of 1
..- attr(*, "load")= chr(0)
..- attr(*, "attach")= chr(0)
..- attr(*, "class")= chr "recordedplot"
it does not come from the hooks IMO but from handle output: https://github.com/r-lib/evaluate/blob/00192335c338ac83af200dcee35c56d807b8b28b/R/eval.R#L151-L158
This handle_output()
is also called on last item (https://github.com/r-lib/evaluate/blob/00192335c338ac83af200dcee35c56d807b8b28b/R/eval.R#L264-L267). evaluate will always capture the content of the graphic device when evaluating the last element. I believe this is expected feature as doc says:
It stores the final result, whether or not it should be visible, and the contents of the current graphics device.
I believe this is what happens:
plot()
in console opens a new device- knitr does open another new device per chunk (unless
global.device
is set, but this is FALSE by default) and set it current. - Then it will evaluate the chunk content using evaluate
- evaluate will parse each line of code, and evaluate each on its own, each time registering the
dev.cur()
device, and capturing its content if it has not changed (source here)
So we can simulate with
# no device at start
dev.list()
#> NULL
# creating a plot in console opens the device
plot(1, main = "Not in the document")
dev.list()
#> RStudioGD png
#> 2 3
dev.cur()
#> RStudioGD
#> 2
# knitr save the opened device
dv0 = dev.cur(); dv0
#> RStudioGD
#> 2
# and creates a new one
grDevices::png()
dv = dev.cur(); dv
#> png
#> 4
# then code is evaluated.
dev.new() # creating a new device
dev.cur() # this modifies the dev cur
#> windows
#> 5
dev.off() # calling from the chunk
#> RStudioGD
#> 2
dev.cur()
#> RStudioGD
#> 2
And we see here that after dev.off()
, the current device has been reset to the one from the created plot before knitting.
And so evaluate will capture the plot already in that device, skipping the device opened by knitr.
dev.off()
shutdown active device, and setup the current to be the next, where here we would have like dev.prev()
instead. From ?dev.off()
:
dev.off shuts down the specified (by default the current) device. If the current device is shut down and any other devices are open, the next open device is made current.
Issue here is that, calling dev.off()
inside a knitr chunk will set the device to the next one, and if one has been opened before knitting happens in will be that one.
I am not sure how we can prevent this for happening exactly, without closing any existing device before rendering. 🤔
from knitr.
Thanks for the report. I can obviously reproduce.
This remind me of something similar we've dealt with for child documents already
This was fixed by ff3d767 and amended by 58fc726
So it is probably related to same part of the code
Lines 205 to 216 in 42f6b3a
but it seems to be different here... I'll look into that.
from knitr.
Interestingly, more minimal reprex of this
---
title: "Untitled"
output:
html_document:
keep_md: true
---
```{r}
dev.new()
dev.off()
# hello
```
So I am thinking something in evaluate rather than knitr directly. This is what we results in evaluate::evaluate()
when debugging interactively
> str(res, max.level = 2)
List of 6
$ :List of 1
..$ src: chr "dev.new()\n"
..- attr(*, "class")= chr "source"
$ : chr "debug: options$message\n"
$ :List of 1
..$ src: chr "dev.off()\n"
..- attr(*, "class")= chr "source"
$ : chr "RStudioGD \n 2 \n"
$ :List of 1
..$ src: chr "# hello"
..- attr(*, "class")= chr "source"
$ :List of 2
..$ :Dotted pair list of 8
..$ : raw [1:35992] 01 00 00 00 ...
.. ..- attr(*, "pkgName")= chr "graphics"
..- attr(*, "engineVersion")= int 16
..- attr(*, "pid")= int 51232
..- attr(*, "Rversion")=Classes 'R_system_version', 'package_version', 'numeric_version' hidden list of 1
..- attr(*, "load")= chr(0)
..- attr(*, "attach")= chr(0)
..- attr(*, "class")= chr "recordedplot"
So it seems that something evaluated after dev.off
will trigger the recordedplot to be among value returned by evaluate::evaluate()
from knitr.
Thanks ! I was just looking into those hooks. It seems indeed directly related.
from knitr.
Thanks @dmurdoch for the report and @cderv for the investigation! I just pushed a fix in the evaluate package, so it should be safe to use dev.off()
in knitr code chunks now.
from knitr.
Oh good solution @yihui ! Thank you !
from knitr.
This old thread has been automatically locked. If you think you have found something related to this, please open a new issue by following the issue guide (https://yihui.org/issue/), and link to this old issue if necessary.
from knitr.
Related Issues (20)
- knitr cache does not recognize magrittr assignment pipe HOT 2
- WISH: Make it possible to call globalCallingHandlers() in a chunk
- Animation not rendering in powerpoint HOT 1
- Error(s) in re-building vignettes: HOT 2
- table output for typst HOT 8
- html/css rendering to PDF HOT 3
- export some pandoc syntax helper HOT 2
- Make `asis_output` works for Quarto the same as `results: asis`
- output hooks and current chunk options
- write_bib producing NULL in knitr 1.45 HOT 5
- feature request: tweak non-escaped ampersand HOT 7
- fig.ncol not working with Latex HOT 2
- `* 2.` does not produce <li>2.</li> HOT 1
- catch error=TRUE chunks produced by hook_purl() HOT 5
- Problem caching instances of torch modules and datasets
- write_bib() includes multiple URLs HOT 3
- autodep regression by #2321 HOT 4
- Issue with code highlighting (RStudio/Sweave/knitr/tinytex) HOT 7
- collapse is not working since 1.46 HOT 1
- hook_purl() writes filename to the md file HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from knitr.