5

I'm currently working on a qt application for Windows. The user needs to be able to select a directory to load all files from. I am having an issue related to this. It seems pretty stupid, but I keep getting the same feedback. The end users get confused by the file dialog because they navigate to the folder, but it doesn't show any files. Even though they are selecting a folder, it confuses them to not see files in the directory.

So I decided to dig into it and do some research. From what I have uncovered, it seems like there are basically 2 options. The IFileOpenDialog with FOS_PICKFOLDERS, which is what I am currently using via qt's QFileDialog. Or SHBrowseForFolder, which does work, but is pretty limited.

Am I missing any options? It seems like IFileOpenDialog that showed the files without allowing the user to select them would be ideal. Is there any way to accomplish this? I found a lot of older information saying it was not possible, but nothing definitive the is more recent.

cbuchart
  • 10,847
  • 9
  • 53
  • 93
thecaptain0220
  • 2,098
  • 5
  • 30
  • 51
  • Which version of qt? 5? – Superlokkus Feb 14 '19 at 15:39
  • Yes, I'm on 5.12 – thecaptain0220 Feb 14 '19 at 15:39
  • I'd recommend against using SHBrowseForFolder, as you say, it's limited and it's full of bugs. I'd use IFileOpenDialog with FOS_PICKFOLDERS, it's definitely better that SHBrowseForFolder (which btw doesn't show files either). – Jabberwocky Feb 14 '19 at 15:44
  • 1
    I think the SHBrowseForFolder is just more clear about what you are doing. The IFileOpenDialog leaves a big empty space that just says "No items match your search". I understand the reasoning, but typical users are used to seeing files there and immediately think the application cant find anything to load. I feel like there is a middle ground that is not covered. – thecaptain0220 Feb 14 '19 at 15:53
  • 1
    You can also implement your own select folder dialog, which shows the files but give you option to select only folders. you can use this: https://doc.qt.io/qt-5/qtwidgets-itemviews-dirview-example.html: – SHR Feb 14 '19 at 15:54
  • That is interesting. I was contemplating the possibility of creating my own qml based control. QFileSystemModel looks like it would definitely help with that. – thecaptain0220 Feb 14 '19 at 15:57
  • Have you tried to use `IFileOpenDialog` without `FOS_PICKFOLDERS`? In theory, when you remove `FOS_PATHMUSTEXIST` and set a default filename like "-this directory-" (possibly also set `FOS_NOVALIDATE`) users should be able to "choose" the directory by clicking on the OK button. You just have to take the parent directory of the returned item. – zett42 Feb 14 '19 at 19:28

3 Answers3

1

Did you tried the QML file dialog FileDialog with selectFolder: true? Since the Qt5 documentation says

The implementation of FileDialog will be a platform file dialog if possible. If that isn't possible, then it will try to instantiate a QFileDialog.

it might be more user friendly for directory sets, since it uses the more native dialogs than QFileDialog. I tried it on MacOS where it shows your desired behaviour perfectly, but I don't have a windows development machine nearby, but find a minimal project attached:

main.qml:

import QtQuick 2.2
import QtQuick.Controls 1.0
import QtQuick.Dialogs 1.2

ApplicationWindow
{
    visible: true
    width: 640
    height: 480
    title: qsTr("Minimal Qml")
    FileDialog {
        id: fileDialog
        title: "Please choose a directory"
        selectFolder: true
        folder: shortcuts.home
        onAccepted: {
            console.log("You chose: " + fileDialog.fileUrls)
            Qt.quit()
        }
        onRejected: {
            console.log("Canceled")
            Qt.quit()
        }
        Component.onCompleted: visible = true
    }
}   

main.cpp

#include <QApplication>
#include <QQmlApplicationEngine>
int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("main.qml")));
    return app.exec();
}

qml.qrc

<RCC>
    <qresource prefix="⁄">
        <file>main.qml</file>
    </qresource>
</RCC>

CMakeLists.txt

cmake_minimum_required(VERSION 3.13)
project(untitled1)

