Force rstudio использовать браузер вместо просмотра

Рассмотрим любую функцию, которая (для rstudio) откроет что-то в средстве просмотра, если y = TRUE и в вашем браузере, если y = FALSE. Вы можете заставить whatever открываться в вашем браузере с помощью options(viewer = NULL) (а затем вам нужно reset раньше), но я не могу заставить это работать внутри функций, используя обычный подход on.exit. Протестировано на окнах и 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

Похоже, что зритель не уважает мои параметры внутри функциональной среды либо с каким-то html (g), либо с виджетами (f). Я думал, что оба будут использовать viewer = NULL внутри функции и возвращать мои параметры так, как они были при выходе, чтобы я мог контролировать, где я хочу просмотреть результат.

Или есть лучший способ сделать это как для html, так и для виджетов? Я пробовал аргумент options в DT::datatable безрезультатно, но это не помогло бы для случая htmlTable::htmlTable.

Единственный другой подход, о котором я могу думать, - написать весь код в временном файле и использовать if (rstudio) rstudio::viewer(tempfile) else browseURL(tempfile), который, по моему мнению, является большой работой для чего-то вроде бы такого прямолинейного.

Ответ 1

Хотя это не проблема, я думаю, что это иллюстрирует, что происходит. Попробуйте добавить вызов Sys.sleep() в обработчик on.exit():

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)

Вы заметите, что RStudio не "решает", что делать с результатом вызова DT::datatable() до тех пор, пока обработчик on.exit() не завершит выполнение. Это означает, что к тому моменту, когда RStudio хочет понять результат, зритель уже восстановлен! Скорее всего, RStudio ждет, пока R больше не "занят", чтобы решить, как отображать полученный контент, и к тому времени слишком поздно для временных изменений в опции viewer.

Обратите внимание, что это не объясняет поведение htmlTable. Мое лучшее предположение заключается в том, что происходит какое-то гоночное состояние; потерянный viewer вариант, похоже, уходит со стратегически расположенными вызовами Sys.sleep()...

К сожалению, работа вокруг этого означает, что вы избегаете использования вызова on.exit() - если мы не сможем понять это в RStudio, конечно.

Ответ 2

Здесь вы можете получить эту функциональность, написав код во временный файл и используя browseURL или все, что вам нравится.

Суть как f, так и g одинакова, поэтому вы можете иметь одну функцию для обработки любого типа html-кода или виджета, я полагаю. И, вероятно, виджеты должны быть 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) 
# ...

Обратите внимание, что на самом деле это правильный способ иметь htmlTable::htmlTable использование другого средства просмотра, но g должен работать для любого html.

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