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

dww*_*dww 16 r

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

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

choose.dir() 仅适用于Windows.

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

log4cplus:ERROR找不到记录器(AdSyncNamespace)的appender.log4cplus:ERROR请正确初始化log4cplus系统.

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

这个答案表明以下可能的替代方案:

library(rJava)
library(rChoiceDialogs)
jchoose.dir()  
Run Code Online (Sandbox Code Playgroud)

但是,作为一个可能出错的事情的一个例子,当我试图install.packages("rJava")得到时:

检查是否可以编译JNI程序... configure:error:无法编译简单的JNI程序.有关详细信息,请参阅config.log.

确保安装了Java Development Kit并在R中正确注册.如果有疑问,请以root身份重新运行"R CMD javareconf".

错误:包'rJava'的配置失败*删除'/home/dominic/R/x86_64-pc-linux-gnu-library/3.3/rJava'install.packages中的警告:包'rJava'的安装具有非零退出状态

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

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

dww*_*dww 10

自发布此问题和该答案的早期版本以来,我已经设法测试了一系列计算机上建议的各种选项。这个过程已经集中在一个相当简单的解决方案上。我发现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)
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

这个答案的存档早期版本

最后,此答案的较早版本包含一些较长的代码,这些代码尝试了几种可能的解决方案以找到可行的解决方案。尽管我已经确定了上面的简单版本,但是如果对其他人有用,则将其保留在此处存档。

它尝试什么:

  1. 检查功能是否utils::choose.dir存在(仅在Windows上可用)。如果是这样,请使用
  2. 检查用户是否正在RStudio 1.1.287或更高版本中工作。如果是使用RStudio API。
  3. 检查我们是否可以加载tcltk程序包,然后打开和关闭tcltk窗口而不会引发错误。如果是这样,使用tcltk。
  4. 检查我们是否可以加载gWidgets2RGtk2部件。如果是这样,请使用gWidgets2。我不尝试在tcltk此处加载这些小部件,因为如果它们可以工作,大概我们已经在使用该tcltk软件包了。我也不尝试加载这些Qt小部件,因为它们似乎没有维护,并且目前在CRAN上不可用。
  5. 检查是否可以加载rJavarChoiceDialogs。如果是这样,请使用rChoiceDialogs
  6. 如果以上都不成功,请使用备用位置在控制台上请求目录名称。

下面的代码的更长的版本:

# 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: ')
  )
}
Run Code Online (Sandbox Code Playgroud)


Lod*_*Lod 5

对于某些用例,一个小技巧可能是使用dirname()aroundfile.choose()

目录 <- 目录名(file.choose())

这将返回目录。但是,它确实需要目录中至少存在一个文件