18

We are developing our first Qt/QML application (trying technology). While technology looks very promising at a glance, we have faced so many unexpected weird issues that almost give up at very beginning.

Here one of such issue.

We want the following folders layout for our application:

--> ApplicationFolder
|--> qml        // QML files (also structured by subfolders)
|--> resources  // Application resources (images, sounds, data-files, etc.)
| |--> images   // Image resources (also structured by subfolders)
| |--> data     // Different data files
| |--> ...      // Other resources
|--> Application.exe   // Application executable
|--> *.dll             // DLLs application depends on

The problem is that in order to specify image file for Image QML item we have to use path relative to QML file?! This is absolutely insane. During development files sometimes moved between folders (you move QML file and have to fix all the path it has?!); some different QML files have to refer to same image (same image resource but different actual path in different QML files).

So the question is: how to specify image path relative to application folder? Is it possible at all?

Thanks in advance!

PS. Using Qt's resource system (when resources are embedded into executable) is not an option in our case. We need raw resources on disk (including QML files itself, at least during development phase).

PPS. Wrote this question after spending a whole day to resolve the issue by myself via documentation/google/stackoverflow; no success at all (most examples use resource embedding, others are too simple and just use relative paths).

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
Alexey Kryshen
  • 1,067
  • 2
  • 11
  • 15
  • `This is absolutely insane. During development files sometimes moved between folders (you move QML file and have to fix all the path it has ?!);` -> What is insane about developing during the development phrase? Obviously if you do not design it properly, just do cargo-cult programming, you may need to reimplement stuff with new design. Is it also insane that you rename something everywhere when you rename something 'cause you realize that you have given wrong name to a variable? Come on, Alexey... :) Sed is your friend ... – László Papp Dec 18 '14 at 15:30
  • 4
    @lpapp This is insane because if image file stay at the same place, then it is absolutely normally to wish the reference to this file stay the same in application code and do not depend on moving other things. Isn't ? If some QML file refer to some image and this image stay at the same place all the time, why one need to fix that QML each time he want to move it to other folder ? That is what I am talking about. When using qrc files you have no such problem, but with raw resources we just can't find a solution unfortunately. – Alexey Kryshen Dec 18 '14 at 15:44

4 Answers4

32

If you can't use a .qrc file for your images, you could try this:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("applicationDirPath", QGuiApplication::applicationDirPath());
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

In QML, you'd then do:

Image {
    source: "file:///" + applicationDirPath + "/../resources/images/image.png"
}

Note that this code is not tested.

Mitch
  • 23,716
  • 9
  • 83
  • 122
  • 3
    Thanks @Mitch! It seems to be a possible solution. Just tested it. In order to get it run properly one need to prepend `"file:///"` to `QGuiApplication::applicationDirPath()`. And it seems that injected `applicationDirPath` value available in all QML files loaded by application, not only in main one loaded by `engine.load(...)` (I was concerned about it at the beginning). Could be improved a bit by passing current working directory of process, to avoid the `/../` at the relative resources path and should work in debug runs in QT Creator and in standalone application runs. Thanks a lot! – Alexey Kryshen Dec 18 '14 at 23:19
  • I couldn't say, as I haven't tested that with a benchmark. It's very easy to set up a benchmark with Qt Creator's project wizard though (even a QML benchmark), and Creator's QML Profiler can help you pinpoint any bottlenecks. – Mitch Dec 19 '14 at 14:41
  • Thank you @Mitch! Will investigate the QML profiling. – Alexey Kryshen Dec 19 '14 at 14:50
  • 2
    Just needed to put the `file:///` in front. Thanks for posting your comment @AlexeyKryshen ! – phyatt Jun 25 '16 at 04:09
  • that not working for me, also is it same for android device? – mohammad alabid Feb 14 '17 at 11:50
  • Instead of somewhat hackishly prepending `file:///` I would recommend to wrap the thing in a `QUrl::fromLocalFile()` like this: `setContextProperty("applicationDirPath", QUrl::fromLocalFile(QGuiApplication::applicationDirPath()))` – Silvan Wegmann Dec 22 '20 at 10:37
3
Image {
   source: "file:resources/images/image.png"
}

will work if the working directory is set to the ApplicationFolder

note: when running from QtCreator, double check what directory the application is actually running in (i had my application run in my home directory even though the working directory was set correctly in the run configuration (after running it once in a terminal window this magically fixed itself) )

newmind
  • 56
  • 2
  • I'd advice against this and use QStandardpaths or something similar, because this doesn't work in environments where cwd is not the appdir. – Thaodan May 04 '19 at 21:28
0

Since QT 6.0 you have the following convenience method:

https://doc.qt.io/qt-6/qqmlapplicationengine.html#setExtraFileSelectors

Sets the extraFileSelectors to be passed to the internal QQmlFileSelector used for resolving URLs to local files. The extraFileSelectors are applied when the first QML file is loaded. Setting them afterwards has no effect.

In case of PySide6 you would use it as following:

from pathlib import Path
import sys

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine

if __name__ == "__main__":
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    app_root_path = Path(__file__).parent
    engine.setExtraFileSelectors(app_root_path.as_uri()) 
    qml_file = app_root_path / 'main.qml'
    engine.load(qml_file.as_uri())

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec())

Now you can access your files in QML with a relative path like this:

 source: "file:images/icon.svg"
alexsb
  • 1
  • 1
-1

Old questions, but got here from google. For me, it works simply with a relative path like this:

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.3

[...]    

Image {
    id: image
    source: "icons/brightness.svg" // simple relative path
    width: 22
    height: 22
    sourceSize.width: 22 // svg render resolution
    sourceSize.height: 22
    fillMode: Image.PreserveAspectFit // just in case to avoid stretching
}

no .qrc needed.

KYL3R
  • 3,877
  • 1
  • 12
  • 26