【问题标题】:C++ Select Folder, Include FilesC++ 选择文件夹,包含文件
【发布时间】:2019-07-08 16:39:06
【问题描述】:

我目前正在为 Windows 开发一个 qt 应用程序。用户需要能够选择一个目录来加载所有文件。我有一个与此相关的问题。这似乎很愚蠢,但我一直得到相同的反馈。最终用户对文件对话框感到困惑,因为他们导航到该文件夹​​,但它不显示任何文件。即使他们正在选择一个文件夹,这也会让他们感到困惑,因为他们看不到目录中的文件。

所以我决定深入研究它并进行一些研究。从我发现的情况来看,似乎基本上有两种选择。 IFileOpenDialogFOS_PICKFOLDERS,这是我目前通过 qt 的 QFileDialog 使用的。或SHBrowseForFolder,它确实有效,但非常有限。

我是否缺少任何选项?似乎IFileOpenDialog 显示文件而不允许用户选择它们是理想的。有没有办法做到这一点?我发现很多旧信息都说这是不可能的,但没有确定是最近的。

【问题讨论】:

  • 哪个版本的qt? 5?
  • 是的,我在 5.12
  • 我建议不要使用 SHBrowseForFolder,正如你所说,它是有限的,并且充满了错误。我会将 IFileOpenDialog 与 FOS_PICKFOLDERS 一起使用,SHBrowseForFolder 肯定更好(顺便说一句,它也不显示文件)。
  • 我认为 SHBrowseForFolder 只是更清楚你在做什么。 IFileOpenDialog 留下一个很大的空白空间,上面写着“没有项目符合您的搜索”。我理解其中的原因,但典型用户习惯于在那里查看文件并立即认为应用程序找不到要加载的任何内容。我觉得有一个没有被覆盖的中间立场。
  • 您也可以实现自己的选择文件夹对话框,它会显示文件,但您可以选择仅选择文件夹。你可以使用这个:doc.qt.io/qt-5/qtwidgets-itemviews-dirview-example.html:

标签: c++ windows qt winapi


【解决方案1】:

尽管有SHBrowseForFolder 错误,我还是会 100% 喜欢它,因为当他看到的只是一个空白区域时,临时用户肯定会对 IFileOpenDialog 感到困惑。甚至我自己也偶尔会感到困惑。编码也更简单。

我总是将它与 BIF_EDITBOX 一起使用,以允许高级用户快速键入路径,而且我总是在另一个对话框中使用它,该对话框具有预选路径和“更改文件夹”按钮。

【讨论】:

    【解决方案2】:

    主要问题是,如文档中所述,Windows 的本机文件对话框不支持仅在选择目录时同时显示文件和目录(也请检查 this other related answer)。对于QFileDialog::FileMode::Directory

    目录的名称。显示文件和目录。但是,本机 Windows 文件对话框不支持在目录选择器中显示文件。

    一种解决方法是使用非本机文件对话框进行这种选择,但就个人而言,如果它必须与其他本机文件对话框一起使用,这看起来很糟糕。

    这里快速比较两种选择目录的方法,使用QFileDialog::getExistingDirectory,手动创建QFileDialog的实例,以及使用原生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;
    }
    

    【讨论】:

      【解决方案3】:

      您是否尝试过 QML 文件对话框 FileDialogselectFolder: true?由于 Qt5 文档说

      如果可能的话,FileDialog 的实现将是一个平台文件对话框。如果这不可能,那么它将尝试实例化一个 QFileDialog。

      它可能对目录集更加用户友好,因为它使用比 QFileDialog 更多的本机对话框。我在 MacOS 上进行了尝试,它完美地显示了您想要的行为,但我附近没有 Windows 开发机器,但找到了一个最小的项目:

      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
              )
      

      【讨论】:

      • 我在 Windows 中测试过,结果是一样的。 As the documentation states原生 Windows 文件对话框不支持在目录选择器中显示文件。为了完整性,我在下面的回答中对 Qt 对本机的调用、Qt 的非本机和 Windows 中的纯本机文件对话框进行了一些比较。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-01
      • 2013-05-01
      • 2021-01-16
      • 1970-01-01
      • 2015-09-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多