Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Loading UI Form from disk, logic deployed as resource



  • I'm currently working on an application in which I want to give the opportunity to customize the look and feel of the application.

    Is there some way to load the UI part from disk (eg. a subdirectory of the applicationDirPath), while the logic to this form is contained as qrc?

    Adding the disk path as importPath to the qml engine does not work.

    In depth:
    I add the path to Page3Form.ui.qml to the engine's importPath.
    If I push Page3.qml (contained in the qrc) to a stackView, Page3Form can't be found (Page3Form is not a type).

    Page3.qml:

    import QtQuick 2.7
    
    Page3Form {
    
    }
    

    Page3Form.ui.qml:

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    
    Page {
        width: 600
        height: 400
    
        title: qsTr("Page 3")
    
        Label {
            text: qsTr("You are on Page 3.")
            anchors.centerIn: parent
        }
    }
    

    If I add Page3Form.ui.qml to the resource file, the test app is working as expected.



  • Or does anyone know a way to make the UI customizable without exposing my program logic?



  • Is Page3Form.ui.qml in the same directory? I guess it cant be if one is in a qrc.

    Also for importing:
    https://stackoverflow.com/questions/22168457/include-another-qml-file-from-a-qml-file

    Maybe try putting your user customizable qml in a directory that is imported?



  • I have tried to load qml files from the local directory and while it acts like it will work in the editor it will not see those files when running. I don't see a way to make qml files that are qrc resources to "see" files that are local on the disk. There has to be a way though. It seems silly that you could not mix them.



  • I checked the qml import path:

    qInfo() << engine.importPathList();
    

    It returns the local directory of the application. I have qml files and directories placed there and it will not import the files or directories from there.



  • I can import relative path qml files this way:

    import 'file:qml' as QML
    ...
    QML.TestComponent02 {
    
        }
    

    Where qml is a directory in the current directory of where the executable runs and TestComponent02 is TestComponent02.qml in that qml directory. I cannot find a way to import the current directory path.

    I found this where some of this is discussed:

    https://stackoverflow.com/questions/45484222/qml-import-external-javascript-file
    


  • @fcarney said in Loading UI Form from disk, logic deployed as resource:

    I can import relative path qml files this way:

    import 'file:qml' as QML
    ...
    QML.TestComponent02 {
    
        }
    

    I could live with a fixed subdirectory of the applicationDirPath, but this only works for me with an absolute path, like

    import "file:D:/Projects/qmltest/qml" as QML
    

    I added both the applicationDirPath and the qml-subdir to the importPathList, with no luck. Did you add anything else to get this to work? Is the file containing this import loaded from qrc?

    @fcarney said in Loading UI Form from disk, logic deployed as resource:

    I found this where some of this is discussed:

    https://stackoverflow.com/questions/45484222/qml-import-external-javascript-file
    

    Unfortunately this seems to work only for javascript files (and not qml).


  • Moderators

    @ThoPa
    I haven't tried this, but you could give your qml engine the applicationDir path as contextproperty

    setContextProperty("applicationDirPath", QApplication::applicationDirPath());

    Maybe the import accepts that as valid path ?



  • @J.Hilk said in Loading UI Form from disk, logic deployed as resource:

    @ThoPa
    I haven't tried this, but you could give your qml engine the applicationDir path as contextproperty

    setContextProperty("applicationDirPath", QApplication::applicationDirPath());

    Maybe the import accepts that as valid path ?

    Yeah, I tried that, but you can't use variables in import statements.

    But I guess I found another workaround.
    If I create my own NetworkAccessManagerFactory and pass it to the qmlengine, use my own scheme in the import statement and redirect this to the local dir, it seems to work:

    QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& originalRequest, QIODevice* outgoingData = nullptr) {
          if (op == QNetworkAccessManager::GetOperation) {
            QUrl url = originalRequest.url();
    
            if (originalRequest.url().scheme() == "test") {
              QUrl nurl = QUrl::fromLocalFile(QString("%1/qml%2").arg(qApp->applicationDirPath()).arg(url.path()));
              
              QNetworkRequest req(originalRequest);
              req.setUrl(nurl);
              
              return QNetworkAccessManager::createRequest(op, req, outgoingData);
            }
          }
    
          return QNetworkAccessManager::createRequest(op, originalRequest, outgoingData);
        }
    

    And in my Page3.qml from qrc:

    import "test://qml" as Test
    
    Test.Page3Form {
    
    }
    

    Thanks for the help!



  • You have to copy the qml files to the actual run/build directory for it to see the files. The path of that directory is added to the qml import paths. So wherever the exe is actually running from is the path that shows up as "local" to the program. I had to explicitly copy those files. So if you want to have your source qml be seen after changes you may want to add the copying of those files to the build process script (pro file).



  • Another option that you could use for development is to add an additional import path:

    engine.addImportPath("D:/Projects/qmltest/qml");
    qInfo() << engine.importPathList();
    

    Then during development you don't have to copy files.


Log in to reply