Search code examples
gtk3gnomefilechoosergobjectgjs

How to get files from selected folder with GtkFileChooserButton?


I am making a GTK+3 App with GJS where users select a folder from a GtkFileChooserButton (action property set to select-folder). I want to find all image files in the given folder the user have selected, so I can display one of the images.

I tried this._fileChooserButton.get_files() and this._folderChooseButton.get_uris() but they only return one file, which is the path to the selected folder. Like this:

    _init(application) {
        this._folderChooseButton.connect('file-set', () => {
          this._onFolderChosen();
        });
    }

    _onFolderChosen() {
      let folder = this._folderChooseButton.get_file();
      // somehow get files from the folder here
      this._image.set_from_file(files[1]);
    }

From the API it is not really clear to me, how do I find out which image files are inside the user's selected directory (and subdirectories)?


Solution

  • OK, after help from patrick, georges and matthias at guadec, here is what I got.

    The get_file() function I tried returns a GFile, which in this case is a folder (in UNIX, folders are also files). In order to get the files within the directory path, we need to call enumerate_children_async() on our GFile, returned by the get_file() function.

    The enumate_children_async() function takes five parameters:

    1. A comma-separated attribute list. In our case, since we want the identifiers of the children in the directory, we want to use the attribute called standard::name.

    2. FileQueryInfoFlag: This allows to either follow or not follow symbolic links. In this case, we will use FileQueryInfoFlag.NONE which will not follow symbolic links.

    3. io_priority: How high priority the IO operation should have (we will use GLib.PRIORITY_DEFAULT)

    4. cancellable: A cancellable, which is a way to cancel this operation, in this case we will leave it as null.

    5. callback: This is the function/code you want to run in response to the files having been retreived.

    More info on this function is at GJS-Docs at GNOME.org

    The enumerate_children_async() function returns a GFileEnumerator, which we can use to retreive a number of the files, by calling next_files_async(), which takes these arguments:

    1. num_files: How many files you want to retreive. In your case, we use 1.

    2. io_priority and cancellable (same as above).

    3. callback: Where we can run a function or code to actually retreive the file.

    Below, is the final code for doing this.

    const { Gio, GLib, GObject, Gtk } = imports.gi; // import Gio and GLib API at top of your document.
    
    _onFolderChosen() {
      let folder = this._folderChooseButton.get_file();
      let files = folder.enumerate_children_async(
        'standard::name',
        Gio.FileQueryInfoFlags.NONE,
        GLib.PRIORITY_DEFAULT,
        null,
        (source, result, data) => {
          this._fileEnumerator = null;
          try {
            this._fileEnumerator = folder.enumerate_children_finish(result);
          } catch (e) {
            log('(Error) Could not retreive list of files! Error:' + e);
            return;
          }
          this._readNextFile();
        });
    }
    
    _readNextFile() {
      if (!this._fileEnumerator)
        return;
    
      let fileInfo = null;
      this._fileEnumerator.next_files_async(
        1,
        GLib.PRIORITY_DEFAULT,
        null,
        (source, result, data) => {
          try {
            fileInfo = this._fileEnumerator.next_files_finish(result);
          } catch (e) {
            log('Could not retreive the next file! Error:' + e);
            return;
          }
          let file = fileInfo[0].get_name();
          let filePath = GLib.build_filenamev([this._folderChooseButton.get_filename(), file]);
          this._carousselImage.set_from_file(filePath);
      });
    }