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.
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
)
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;
}
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