Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Dynamically update QML Image
Forum Updated to NodeBB v4.3 + New Features

Dynamically update QML Image

Scheduled Pinned Locked Moved Unsolved General and Desktop
13 Posts 5 Posters 9.2k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    Bits-n-Flips
    wrote on last edited by
    #1

    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!

    1 Reply Last reply
    0
    • fcarneyF Offline
      fcarneyF Offline
      fcarney
      wrote on last edited by
      #2

      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.

      C++ is a perfectly valid school of magic.

      B 1 Reply Last reply
      0
      • fcarneyF fcarney

        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.

        B Offline
        B Offline
        Bits-n-Flips
        wrote on last edited by
        #3

        @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?

        1 Reply Last reply
        0
        • fcarneyF Offline
          fcarneyF Offline
          fcarney
          wrote on last edited by
          #4

          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.

          C++ is a perfectly valid school of magic.

          B 1 Reply Last reply
          0
          • fcarneyF fcarney

            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.

            B Offline
            B Offline
            Bits-n-Flips
            wrote on last edited by
            #5

            @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.

            J.HilkJ 1 Reply Last reply
            0
            • fcarneyF Offline
              fcarneyF Offline
              fcarney
              wrote on last edited by fcarney
              #6

              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.

              C++ is a perfectly valid school of magic.

              1 Reply Last reply
              0
              • B Bits-n-Flips

                @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.

                J.HilkJ Offline
                J.HilkJ Offline
                J.Hilk
                Moderators
                wrote on last edited by
                #7

                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()
                       }
                    }
                

                Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                Q: What's that?
                A: It's blue light.
                Q: What does it do?
                A: It turns blue.

                P 1 Reply Last reply
                0
                • J.HilkJ J.Hilk

                  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()
                         }
                      }
                  
                  P Offline
                  P Offline
                  Puya
                  wrote on last edited by
                  #8

                  @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(); ?

                  J.HilkJ 1 Reply Last reply
                  0
                  • P Puya

                    @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(); ?

                    J.HilkJ Offline
                    J.HilkJ Offline
                    J.Hilk
                    Moderators
                    wrote on last edited by
                    #9

                    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.


                    Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                    Q: What's that?
                    A: It's blue light.
                    Q: What does it do?
                    A: It turns blue.

                    L 2 Replies Last reply
                    1
                    • J.HilkJ J.Hilk

                      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.

                      L Offline
                      L Offline
                      LS-KS
                      wrote on last edited by
                      #10
                      This post is deleted!
                      1 Reply Last reply
                      0
                      • J.HilkJ J.Hilk

                        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.

                        L Offline
                        L Offline
                        LS-KS
                        wrote on last edited by
                        #11

                        @J-Hilk I found this thread by googling. I need to update three images by button or when a Dialog is opened.
                        the source is calculated by row /col of my tablemodel.

                        The ''bool trick' only works only two times:

                        • first the url ends with 'false'
                        • second time the url end with 'true'

                        I had the idea to change he counter to an int because i already ignore the value in mey requestImage(). But this worked only once when counter is 0.

                        Here are some snippets:

                        Column{
                                        leftPadding: 5
                                        Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
                                        id: slotB
                                        Text{
                                            id: slotBtext
                                            text: "Slot B: Detected ID: "+ main_rect.slotid_b
                                        }
                                        Rectangle{
                                            width: main_rect.width/2 -15
                                            height: 125
                                            radius: 5
                                            border.width: 2
                                            border.color: 'black'
                                            Image{
                                                id: slotBImage
                                                property int counter: 0
                                                anchors.fill: parent
                                                fillMode: Image.PreserveAspectFit
                                                cache: false
                        
                                            }
                                        }
                                    }
                        

                        loadImages()

                            function loadImages(){
                                //
                        
                                slotAImage.counter += slotAImage.counter
                                let a_sourcestring = "image://stockimage/"+ main_rect.row + "_" + main_rect.col +"_A.png" + slotAImage.counter
                                slotAImage.counter += slotBImage.counter
                                let b_sourcestring = "image://stockimage/"+ main_rect.row + "_" + main_rect.col +"_B.png" + slotBImage.counter
                                slotAImage.counter += palletImage.counter
                                let pallet_string = "image://stockimage/" + main_rect.row + "_" + main_rect.col +"_Pallet.png" + slotBImage.counter
                                slotAImage.source = a_sourcestring
                                slotBImage.source = b_sourcestring
                                palletImage.source = pallet_string
                            }
                        

                        Any idea how to get this working?

                        J.HilkJ 1 Reply Last reply
                        0
                        • L LS-KS

                          @J-Hilk I found this thread by googling. I need to update three images by button or when a Dialog is opened.
                          the source is calculated by row /col of my tablemodel.

                          The ''bool trick' only works only two times:

                          • first the url ends with 'false'
                          • second time the url end with 'true'

                          I had the idea to change he counter to an int because i already ignore the value in mey requestImage(). But this worked only once when counter is 0.

                          Here are some snippets:

                          Column{
                                          leftPadding: 5
                                          Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
                                          id: slotB
                                          Text{
                                              id: slotBtext
                                              text: "Slot B: Detected ID: "+ main_rect.slotid_b
                                          }
                                          Rectangle{
                                              width: main_rect.width/2 -15
                                              height: 125
                                              radius: 5
                                              border.width: 2
                                              border.color: 'black'
                                              Image{
                                                  id: slotBImage
                                                  property int counter: 0
                                                  anchors.fill: parent
                                                  fillMode: Image.PreserveAspectFit
                                                  cache: false
                          
                                              }
                                          }
                                      }
                          

                          loadImages()

                              function loadImages(){
                                  //
                          
                                  slotAImage.counter += slotAImage.counter
                                  let a_sourcestring = "image://stockimage/"+ main_rect.row + "_" + main_rect.col +"_A.png" + slotAImage.counter
                                  slotAImage.counter += slotBImage.counter
                                  let b_sourcestring = "image://stockimage/"+ main_rect.row + "_" + main_rect.col +"_B.png" + slotBImage.counter
                                  slotAImage.counter += palletImage.counter
                                  let pallet_string = "image://stockimage/" + main_rect.row + "_" + main_rect.col +"_Pallet.png" + slotBImage.counter
                                  slotAImage.source = a_sourcestring
                                  slotBImage.source = b_sourcestring
                                  palletImage.source = pallet_string
                              }
                          

                          Any idea how to get this working?

                          J.HilkJ Offline
                          J.HilkJ Offline
                          J.Hilk
                          Moderators
                          wrote on last edited by
                          #12

                          hi @LS-KS and welcome

                          It's been a while since I last dealed with this!

                          You never increase slotBImage.counter but only ever add to slotAImage.counter is that intended ?

                          If you wan't a working example, I have this old project of mine:

                          https://github.com/DeiVadder/Sandepile-Challenge-QML.git

                          as you can see the c++ function takes the counter as String so, there's no reason for it not also working with a real counter.

                          But, IIRC, you'll need a different ID each time or the QML component doesn't update, but continues to show the cached Image rather than fetching the data anew from qml


                          Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


                          Q: What's that?
                          A: It's blue light.
                          Q: What does it do?
                          A: It turns blue.

                          L 1 Reply Last reply
                          1
                          • J.HilkJ J.Hilk

                            hi @LS-KS and welcome

                            It's been a while since I last dealed with this!

                            You never increase slotBImage.counter but only ever add to slotAImage.counter is that intended ?

                            If you wan't a working example, I have this old project of mine:

                            https://github.com/DeiVadder/Sandepile-Challenge-QML.git

                            as you can see the c++ function takes the counter as String so, there's no reason for it not also working with a real counter.

                            But, IIRC, you'll need a different ID each time or the QML component doesn't update, but continues to show the cached Image rather than fetching the data anew from qml

                            L Offline
                            L Offline
                            LS-KS
                            wrote on last edited by LS-KS
                            #13

                            @J-Hilk Thank you!
                            You are absolutely right.
                            I was a bit fast to change the property to int. Here is the refactored and working function:

                            function loadImages(){
                                    slotAImage.counter += 1
                                    let a_sourcestring = "image://stockimage/"+ main_rect.row + "_" + main_rect.col +"_A.png" + slotAImage.counter
                                    slotBImage.counter += 1
                                    let b_sourcestring = "image://stockimage/"+ main_rect.row + "_" + main_rect.col +"_B.png" + slotBImage.counter
                                    palletImage.counter += 1
                                    let pallet_string = "image://stockimage/" + main_rect.row + "_" + main_rect.col +"_Pallet.png" + slotBImage.counter
                                    slotAImage.source = a_sourcestring
                                    slotBImage.source = b_sourcestring
                                    palletImage.source = pallet_string
                                }
                            

                            First issue was the cpoy/paste error of the counters. second issue was not to increase by 1 or anything else. So the counter would always be 0.

                            But i can confirm that it wont work if the property is bool, something like that:

                                function loadImages(){
                                    slotAImage.counter = !slotAImage.counter 
                                    let a_sourcestring = "image://stockimage/"+ main_rect.row + "_" + main_rect.col +"_A.png" + slotAImage.counter
                                    slotBImage.counter = !slotBImage.counter
                                    let b_sourcestring = "image://stockimage/"+ main_rect.row + "_" + main_rect.col +"_B.png" + slotBImage.counter
                                    palletImage.counter = !palletImage.counter 
                                    let pallet_string = "image://stockimage/" + main_rect.row + "_" + main_rect.col +"_Pallet.png" + slotBImage.counter
                                    slotAImage.source = a_sourcestring
                                    slotBImage.source = b_sourcestring
                                    palletImage.source = pallet_string
                                }
                            

                            As described it works two times. Is it possible that all source strings get stored somehow? Actually i have nt much time to spend on this but i looked for a possibility to purge the Image from the QML Type -without success yet.

                            1 Reply Last reply
                            0

                            • Login

                            • Login or register to search.
                            • First post
                              Last post
                            0
                            • Categories
                            • Recent
                            • Tags
                            • Popular
                            • Users
                            • Groups
                            • Search
                            • Get Qt Extensions
                            • Unsolved