I would like users to be able to select a directory interactively in R. The solution needs to work on different platforms (at least on Linux, Windows and Mac machines that have a graphical desktop environment). And it needs to be robust enough to work on a variety of computers. I've run into problems with the variants I know of:
file.choose()
unfortunately only works for files - It won't allow to select a directory. Other than this limitation, file.choose
is a good example of the type of solution I'm looking for - it works across platforms and does not have external dependencies that may not be available on a particular computer.
choose.dir()
Only works on Windows.
tk_choose.dir()
from library(tcltk)
was my preferred solution until recently. But I've had users report that this throws an error
log4cplus:ERROR No appenders could be found for logger (AdSyncNamespace). log4cplus:ERROR Please initialize the log4cplus system properly.
which we tracked back to Autodesk360 software being installed, which for some reason interferes with tcltk
. So this is not a suitable solution unless there is a fix for this. (the only solution I've found by googling is to uninstall Autodesk360, which won't be a solution for users who installed it because they actually use it).
This answer suggests the following as a possible alternative:
library(rJava)
library(rChoiceDialogs)
jchoose.dir()
But, as an example of the sort of thing that can go wrong with this, when I tried to install.packages("rJava")
I got:
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
I managed to fix this on my own machine (linux running openJDK) by installing the openjdk compiler using the linux package manager then running sudo R CMD javareconf
. But I can't expect random users with varying levels of computer expertise to have to jump through hoops just so that they can select a directory. Even if they do manage to fix it, it will look bad when every other piece of software they use manages to open a directory-selection dialogue without any problems.
So my question: Is there a robust method that can reliably be expected to "just work" (like file.choose
does for files), on a variety of platforms and makes no expectation of the end user being computer literate enough to solve these kinds of issues (such as incompatabilities with Autodesk360 or unresolved Java dependencies)?
You can change the default working directory from RStudio menu under: Tools –> Global options –> click on “Browse” to select the default working directory you want.
In R, the function file. path() is used to fill in the directory separator.
In the time since posting this question and an earlier version of this answer, I've managed to test the various options that have been suggested on a range of computers. This process has converged on a fairly simple solution. The only cases I have found where tcltk::tk_choose.dir()
fails due to conflicts are on Windows computers running Autodesk software. But on Windows, we have utils::choose.dir
available instead. So the answer I am currently running with is:
choose_directory = function(caption = 'Select data directory') {
if (exists('utils::choose.dir')) {
choose.dir(caption = caption)
} else {
tk_choose.dir(caption = caption)
}
}
For completeness, I think it is useful to summarise some of the issues with other approaches and why they do not meet the criteria of being generally robust on a variety of platforms (including robustness against potentially unresolved external dependencies that can't be fixed from within R and that that may require administrator privileges and/or expertise to fix):
easycsv::choose_dir
in Linux depends on zenity, which may not be available.rstudioapi::selectDirectory
requires that we are in RStudio Version greater than 1.1.287.rChoiceDialogs::rchoose.dir
requires not only that java runtime environment is installed, but also java compiler must be installed and configured correctly to work with rJava.utils::menu
does not work if the R function is run from the command line, rather than in an interactive session. Also on Linux X11 it frequently leaves an orphan window open after execution, which can't be readily closed.gWidgets2::gfile
has external dependency on either gtk2 or tcltk or Qt. Resolving these dependencies was found to be non-trivial in some cases. Archived earlier version of this answer
Finally, an earlier version of this answer contained some longer code that tries out several possible solutions to find one that works. Although I have settled on the simple version above, I leave this version archived here in case it proves useful to someone else.
What it tries:
utils::choose.dir
exists (will only be available on Windows). If so, use thattcltk
package and then open and close a tcltk window without throwing an error. If so, use tcltk.gWidgets2
and the RGtk2
widgets. If so, use gWidgets2. I don't try to load the tcltk
widgets here, because if they worked, presumably we would already be using the tcltk
package. I also do not try to load the Qt
widgets, as they seem somewhat unmaintained and are not currently available on CRAN.rJava
and rChoiceDialogs
. If so, use rChoiceDialogs
.Here's the longer version of the code:
# 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: ')
)
}
Here is a simple directory navigation menu (using 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)
}
}
Note: I did not (yet) test it under different systems. Here is what the documentation says:
If graphics = TRUE and a windowing system is available (Windows, macOS or X11 via Tcl/Tk) a listbox widget is used, otherwise a text menu. It is an error to use menu in a non-interactive session.
One limitation: The title =
can only be a single line.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With