QML /WebGL — Как запретить DefaultFileDialog возвращать неиспользуемые пути в Windows?

#qt #dialog #qml #webgl

#qt #диалоговое окно #qml #webgl

Вопрос:

Описание проблемы

При создании приложения QML в Windows для рабочего стола, содержащего FileDialog, отображается диалоговое окно файла Windows по умолчанию. Этот работает как ожидалось, возвращая путь к выбранному файлу или папке с префиксом (file:///) перед путем. Проблема возникает только после того, как WebGL вступает в игру.

Сборка для платформы WebGL и подключение через браузер приведет к тому, что по умолчанию диалоговое окно файла будет иметь значение DefaultFileDialog, которое ведет себя совершенно иначе при выборе (вложенных) каталогов. Допустим, мы начинаем с этого пути в диалоговом окне (обрезан, чтобы сосредоточиться на рентабельности инвестиций):

Первый путь к папке в диалоговом окне начинается с C:/

После выбора подкаталога (одним щелчком мыши):

После выбора одного из подкаталогов C:/ заменяется на c:/ . Обратите внимание, что этого не произойдет, если выбран ярлык (сбрасывается на C:/)

Другой щелчок вводит этот каталог и заменяет путь на:

Ввод выбранного подкаталога удаляет двоеточие и помещает две дополнительные косые черты спереди (//c/)

Если в подкаталоге выбран другой вариант, мы окончательно потеряем букву диска:

Больше нет доступной буквы диска. Я думаю, этот стиль пути должен работать в Linux, но больше не работает в Windows

Я ожидаю, что этот путь будет работать в Linux, что может быть причиной, по которой я не смог найти никакой дополнительной информации об этом поведении. Дополнительные шаги в подкаталогах не нарушают дальнейший путь:

Дополнительные шаги в подкаталогах не нарушают дальнейший путь

Мой поиск до сих пор

Я погуглил, искал 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(amp;engine, amp;QQmlApplicationEngine::objectCreated,
                   amp;app, [url](QObject *obj, const QUrl amp;objUrl) {
    if (!obj amp;amp; 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
        }
    }
}

  

Комментарии:

1. Я полагаю, вы можете найти DefaultFileDialog. qml в дереве исходных текстов qtdeclarative вы можете просто скопировать его в папку вашего проекта и изменить его, удалив любой код, который также вызывает нежелательное поведение.

2. Это подход, который я пытаюсь применить после разговора со службой поддержки Qt. Скрестив пальцы, я получаю решение, которое они предоставили для работы 🙂

Ответ №1:

Я почти уверен, что, поскольку платформа WebGL использует схему, отличную от структуры каталогов файловой системы Windows, вы получите странные папки. Вы можете использовать схему file:// для преобразования URL-адресов в указатели на файлы на локальном компьютере, вы можете использовать folder свойство вместе с file:///C:/Users/что угодно или сделать следующее…

Чтобы получить расположение базовой файловой системы операционной системы, вы можете использовать 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
        }
    }
  

Комментарии:

1. Спасибо за предоставление этого ответа. К сожалению, это не помогает мне в этом случае, поскольку начальное местоположение в порядке, но как только я нахожусь в диалоговом окне, возвращаемое значение, сохраненное в folder , непригодно для использования (поскольку буква диска удаляется при запуске для WebGL из-за того, что DefaultFileDialog используется вместо диалогового окна системного файла, я полагаю). Итак, я начинаю с действительного пути, но теряю его, работая сам по подкаталогам, как показано на скриншотах-

2. Не могли бы вы просто сделать причудливое объединение или что-то в этом роде с возвращаемым именем файла? как QString newfile = QString("C:/%1").arg(oldfile); тогда сделать newfile.replace("/", QDir::Separator()); , чтобы переключить косые черты??

3. Это было бы решением, если бы я мог гарантировать две вещи: 1. Я всегда работаю на диске C 2. Буква диска всегда удаляется. Я отредактирую вопрос, чтобы явно указать требование к букве диска. Спасибо!

Ответ №2:

С помощью поддержки 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.