与操作系统无关的方式在R中以交互方式选择目录

我希望用户能够在R中以交互方式选择目录.解决方案需要在不同的平台上工作(至少在具有图形桌面环境的 Linux,Windows和Mac机器上).它需要足够强大,才能在各种计算机上运行.我遇到了我所知道的变种的问题:

不幸的是,file.choose()仅适用于文件 – 它不允许选择目录.除了这个限制之外,file.choose是我正在寻找的解决方案类型的一个很好的例子 – 它可以跨平台工作,并且没有可能在特定计算机上不可用的外部依赖性.

choose.dir()仅适用于Windows.

直到最近,来自库(tcltk)的tk_choose.dir()才是我首选的解决方案.但我有用户报告这会引发错误

log4cplus:ERROR No appenders could be found for logger (AdSyncNamespace).
log4cplus:ERROR Please initialize the log4cplus system properly.

我们追溯到安装的Autodesk360软件,由于某种原因干扰了tcltk.所以这不是一个合适的解决方案,除非有一个解决方案. (我通过Google搜索找到的唯一解决方案是卸载Autodesk360,这对于安装它的用户来说不是解决方案,因为他们实际使用它).

This answer建议以下作为可能的替代方案:

library(rJava)
library(rChoiceDialogs)
jchoose.dir()

但是,作为一个可能出现问题的例子,当我尝试安装.packages(“rJava”)时,我得到了:

checking whether JNI programs can be compiled… configure: error:
Cannot compile a simple JNI program. See config.log for details.

Make sure you have Java Development Kit installed and correctly
registered in R. If in doubt, re-run “R CMD javareconf” as root.

ERROR: configuration failed for package ‘rJava’
* removing ‘/home/dominic/R/x86_64-pc-linux-gnu-library/3.3/rJava’ Warning in install.packages : installation of package ‘rJava’ had
non-zero exit status

我设法通过使用linux包管理器安装openjdk编译器然后运行sudo R CMD javareconf来修复我自己的机器(linux运行openJDK).但我不能指望具有不同计算机专业水平的随机用户必须跳过篮球才能选择目录.即使他们确实设法修复它,当他们使用的每个其他软件都设置为没有任何问题的情况下打开目录选择对话时,它看起来会很糟糕.

所以我的问题是:是否存在一种可靠的方法,可以在各种平台上可靠地预期“正常工作”(如file.choose对文件),并且不期望最终用户具有足够的计算机知识来解决这些问题问题(例如与Autodesk360不兼容或未解决的Java依赖项)?

在发布此问题和此答案的早期版本之后的时间里,我已经设法测试了在一系列计算机上建议的各种选项.这个过程已经融合到一个相当简单的解决方案上.我发现tcltk :: tk_choose.dir()因冲突而失败的唯一情况是在运行Autodesk软件的Windows计算机上.但在Windows上,我们可以使用utils :: choose.dir.所以我目前正在运行的答案是:

choose_directory = function(caption = 'Select data directory') {
  if (exists('utils::choose.dir')) {
    choose.dir(caption = caption) 
  } else {
    tk_choose.dir(caption = caption)
  }
}

为了完整起见,我认为总结一些其他方法的问题以及为什么它们不符合在各种平台上通常可靠的标准(包括对可能无法解决的潜在未解决的外部依赖性的鲁棒性)是有用的. R和那些可能需要管理员权限和/或专业知识才能解决的问题):

> Linux中的easycsv :: choose_dir取决于zenity,这可能不可用.
> rstudioapi :: selectDirectory要求我们在RStudio版本中大于1.1.287.
> rChoiceDialogs :: rchoose.dir不仅要求安装java运行时环境,还必须正确安装和配置java编译器才能使用rJava.
如果从命令行运行R函数,而不是在交互式会话中运行,则> utils :: menu不起作用.同样在Linux X11上,它经常在执行后打开一个孤立窗口,这个窗口不能轻易关闭.
> gWidgets2 :: gfile对gtk2或tcltk或Qt具有外部依赖性.在某些情况下,发现解决这些依赖关系并非易事.

归档此答案的早期版本

最后,这个答案的早期版本包含一些较长的代码,它们尝试了几种可能的解决方案来找到有效的解决方案.虽然我已经确定了上面的简单版本,但我将此版本保存在此处,以防它对其他人有用.

尝试的内容:

>检查函数utils :: choose.dir是否存在(仅在Windows上可用).如果是这样,请使用它
>检查用户是否在RStudio 1.1.287或更高版本中工作.如果是这样,请使用RStudio API.
>检查我们是否可以加载tcltk包,然后打开和关闭tcltk窗口而不会抛出错误.如果是这样,请使用tcltk.
>检查我们是否可以加载gWidgets2和RGtk2小部件.如果是这样,请使用gWidgets2.我不试图在这里加载tcltk小部件,因为如果它们工作,大概我们已经在使用tcltk包了.我也不会尝试加载Qt小部件,因为它们似乎有点不受维护,目前在CRAN上不可用.
>检查我们是否可以加载rJava和rChoiceDialogs.如果是这样,请使用rChoiceDialogs.
>如果以上都不成功,请使用在控制台请求目录名称的后备位置.

这是代码的较长版本:

# First a helper function to load packages, installing them first if necessary
# Returns logical value for whether successful
ensure_library = function (lib.name){
    x = require(lib.name, quietly = TRUE, character.only = TRUE)
    if (!x) {
      install.packages(lib.name, dependencies = TRUE, quiet = TRUE)
      x = require(lib.name, quietly = TRUE, character.only = TRUE)
      }
  x
}

select_directory_method = function() {
  # Tries out a sequence of potential methods for selecting a directory to find one that works 
  # The fallback default method if nothing else works is to get user input from the console
  if (!exists('.dir.method')){  # if we already established the best method, just use that
    # otherwise lets try out some options to find the best one that works here
    if (exists('utils::choose.dir')) {
      .dir.method = 'choose.dir'
    } else if (rstudioapi::isAvailable() & rstudioapi::getVersion() > '1.1.287') {
      .dir.method = 'RStudioAPI'
      ensure_library('rstudioapi')
    } else if(ensure_library('tcltk') & 
              class(try({tt  <- tktoplevel(); tkdestroy(tt)}, silent = TRUE)) != "try-error") {
      .dir.method = 'tcltk'
    } else if (ensure_library('gWidgets2') & ensure_library('RGtk2')) {
      .dir.method = 'gWidgets2RGtk2'
    } else if (ensure_library('rJava') & ensure_library('rChoiceDialogs')) {
      .dir.method = 'rChoiceDialogs'
    } else {
      .dir.method = 'console'
    }
    assign('.dir.method', .dir.method, envir = .GlobalEnv) # remember the chosen method for later
  }
  return(.dir.method)
}

choose_directory = function(method = select_directory_method(), title = 'Select data directory') {
  switch (method,
          'choose.dir' = choose.dir(caption = title),
          'RStudioAPI' = selectDirectory(caption = title),
          'tcltk' = tk_choose.dir(caption = title),
          'rChoiceDialogs' = rchoose.dir(caption = title),
          'gWidgets2RGtk2' = gfile(type = 'selectdir', text = title),
          readline('Please enter directory path: ')
  )
}
相关文章
相关标签/搜索