QML/WebGL - 如何阻止 DefaultFileDialog 在 Windows 上返回不可用的路径?

QML/WebGL - How to stop DefaultFileDialog from returning unusable paths on Windows?

问题描述

在 Windows 上为包含 FileDialog 的桌面构建 QML 应用程序时,会显示默认的 Windows 文件对话框。这一个按预期工作,返回到所选文件或文件夹的路径,该文件或文件夹在路径前具有前缀 (file:///)。只有当 WebGL 发挥作用时,问题才会出现。

为 WebGL 平台构建并通过浏览器连接会将文件对话框默认为 DefaultFileDialog,它在选择(子)目录时表现截然不同。假设我们从对话框中的这条路径开始(剪裁以关注 ROI):

选择子目录后(单击):

再次单击进入该目录并将路径替换为:

如果在子目录中进行另一个选择,我们最终丢失了盘符:

路径是我希望在 Linux 上工作的东西,这可能是我找不到有关此行为的任何其他信息的原因。进入子目录的其他步骤不会进一步破坏路径:

我目前的搜索

我有 google 它,搜索 Stack Overflow 并尝试通过正则表达式解决这个问题,但到目前为止,运气不好。

问题

有没有办法阻止 DefaultFileDialog 实例优化 我的路径选择在 Windows 上变得无用?理想情况下,我希望在不是 运行 WebGL 应用程序时保留系统文件对话框,但这对我来说不是硬性要求。

编辑:@mike510a 揭示了我遗漏的其他要求:

  1. 驱动器号可以是任何有效的,因为在对话框中,驱动器可以更改。因此,硬编码驱动器号以替换对话框中丢失的驱动器号是行不通的。
  2. 由于在接受文件或文件夹之前采取的步骤数量未知,因此不能假设驱动器号已经从返回的 QString 中删除。

示例代码

修改由 QtCreator 创建的项目(在我的例子中是 4.13.2,空 QtQuick 项目)。

# autogenerated
QT += quick

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp

RESOURCES += qml.qrc

# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =

# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
  QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

  QGuiApplication app(argc, argv);

  app.setOrganizationName("Something");        // modified to avoid QML warning
  app.setOrganizationDomain("Something.else"); // modified to avoid QML warning

  QQmlApplicationEngine engine;
  const QUrl url(QStringLiteral("qrc:/main.qml"));
  QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                   &app, [url](QObject *obj, const QUrl &objUrl) {
    if (!obj && url == objUrl)
      QCoreApplication::exit(-1);
  }, Qt::QueuedConnection);
  engine.load(url);

  return app.exec();
}

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Dialogs 1.2
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3

Window {
    width: 640
    height: 480
    minimumHeight: 320
    minimumWidth: 480
    visible: true
    title: qsTr("Hello World")
    color: "grey"

    RowLayout {
        anchors.fill: parent

        Label {
            text: "File path:"
            Layout.column: 0
        }
        TextEdit {
            id: pathTextEdit
            readOnly: true
            clip: true
            Layout.column: 1
            Layout.fillWidth: true
            Rectangle {
                z: -1
                anchors.fill: parent
                radius: 3
                color: "white"
            }
        }
        Button {
            text: "Select file"
            Layout.column: 2
            onClicked: {
                pathSelection.visible = true
            }
        }
    }

    FileDialog {
        id: pathSelection
        visible: false
        width: 300
        height: 300
        selectFolder: true
        onAccepted: {
            pathTextEdit.text = folder
        }
    }
}

我很确定因为 WebGL 平台使用与 Windows 文件系统目录结构不同的架构,所以您会得到奇怪的文件夹。您可以使用架构 file:// 将 URLS 转换为指向本地计算机上文件的指针,您可以将 folder 属性 与 file:///C:/Users/whatever 一起使用或执行以下操作...

要获取操作系统的底层文件系统位置,您可以使用 folder 属性 和值 shortcuts.home,如下所述:https://doc.qt.io/qt-5/qml-qtquick-dialogs-filedialog.html

 FileDialog {
        id: pathSelection
        visible: false
        width: 300
        height: 300
        selectFolder: true

        // point to an operating system location and you should get
        // a usable URL

        folder: shortcuts.home

        onAccepted: {
            pathTextEdit.text = folder
        }
    }

在 Qt 支持的帮助下,可以找到解决方案。导致该行为的问题在 DefaultFileDialog.qml 中找到,更具体地说,在函数 dirDown 中。可以通过替换

来修复
root.folder = "file://" + path

root.folder = root.pathToUrl(path)

然后重建 Qt。整个补丁是这样写的:

--- a/src/dialogs/DefaultFileDialog.qml
+++ b/src/dialogs/DefaultFileDialog.qml
@@ -115,7 +115,7 @@ AbstractFileDialog {
 
     function dirDown(path) {
         view.selection.clear()
-        root.folder = "file://" + path
+        root.folder = root.pathToUrl(path)
     }
     function dirUp() {
         view.selection.clear()

此补丁已提交以包含在 Qt 5.15.3 中。