Reputation: 2168
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.
Upvotes: 5
Views: 2601
Reputation: 11178
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.
Upvotes: 1
Reputation: 11575
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;
}
Upvotes: 1
Reputation: 5069
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
)
Upvotes: 1