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

Dynamically update QML Image



  • Hello everyone,

    So What I am trying to do is fine the most efficient way to feed pre-constructed image data to a QML image.
    I'm using PyQt5 (Sorry!) but answers in C/C++ are okay as well since I know both.

    What this program is doing is creating random images from the Python side and then "feeds" the constructed image elsewhere from memory. So is there a way to take that image data and update a QML image dynamically every second?

    I heard QQuickImageProvider was the option to use but I am having difficult reassigning new image data with it. It works if I assign an image once and only once using that 'RequestImage' function (which I overloaded).

    Maybe I'm approaching it wrong? Do you have any other suggestions I should try?

    So superficially my code looks like this (It's kind of psudocode so bare with me...)

    QML side --

    ApplicationWindow{
       id:win
       objectName: "win"
       width: 640
       height: 480
       
       Image{
        id: img
        objectName: "img"
        anchors.fill:parent
        source: "image://myprovider/test.png"
        }
    }
    

    Python side --

    class MyImageProvider(QQuickImageProvider):
        def __init__(self):
            super(MyImageProvider, self).__init__(QQuickImageProvider.Image)
    
        # Here is where the image will be filled
        def requestImage(self, p_str, size):
            proc_img = QImage(640, 480, QImage.Format_RGBA8888)
            proc_img.loadFromBytes(ProcessImage())
            return proc_img, proc_img.size()
    
    def ProcessImage():
       #Image processing here from bytes...
       ...
       return byte_output
    
    app = QApplication(sys.argv)
    engine = QmlApplicationEngine()
    
    provider = myImageProvider()
    engine.addImageProvider("myprovider", provider)
    app.exec_()
    

    Now, this works once. Once I add the image provider, I can never call requestImage again and see an update. Are there different ways to go about updating an image dynamically qith Qt/QML and either C/C++ or Python? any direction would be greatly appreciated.

    One option I did hear was to have Python create the image in a temporary directory in the file system such as my_image_001.png, my_image_002.png, my_image_003.png and make it a circular buffer and have the Python side update the QML side with these images. Just seems like a lot of file overhead in my opinion.

    Anyway - ANY suggestions would be good! Thanks folks!



  • Create 2 images in your image provider (pointing to the same image data) and cycle between them in the QML Image:Source. Perhaps use a signal to trigger this change in QML when the image is updated. I believe its a caching thing on the QML side that prevents it re-fetching the data.

    There may be more appropriate ways of doing this.



  • @fcarney said in Dynamically update QML Image:

    Create 2 images in your image provider (pointing to the same image data) and cycle between them in the QML Image:Source. Perhaps use a signal to trigger this change in QML when the image is updated. I believe its a caching thing on the QML side that prevents it re-fetching the data.

    There may be more appropriate ways of doing this.

    This is kind of what I have been doing now.
    What I have been doing is that every time a new image is generated, I have it write to a file and then a signal is called to set the property of the image such as...

    img.setProperty("source",new_img_src)
    

    and it just cycles through a buffer of 3 images.
    Is there any better way to do this without needing to use the filesystem?
    I'd like to read in bytes directly from memory and apply it right to the QML image dynamically every second. So far I don't see a good solution to do this?



  • Where, when are you writing to file? I did the image provider trick completely in memory. You just have to provide a QImage object. It can be completely in memory. I did this in C++, and unfortunately I don't have the code on hand.

    I think this:

    Image{
        id: img
        objectName: "img"
        anchors.fill:parent
        source: "image://myprovider/test.png"
        }
    

    Is all in memory. The test.png is just an identifier for the source. Unless you are doing something different somewhere else.



  • @fcarney said in Dynamically update QML Image:

    Where, when are you writing to file? I did the image provider trick completely in memory. You just have to provide a QImage object. It can be completely in memory. I did this in C++, and unfortunately I don't have the code on hand.

    I think this:

    Image{
        id: img
        objectName: "img"
        anchors.fill:parent
        source: "image://myprovider/test.png"
        }
    

    Is all in memory. The test.png is just an identifier for the source. Unless you are doing something different somewhere else.

    Thank you for clearing that up. I scrolled back up to your previous reply and re-read it. So if I created say 2 empty QImages (or maybe an array of 3 to create some kind of generated image buffer). My ImageProvider class would need to have these QImages within the requestImage function? I'm not entirely sure how I would implement a QImage to a ImageProvider and have it update. I understand the singals/slots concept though. I use it all the time. Not entirely sure about just how to get this image provider working dynamically.



  • When you are setting the source to: "image://myprovider/test.png" you are just telling the imageprovider to give you the image represented by test.png. So what I did was create 2 images in my image provider that pointed to the same data. When I updated the data in the image provider then a new request would get the data. However, Image will not request the new data unless the source string changes. I then setup a timer to change the source string to a different name that still pointed to the same data in the imageprovider.

    I only suggested a messaging scheme to make this maybe more efficient and change only when the image changes. The way I did is definitely hackish and might not be the best way, but I did get it to work.

    It looks like your imageprovider is ignoring p_str which is the "test.png" string. So just changing the "source" string in the Image component in QML should make it update whenever that string changes.


  • Moderators

    hi @Bits-n-Flips
    maybe you're making that more comlicated than it needs to be.

    do you emit a signal (from your image provider class) when the image is changed ?

    if yes, than you can do the following, inside your qml file (excerpt from a project/challenge of mine)

    Image {
            id: img
            property bool counter: false
    
            asynchronous: false
            source: "image://live/image"
            anchors.fill: parent
            fillMode: Image.PreserveAspectFit
            cache: false
    
            function reload() {
                counter = !counter
                source = "image://live/image?id=" + counter
            }
    
            Connections{
                target: liveImageProvider
                onImageChanged: img.reload()
           }
        }
    


  • @J-Hilk said in Dynamically update QML Image:

    hi @Bits-n-Flips
    maybe you're making that more comlicated than it needs to be.

    do you emit a signal (from your image provider class) when the image is changed ?

    if yes, than you can do the following, inside your qml file (excerpt from a project/challenge of mine)

    Image {
            id: img
            property bool counter: false
    
            asynchronous: false
            source: "image://live/image"
            anchors.fill: parent
            fillMode: Image.PreserveAspectFit
            cache: false
    
            function reload() {
                counter = !counter
                source = "image://live/image?id=" + counter
            }
    
            Connections{
                target: liveImageProvider
                onImageChanged: img.reload()
           }
        }
    

    @J-Hilk, I think that is missing initialization of counter. Also that seems a bit hacky (thought the cleanest solution I have seen so far). Is it not possible to just do onImageChanged: img.sourceChanged(); ?


  • Moderators

    Hi @Puya and welcome,

    I think that is missing initialization of counter

    not really QML doesn't really do uninitialized. The best you can have is that type casting fails and you get a var = undefined

    that said this line

    property bool counter: false

    defines counter to be false after Component.onCompletion

    Is it not possible to just do onImageChanged: img.sourceChanged(); ?

    I don't think so. From a quick look into the source code, I would say, you have to call the virtual load function of QQuickImage which is protected.
    So the only way to do it from the outside is to change the source or derive your own class from it and expose the function.


Log in to reply