set(CMAKE_CXX_STANDARD 14)

find_package(Qt5 COMPONENTS Widgets Qml Quick REQUIRED)


include_directories(${Qt5Widgets_INCLUDE_DIRS} ${QtQml_INCLUDE_DIRS})
add_definitions(${Qt5Widgets_DEFINITIONS} ${QtQml_DEFINITIONS} ${${Qt5Quick_DEFINITIONS}})
qt5_add_resources(QT_RESOURCES qml.qrc)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(PROJECT "MinimalQml")

configure_file(main.qml main.qml COPYONLY)

add_executable(${PROJECT} main.cpp ${QT_RESOURCES})
target_link_libraries(${PROJECT}
        Qt5::Widgets
        Qt5::Qml
        Qt5::Quick
        )
Superlokkus
  • 4,731
  • 1
  • 25
  • 57
  • 1
    I've tested it in Windows and the result is the same. [As the documentation states](https://doc.qt.io/qt-5/qfiledialog.html#FileMode-enum): _The native Windows file dialog does not support displaying files in the directory chooser_. I've made some comparisons in my answer below between Qt's call to native, Qt's non-native and plain native file dialogs in Windows for completeness. – cbuchart Feb 14 '19 at 17:17
1

The main issue is that, as indicated in the documentation, Windows' native file dialog doesn't support showing both files and directories when selecting directories only (check this other related answer too). For QFileDialog::FileMode::Directory:

The name of a directory. Both files and directories are displayed. However, the native Windows file dialog does not support displaying files in the directory chooser.

One workaround is to use the non-native file dialog for this kind of selection, but, personally, it looks terrible if it has to live together other native file dialogs.

Here a quick comparison of two ways of selecting a directory, using the QFileDialog::getExistingDirectory, manually by creating an instance of QFileDialog, and by using native IFileDialog:

#include <qapplication.h>
#include <qfiledialog.h>
#include <qdebug.h>
#include <Windows.h>
#include <shobjidl.h>

void using_IFileDialog()
{
  IFileOpenDialog* pFileOpen;
  HRESULT hr;

  // Create the FileOpenDialog object.
  hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
                        IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

  if (SUCCEEDED(hr)) {
    // Show the Open dialog box.
    pFileOpen->SetOptions(FOS_PICKFOLDERS | FOS_PATHMUSTEXIST);
    hr = pFileOpen->Show(NULL);

    // Get the file name from the dialog box.
    if (SUCCEEDED(hr)) {
      IShellItem* pItem;
      hr = pFileOpen->GetResult(&pItem);
      if (SUCCEEDED(hr)) {
        PWSTR pszFilePath;
        hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);

        // Display the file name to the user.
        if (SUCCEEDED(hr)) {
          MessageBox(NULL, pszFilePath, L"File Path", MB_OK);
          CoTaskMemFree(pszFilePath);
        }
        pItem->Release();
      }
    }
    pFileOpen->Release();
  }
}

int main(int argc, char* argv[])
{
  QApplication a(argc, argv);

  const auto dir_1 = QFileDialog::getExistingDirectory(nullptr, "getExistingDirectory (dirs only)");
  qDebug() << dir_1;

  QFileDialog dlg(nullptr, "QFileDialog::DontUseNativeDialog");
  dlg.setFileMode(QFileDialog::Directory);
  dlg.setOption(QFileDialog::DontUseNativeDialog);
  if (dlg.exec() == QFileDialog::Accepted) {
    const auto dir_2 = dlg.directory().absolutePath();
    qDebug() << dir_2;
  }

  using_IFileDialog();

  return 0;
}
cbuchart
  • 10,847
  • 9
  • 53
  • 93
1

Despite SHBrowseForFolder bugs, I would 100% prefer it, for the casual user is certain to get confused by the IFileOpenDialog when all he sees is an empty area. Even I myself get occasionally confused. It is simpler to code it as well.

I always use it with BIF_EDITBOX to allow power users to quickly type a path and, also, I always use it inside another dialog that has a preselected path and a ‘Change folder’ button.

Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78