R Windows Os Choose.Dir() File Chooser Won't Open at Working Directory

R Windows OS choose.dir() File chooser won't open at working directory

You are right in that you should not use choose.dir(), as it is OS-specific. I can indeed reproduce the issue you report - my guess is that it won't let you start in a directory that belongs to the "Root" user (whatever that may mean in Windows), because it seems to work well for other directories, not under 'Root':

 getwd()
# [1] "C:/Users/Root/Documents"
choose.dir(getwd(), "Choose a suitable folder") # leads to 'Computer'

setwd("C:/datathon")
choose.dir(getwd(), "Choose a suitable folder") # select subfolder 'scripts', works OK
# [1] "C:\\datathon\\scripts"

There are two OS-independent solutions; the first, as it has been pointed out before, is to use the following functionality from the tcltk package:

 library(tcltk)
setwd('~')
getwd()
# [1] "C:/Users/Root/Documents"
dir <- tclvalue(tkchooseDirectory()) # opens a dialog window in 'My Documents'

The second is to use the rChoiceDialogs package (requires rJava):

 library(rJava)
library(rChoiceDialogs)
getwd()
# [1] "C:/Users/Root/Documents"
jchoose.dir() # opens the dialog window in "C:\\Users\\Root\\Documents"

OS-independent way to select directory interactively in R

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):

  1. easycsv::choose_dir in Linux depends on zenity, which may not be available.
  2. rstudioapi::selectDirectory requires that we are in RStudio Version greater than 1.1.287.
  3. 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.
  4. 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.
  5. 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:

  1. Check whether the function utils::choose.dir exists (will only be available on Windows). If so, use that
  2. Check whether the user is working from within RStudio version 1.1.287 or greater. If so use the RStudio API.
  3. Check if we can load the tcltk package and then open and close a tcltk window without throwing an error. If so, use tcltk.
  4. Check whether we can load 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.
  5. Check if we can load rJava and rChoiceDialogs. If so, use rChoiceDialogs.
  6. If none of the above are successful, use a fallback position of requesting the directory name at the console.

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

tcltk Dialog Boxes Appear Underneath RStudio/Shiny Windows

So, I don't think there's a direct solution to this unfortunately ...

One option is to raise a toplevel window and then the directory dialog on top of it (you have to run everything at once here, otherwise the root is in the background again).

library(tcltk2)

root = tktoplevel("width" = 1, "height" = 1)
tkraise(root)
tkchooseDirectory("-parent", root)

Another option would be to use gWidgets.

dir_ <- gWidgets::gfile(type = "selectdir")

File browser in R

The file.choose function performs this, eg:

fname <- file.choose()

source(file.choose())

You may also want to look at choose.files (for multiple files) and choose.dir (for just selecting a directory path).

Folder Picker .NET MAUI

I've made a start implementing this for Windows and macOS. You can review the code here: https://github.com/jfversluis/MauiFolderPickerSample and wrote a little blog post about this as well here: https://blog.verslu.is/maui/folder-picker-with-dotnet-maui/

This follows kind of the basic pattern you'd want to use if you want to access platform-specific APIs:

  • Define an interface
  • Implement interface on each supported platform
  • Consume functionality

For this I have created a very simple but effective interface

public interface IFolderPicker
{
Task<string> PickFolder();
}

Then we create an implementation for Windows, by adding a new file FilePicker.cs to the Platforms\Windows\ folder. This makes it specific to Windows and allows us to write Windows specific code. The file contains this code:

using WindowsFolderPicker = Windows.Storage.Pickers.FolderPicker;

namespace MauiFolderPickerSample.Platforms.Windows
{
public class FolderPicker : IFolderPicker
{
public async Task<string> PickFolder()
{
var folderPicker = new WindowsFolderPicker();
// Make it work for Windows 10
folderPicker.FileTypeFilter.Add("*");
// Get the current window's HWND by passing in the Window object
var hwnd = ((MauiWinUIWindow)App.Current.Windows[0].Handler.PlatformView).WindowHandle;

// Associate the HWND with the file picker
WinRT.Interop.InitializeWithWindow.Initialize(folderPicker, hwnd);

var result = await folderPicker.PickSingleFolderAsync();

return result.Path;
}
}
}

Because I chose FolderPicker as the name for my own object here, there is a naming conflict with the Windows FolderPicker that is why there is that weird using at the top. If you go for MyFolderPicker as your object name that wouldn't be needed.

Now we register this interface and implementation with the generic host builder in our MauiProgram.cs:

public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});

// Note: this part was added

#if WINDOWS
builder.Services.AddTransient<IFolderPicker, Platforms.Windows.FolderPicker>();
#elif MACCATALYST
builder.Services.AddTransient<IFolderPicker, Platforms.MacCatalyst.FolderPicker>();
#endif
builder.Services.AddTransient<MainPage>();
builder.Services.AddTransient<App>();
// Note: end added part

return builder.Build();
}
}

Note that I also added MainPage and App here so that our constructor injection works (have a look at MainPage.xaml.cs in the linked repository).

Now we can consume our functionality as follows:

namespace MauiFolderPickerSample;

public partial class MainPage : ContentPage
{
private readonly IFolderPicker _folderPicker;

public MainPage(IFolderPicker folderPicker)
{
InitializeComponent();
_folderPicker = folderPicker;
}

private async void OnPickFolderClicked(object sender, EventArgs e)
{
var pickedFolder = await _folderPicker.PickFolder();

FolderLabel.Text = pickedFolder;

SemanticScreenReader.Announce(FolderLabel.Text);
}
}

Implementing other platforms would require you to implement the interface for the platform you want to support and register it in the generic host builder. This should get you started for Windows and macOS.

Actually calling this should not be any different between .NET MAUI (regular) or .NET MAUI Blazor.

How do you configure an OpenFileDialog to select folders?

I have a dialog that I wrote called an OpenFileOrFolder dialog that allows you to open either a folder or a file.

If you set its AcceptFiles value to false, then it operates in only accept folder mode.

You can download the source from GitHub here

How to open CSV file in R when R says no such file or directory?

To throw out another option, why not set the working directory (preferably via a script) to the desktop using setwd('C:\John\Desktop') and then read the files just using file names

How to change the Jupyter start-up folder

Jupyter Notebook and JupyterLab < 3.0

For old Jupyter Notebook interface installed with notebook package and run as jupyter notebook (see the next section for the identical interface installed with nbclassic and run with jupyter nbclassic, and for JupyterLab):

  1. Open cmd (or Anaconda Prompt) and run jupyter notebook --generate-config.

  2. This writes a file to C:\Users\username\.jupyter\jupyter_notebook_config.py.

  3. Browse to the file location and open it in an Editor

  4. Search for the following line in the file:
    #c.NotebookApp.notebook_dir = ''

  5. Replace by c.NotebookApp.notebook_dir = '/the/path/to/home/folder/'

    Make sure you use forward slashes in your path and use /home/user/ instead of ~/ for your home directory, backslashes could be used if placed in double quotes even if folder name contains spaces as such :
    "D:\yourUserName\Any Folder\More Folders\"

  6. Remove the # at the beginning of the line to allow the line to execute

JupyterLab >= 3, Jupyter Notebook Classic, and RetroLab

For recent nbclassic and JupyterLab >= 3 use c.ServerApp.root_dir instead of c.NotebookApp.notebook_dir (and jupyter server --generate-config instead of jupyter notebook --generate-config).

For context see migration guide and this question on differences between server and notebook.



Related Topics



Leave a reply



Submit