我希望用户能够在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依赖性)?
答案 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内部修复,并且可能需要管理员权限和/或专业知识来修复):
easycsv::choose_dir
依赖于zenity,而zenity可能无法使用。rstudioapi::selectDirectory
要求我们的RStudio版本大于1.1.287。rChoiceDialogs::rchoose.dir
不仅要求安装java运行时环境,还必须正确安装和配置java编译器才能使用rJava。utils::menu
不起作用。同样在Linux X11上,它经常会在执行后打开一个孤立窗口,而这个窗口不能随时关闭。gWidgets2::gfile
对gtk2或tcltk或Qt具有外部依赖性。在某些情况下,发现解决这些依赖关系并非易事。 已归档此答案的早期版本
最后,这个答案的早期版本包含一些较长的代码,它们尝试了几种可能的解决方案来找到有效的解决方案。虽然我已经确定了上面的简单版本,但我将此版本保存在此处,以防它对其他人有用。
尝试的内容:
utils::choose.dir
是否存在(仅在Windows上可用)。如果是这样,请使用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: ')
)
}
答案 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())
这将返回目录。然而,它确实要求目录中至少存在一个文件