Problem with dynamically loaded images when using Qt Resource System



  • Hello,

    I'd like to use the Qt Resource System (qrc) for distributing my app. I developed it without qrc and used relative paths to the resources, which was no problem. But I don't want the qml source code to be visible when distributing the app, so I switched everything to qrc. This also works fine with all images that are displayed in the "normal" way, like this:

    @Image {
    source: "../images/test.png"
    }@

    But I have some images which are dynamically loaded (it's a game, so some game objects are created dynamically), and these are not displayed anymore. My code looks like this:

    @property int someInteger
    property url imagePath: "../images/test.png"
    property url imagePath2: "../images/test2.png"

    function getImagePath() {
    switch (someInteger) {
    case 0: return imagePath;
    case 1: return imagePath2;
    }
    }

    Image {
    source: getImagePath()
    }@

    Which one of these image will be used is decided at runtime. When I switched to qrc, these images were not visible anymore and I don't know why.

    Strangely, it works when I open the release build in the IDE (I use Visual Studio) , but it doesn't work when I open the executable. I also removed the image folder from the visual studio project, to see if the app still uses the relative paths when creating the dynamic objects, but it doesn't. The behaviour is the same: It works in the IDE, but not outside. Is there any special file that I have to include in the release package so that it works?

    I also tried different versions of the url in the qml files, for example
    "qrc:/images/..."
    "qrc:///images/..."
    ": / images/..."

    But none of these work, and I don't think that's the problem because in the Qt documentation, I always read that you only have to use the qrc prefix in the C++ files. In the qml files, everything should work as before...

    Do you have any suggestions? Thanks!


  • Moderators

    When you use QRC, the QML engine switches to "qrc mode" totally. That is, it interprets all URLs it is given as QRC ones. This is why it suddenly stops recognizing your relative paths.

    I have 2 suggestions:

    Reset the engine context. This, although it may look strange, works very well. Example from my project: "link":https://github.com/sierdzio/closecombatfree/blob/master/src/ccfmain.cpp#L171

    Use full URL for local files:

    @
    imagePath: "file:///path/to/my/file.png"
    @



  • Thanks for your answer! I will try one of the solutions and report back later :)



  • I see, so you suggest to disable the qrc-mode? Maybe there is a misunderstanding. I don't want to use relative paths anymore, each one of my images is in qrc format. And if it is true that all URLs are switched to qrc (also the URLs that are defined as custom properties), then my code should work. But strangely, it only works when I write the url directly behind the "source:" keyword.


  • Moderators

    Perhaps indeed we do not understand each other. I do not know what you mean in your last post.

    If all your pictures are in the resource file (compiled into your binary or a QRC file), then you need to modify all paths to be relative to the QRC root.

    If you load your QML from the resource file, but you want to access picters that are not compiled into the resource file, you need to use one of the suggestions I've posted.



  • Okay, then I try to explain it again. Sorry if my first post was too confusing.

    All of my images are in the resource file, without exceptions. I provided two code examples in my first post, and in both of them I want to use images from the resource file.
    Of course all of the paths look like relative paths, but as you said, the QML engine totally switches to qrc mode and then it looks up everything in the resource file. That's exactly what I want. I leave the relative paths as they are, but transfer the images to QRC and then it still works fine.

    The strange thing is, that the first one works and the second one does not. When I define URLs as "property url imagepath: ...", then it doesn't work. I don't understand why the QML engine doesn't find these images in the resource file, though they are definitely there...


  • Moderators

    OK, thank now I get it. "dynamically loaded images" got me into thinking that you are trying to include pictures from outside of QRC.

    My first bet here is that you should define string properties instead of url properties.



  • I already tried that, it still doesn't work



  • Hello,

    this code works like a charm:

    @import QtQuick 2.4
    import QtQuick.Window 2.2

    Window {
    visible: true
    property int someInteger: 0
    property url imagePath: "qrc:/Chrysanthemum.jpg"
    property url imagePath2: "qrc:/Desert.jpg"

    function getImagePath() {
      switch (someInteger) {
        case 0: return imagePath;
        case 1: return imagePath2;
      }
    }
    
    Image {
      source: getImagePath()
      MouseArea {
          anchors.fill: parent
          onClicked: someInteger = someInteger == 0 ? 1 : 0
      }
    }
    

    }@

    Tested with QtCreator 3.3.0 and Qt 5.4.0 using MSVC2013.

    Both images were added manually into the qrc file.
    I can change someInteger and since QML detects that a variable inside the function getImagePath() changed it will reinvoke the method for source:. It updates perfectly and also the qrc path in QML should begin with "qrc:/", since i remember it could work with ":/" too.

    Looks like QML can work with that so i assume the problem is the part where you add the images into the qrc.

    • Could you please try out the code above and tell us your results?
    • And is there any console output in your application?


  • Thank you, this code works perfectly for me too. Now that I know that QML can indeed handle this, the problem has to be somewhere else.

    In my code, the objects which should display the images are created from the C++ side. I do this by creating a QQmlComponent and then I call the create() function several times. When the qml objects are created, I call setProperty() to set the variables (the one that I called "someInteger" here). I don't think there are any errors in the code, because when I don't use QRC, then there are no problems.

    Another strange thing that I mentioned in my first post is, that it works when I open the app in Visual Studio. It even works when I remove all images from the Visual Studio project, so I'm sure the app only uses images from the QRC file. But when I open the executable itself, then it doesn't work.

    Do you have more suggestions which could help me solve the problem? Thanks for the help so far!

    Edit: Yes I have some console output in my application, for example I print out the source property of the QML images in the moment when they should be displayed on screen and they always have the correct URL. But I can't see them...



  • Hey,

    • Which compiler are you using in the QtCreator?
    • Which Qt version are you using?

    Maybe you are missing a dependency. Have you checked your executeable with a dependency walker? I need a subdirectory called "imageformats" when i am working with Images. In this path i have got all the required image .dll files.

    !http://i.imgur.com/fgRKdzH.png(Example Structure)!

    My projects can start and run without them but it won't display any images if i don't have this files.
    This seems familiar with your problem.



  • I think I will try to use my method together with your example. I'll create the QML object from the C++ side, and also set the property someInteger from the C++ side. I'll post again when I have the results :)



  • I didn't use a dependency walker, I just put in all dll files and then removed them step by step, until I had everything that I needed to run the app. The imageformats folder is already included.

    I use the newest Qt version 5.4 and the Visual Studio add-in, not Qt Creator.



  • [quote author="CarolaC" date="1422352564"]I think I will try to use my method together with your example. I'll create the QML object from the C++ side, and also set the property someInteger from the C++ side. I'll post again when I have the results :)[/quote]

    Alright, go ahead :).
    -So the "imageformats" folder is no problem?-



  • How do you set the source property of your Components? Could you please provide us a code snippet?



  • I tested this and it's almost the same result as in my app. It works in Visual Studio, but not when I start the executable. I did the following:

    I created a QQmlComponent from the QML file, then I created a QObject with the create() method. Then I set a timer which triggers the setProperty() method after 10 seconds, to switch the value of the someInteger variable.

    When I open the program in Visual Studio, at first I see an image, then after 10 seconds it changes to the other image. But when I open the executable, I see the image, and after 10 seconds the app crashes.
    I also checked if some dependency is missing (I put the executable in a new folder together with all dlls, all plugins, all qml files etc.) but the behavior is the same. I can post my code here, maybe the error can be reproduced:

    @// GameApp is a QGuiApplication
    // MainWidget is a QQuickView@

    @int main(int argc, char argv[])
    {
    GameApp app (argc, argv);
    MainWidget
    mainWidget = new MainWidget;
    app.connectToView (mainWidget);
    mainWidget->setResizeMode (QQuickView::SizeRootObjectToView);
    mainWidget->setSource (QUrl ("qrc:/qml/Test.qml"));

    app.prepareTest ();

    mainWidget->show ();
    app.exec ();
    }@

    @void GameApp::connectToView (MainWidget* mWidget)
    {
    if (mainWidget == 0)
    mainWidget = mWidget;
    }@

    @void GameApp::prepareTest ()
    {
    QQmlComponent* component = new QQmlComponent (mainWidget->engine (), QUrl ("qml/Test.qml"));
    qmlGroup = component->create ();
    QQmlProperty::write (qmlGroup, "parent"
    , QVariant::fromValue<QObject*> (mainWidget->rootObject ()));
    QQmlEngine::setObjectOwnership (qmlGroup, QQmlEngine::CppOwnership);

    QTimer::singleShot (10000, this, SLOT (displayImage ()));
    }

    void GameApp::displayImage ()
    {
    qmlGroup->setProperty ("someInteger", 1);
    }@

    Test.qml:
    @import QtQuick 2.4
    import QtQuick.Window 2.2

    Item {
    visible: true
    property int someInteger;
    property url imagePath: "qrc:/images/icons/iconFistL.png"
    property url imagePath2: "qrc:/images/icons/iconBig5L.png"

    function getImagePath() {
      switch (someInteger) {
        case 0: return imagePath;
        case 1: return imagePath2;
      }
    }
    
    Image {
      source: getImagePath()
    }
    

    }@

    I don't know if this is a bug or maybe I'm doing something wrong. Unfortunately the app has to be ready next week and I don't know if I can spend more time on this issue. If you want to investigate further, then feel free to do so, but if the issue cannot be resolved, then I don't use the Qt Resource System. That would be okay, too ;)

    Many thanks to you two for your help so far!



  • Hi,
    I thought I'd step into this thread, maybe you can benefit from our learnings..

    We have also been seeing issues with qrc in our projects, and the default Qt implementation has a couple of disadvantages:

    • It is ugly to have to change the qrc:/ scheme in all your images - ideally you would not need to change the scheme when you switch between qrc and non-qrc
    • Qrc has problems if the size of all combined images gets bigger than 50MB. Thus ideally you would ship the image assets separately as non-qrc, and use qrc for your qml files (to protect them from outside use). However, you still should have the option to also put the assets into qrc, if exposing your image files is a problem.

    That's exactly what we allow to do in V-Play: the QML & JS files are put into qrc files (this also makes sure no one can read your source code) – the assets are put into an own assets folder and are deployed with DEPLOYMENTFOLDERS += assets in the .pro file. You can also use qrc for assets if your prefer, but it has some limitations like the 50MB limit).

    That way, you don’t have the size limitation of qrc files (at above ~50MB qrc hangs).
    Also, during development working with DEPLOYMENTFOLDERS is better on Desktop, because you can just re-run your application and dont need to recompile.

    What we did additionally, is to abstract the file access, i.e. you dont need a prefix assets:/… for your images, or mix up your code with any qrc:/… prefixes.

    You can have a look at some .pro files of our games by downloading the "V-Play SDK":http://v-play.net and then check out the <QtSDK>/examples/V-Play/demos directory.

    I’ve also posted some comments about the qrc approach set as default since Qt Creator 3.1 on the Qt Blog, maybe that’s interesting to the pros and cons of it (and why I think it’s bad..):
    http://blog.qt.digia.com/blog/2014/03/04/qt-creator-3-1-beta-released/#comment-1193034

    Hope that helps,
    Chris


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.