Force rstudio to use browser instead of viewer

2019-02-16 07:42发布

问题:

Consider either function which (for rstudio) will open something in the viewer if y = TRUE and in your browser if y = FALSE. You can force the whatever to open in your browser via options(viewer = NULL) (and then you need to reset to before), but I can't get this to work inside functions using the normal on.exit approach. Tested on windows and osx.

f <- function(x, y = TRUE) {
  if (y) {
    oo <- getOption('viewer')
    on.exit(options(viewer = oo))
    options(viewer = NULL)
  } else options(viewer = NULL)
  print(getOption('viewer'))
  DT::datatable(x)
}

g <- function(x, y = TRUE) {
  if (y) {
    oo <- getOption('viewer')
    on.exit(options(viewer = oo))
    options(viewer = NULL)
  } else options(viewer = NULL)
  print(getOption('viewer'))
  htmlTable::htmlTable(x)
}

## in rstudio, returns the viewer function
getOption('viewer')
# function (url, height = NULL) 
# ...

## opens in viewer despite `options(viewer = NULL)`
g(mtcars)
# NULL

## again returns the function, ie, reset my options to before g call successfully
getOption('viewer')
# function (url, height = NULL) 
# ...

## opens in browser but leaves `options(viewer = NULL)` after exiting
g(mtcars, FALSE)
# NULL

getOption('viewer')
# NULL

It seems like the viewer isn't respecting my options within the function environment with either just some html (g) or a widget (f). I thought both would use viewer = NULL inside the function and return my options the way they were upon exiting so that I can control where I want to view the result.

Or is there a better way of doing this for both html and widgets? I have tried the options argument in DT::datatable to no avail, but this wouldn't help for the htmlTable::htmlTable case.

The only other approach I can think of is to write all the code to a temp file and use if (rstudio) rstudio::viewer(tempfile) else browseURL(tempfile) which I think is a lot of work for something seemingly so straight-forward.

回答1:

Although this isn't a fix, I think it illustrates what's going on. Try adding a Sys.sleep() call in the on.exit() handler:

f <- function(x) {
  viewer <- getOption("viewer")
  on.exit({
    print("Restoring viewer...")
    Sys.sleep(3)
    options(viewer = viewer)
  }, add = TRUE)
  options(viewer = NULL)
  DT::datatable(x)
}

## opens in viewer despite `options(viewer = NULL)`
f(mtcars)

You'll notice that RStudio doesn't 'decide' what to do with the result of DT::datatable() call until after the on.exit() handler has finished execution. This means that, by the time RStudio wants to figure out to do with the result, the viewer has already been restored! Odds are, RStudio waits until R is no longer 'busy' to decide how to display the resulting content, and by then is too late for temporary changes to the viewer option.

Note that this doesn't explain the htmlTable behaviour. My best guess is that there is some kind of race condition going on; the lost viewer option seems to go away with strategically placed Sys.sleep() calls...

Unfortunately, working around this means avoiding the use of on.exit() call -- unless we can figure out to handle this in RStudio, of course.



回答2:

Here's one way you could get this functionality by writing the code to a temporary file and using browseURL or whatever you like.

The gist of both f and g are the same, so you could have one function to handle any type of html code or widget I suppose. And probably widgets need to be selfcontained = TRUE.

f <- function(x, y = TRUE) {
  x <- if ((inherits(x, 'iplot'))) x else DT::datatable(x)
  if (!y) {
    htmlFile <- tempfile(fileext = '.html')
    htmlwidgets::saveWidget(x, htmlFile, selfcontained = TRUE)
    utils::browseURL(htmlFile)
  } else x
}

g <- function(x, y = TRUE) {
  x <- htmlTable::htmlTable(x)
  if (!y) {
    htmlFile <- tempfile(fileext = '.html')
    writeLines(x, con = htmlFile)
    utils::browseURL(htmlFile)
  } else x
}

## opens in viewer
g(mtcars)
## opens in browser
g(mtcars, FALSE)

## same for widgets
f(mtcars)
f(mtcars, FALSE)

f(qtlcharts::iplot(1:5, 1:5), FALSE)

## and my options haven't changed
getOption('viewer')
# function (url, height = NULL) 
# ...

Side note that this is actually the proper way to have htmlTable::htmlTable use a different viewer, but g should work for any html.

library('htmlTable')
print(htmlTable(mtcars), useViewer = utils::browseURL)


标签: r rstudio