与操作系统无关的方式在R

时间:2018-01-12 01:56:20

标签: r

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

遗憾的是,

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

choose.dir()仅适用于Windows。

直到最近,来自tk_choose.dir()

library(tcltk)是我首选的解决方案。但我已经让用户报告这会引发错误

  

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

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

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

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

但是,作为一个可以解决这个问题的例子,当我尝试install.packages("rJava")时,我得到了:

  

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

     

确保已正确安装Java Development Kit   如果有疑问,请重新运行" R CMD javareconf"作为根。

     

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

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

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

5 个答案:

答案 0 :(得分:6)

自发布此问题及此答案的早期版本以来,我已成功测试了各种计算机上建议的各种选项。这个过程已经融合到一个相当简单的解决方案上。我发现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中的
  1. easycsv::choose_dir依赖于zenity,而zenity可能无法使用。
  2. rstudioapi::selectDirectory要求我们的RStudio版本大于1.1.287。
  3. rChoiceDialogs::rchoose.dir不仅要求安装java运行时环境,还必须正确安装和配置java编译器才能使用rJava。
  4. 如果R函数是从命令行运行而不是在交互式会话中运行,则
  5. utils::menu不起作用。同样在Linux X11上,它经常会在执行后打开一个孤立窗口,而这个窗口不能随时关闭。
  6. gWidgets2::gfile对gtk2或tcltk或Qt具有外部依赖性。在某些情况下,发现解决这些依赖关系并非易事。
  7. 已归档此答案的早期版本

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

    尝试的内容:

    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. 如果以上都不成功,请使用在控制台请求目录名称的后备位置。
    7. 这是代码的较长版本:

      # 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: ')
        )
      }
      

答案 1 :(得分:2)

这是一个简单的目录导航菜单(使用menu{utils}):

d=1
while(d != 0) {
  a = getwd()
  a = strsplit(a, "/")
  a = unlist(a)
  b = list.dirs(recursive = F, full.names = F)
  c = paste("..", a[length(a) - 1], a[length(a)], sep = "/")
  d = menu(c("..", b), title = c, graphics = T)
  if(d==1){
    e=paste(paste(a[1:(length(a)-1)],collapse = '/',sep = ''),'/',sep = '')
    #print(e)
    setwd(e)
  }else{
    e=paste(paste(a,collapse = '/',sep = ''),'/',b[d-1],sep='')
    #print(e)
    setwd(e)
  }
}

注意:我没有(还)在不同的系统下测试它。以下是文档说的内容:

  

如果graphics = TRUE且窗口系统可用(Windows,macOS或X11,通过Tcl / Tk),则使用列表框小部件,否则使用文本菜单。在非交互式会话中使用菜单是错误的。

一个限制:title =只能是一行。

答案 2 :(得分:2)

您可以使用 easycsv 中的choose_dir功能。
它适用于Windows,Linux和OSX

easycsv::choose_dir() # can be run without parameters to prompt a folder selection window

答案 3 :(得分:0)

我的评论(06.09.2018 RFelber)中提到的适应choose_directory()的建议:

choose_directory <- function(ini_dir = getwd(),
                             method = select_directory_method(), 
                             title = 'Select data directory') {
  switch(method,
          'choose.dir' = choose.dir(default = ini_dir, caption = title),
          'RStudioAPI' = selectDirectory(path = ini_dir, caption = title),
          'tcltk' = tk_choose.dir(default = ini_dir, caption = title),
          'rChoiceDialogs' = rchoose.dir(default = ini_dir, caption = title),
          'gWidgets2RGtk2' = gfile(type = 'selectdir', text = title, initial.dir = ini_dir),
          readline('Please enter directory path: ')
  )
}

答案 4 :(得分:0)

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

dir <- dirname(file.choose())

这将返回目录。然而,它确实要求目录中至少存在一个文件