Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Image not updating in QGraphicsScene

Image not updating in QGraphicsScene

Scheduled Pinned Locked Moved Solved Qt for Python
7 Posts 3 Posters 1.4k Views 2 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.
  • T Offline
    T Offline
    TommasoFurieriDO
    wrote on last edited by
    #1

    Good morning and thanks you all for your precious work here.

    I'm struggling to make an image update in a QGraphicsScene.

    In particular i would like to create two windows, the second one that opens when a button is pushed on the main window. I managed to do that, but what happened is that in the second window i would like to display an image (more or less like a live stream, but at a slow framerate) and in future I plan to add more graphical items, like lines and so on.

    Here is a minimum reproducible example of the problem i'm facing:

    import sys
    
    import numpy as np
    
    from PySide6.QtWidgets import QApplication, QGraphicsPixmapItem, QMainWindow, QVBoxLayout, QWidget, QGraphicsView, QGraphicsScene
    from PySide6.QtGui import QPixmap, QImage
    from PySide6.QtCore import QTimer
    
    def convert_16bit_to_8bit_for_display(imageArray, min_val, max_val):
        image_clipped = np.clip(imageArray, min_val, max_val) - min_val
        image_scaled = ((image_clipped / (max_val - min_val)) * 255.0).astype(np.uint8)
        return image_scaled
    
    class imageVisualizationWindow(QMainWindow):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.setWindowTitle("Image Viewer")
            self.initImage()
            self.updatingTimer = QTimer()
            self.updatingTimer.timeout.connect(self.updateImage)
            self.generateLayout()
    
    
        def initImage(self):
            self.imageArray = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
            self.imageArray8b = convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
            self.view = QGraphicsView()
            self.scene = QGraphicsScene()
            self.qImage = QImage(self.imageArray8b.data, self.imageArray8b.shape[1], self.imageArray8b.shape[0], QImage.Format_Grayscale8)
            self.pixmapItem = self.scene.addPixmap(QPixmap.fromImage(self.qImage))
            self.view.setScene(self.scene)
    
    
        def updateImage(self):
            newImage = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
            self.imageArray = newImage.copy()
            self.updateImageDisplay()
    
    
        def updateImageDisplay(self):
            self.imageArray8b = convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
            self.qImage = QImage(self.imageArray8b.data, self.imageArray8b.shape[1], self.imageArray8b.shape[0], QImage.Format_Grayscale8)
            self.pixmapItem.setPixmap(QPixmap.fromImage(self.qImage))
            self.scene.update()
    
        
        def generateLayout(self):
            imageLayout = QVBoxLayout()
            imageLayout.addWidget(self.view)
            centralWidget = QWidget()
            centralWidget.setLayout(imageLayout)
            self.setCentralWidget(centralWidget)
    
    
    class MainWin(QMainWindow):
        def __init__(self):
            super().__init__()
    
            self.setWindowTitle("Main WIndow")
    
            ######################################################
            # Generate the image visualization window
            ######################################################
    
            self.imageVisualizationWindow = imageVisualizationWindow(self)
            self.imageVisualizationWindow.initImage()
            self.imageVisualizationWindow.updatingTimer.start(500)
            self.imageVisualizationWindow.show()
            
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
    
        window = MainWin()
        window.show()
        sys.exit(app.exec())
    

    Here i checked that the timer calls the updateImage() that calls updateImageDisplay(), and i checked that a new image is generated at each call, but the image does not update on the window.

    The strage thing is that the same logic works here:

    import sys
    import numpy as np
    from PySide6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QPushButton, QVBoxLayout, QWidget
    from PySide6.QtGui import QPixmap, QImage
    from PySide6.QtCore import QTimer
    
    def create_noisy_image(width, height):
        # Create a random noisy image
        noisy_image = np.random.randint(0, 256, (height, width), dtype=np.uint8)
        return noisy_image
    
    def update_pixmap_and_scene(pixmap_item, scene, new_image):
        # Convert the numpy array to QImage
        qimage = QImage(new_image.data, new_image.shape[1], new_image.shape[0], QImage.Format_Grayscale8)
        # Update the pixmap item with the new image
        pixmap_item.setPixmap(QPixmap.fromImage(qimage))
        # Update the scene
        scene.update()
    
    def change_image():
        new_noisy_image = create_noisy_image(width, height)
        update_pixmap_and_scene(pixmap_item, scene, new_noisy_image)
    
    if __name__ == "__main__":
        # Create the Qt Application
        app = QApplication(sys.argv)
    
        # Create a QWidget
        widget = QWidget()
    
        # Create a QVBoxLayout
        layout = QVBoxLayout(widget)
    
        # Create a QGraphicsView
        view = QGraphicsView()
    
        # Create a QGraphicsScene
        scene = QGraphicsScene()
    
        # Create a noisy image
        width = 100
        height = 100
        noisy_image = create_noisy_image(width, height)
    
        # Convert the numpy array to QImage
        qimage = QImage(noisy_image.data, noisy_image.shape[1], noisy_image.shape[0], QImage.Format_Grayscale8)
    
        # Create a QGraphicsPixmapItem with the QImage
        pixmap_item = scene.addPixmap(QPixmap.fromImage(qimage))
    
        # Set the scene for the QGraphicsView
        view.setScene(scene)
    
        # Add the QGraphicsView to the layout
        layout.addWidget(view)
    
        # Create a button to change the image
        button = QPushButton("Change Image")
        button.clicked.connect(change_image)
    
        # Add the button to the layout
        layout.addWidget(button)
    
        # Set the layout for the widget
        widget.setLayout(layout)
    
        # Show the widget
        widget.show()
    
        timer = QTimer()
        timer.timeout.connect(change_image)
        timer.start(500)
    
        # Execute the application
        sys.exit(app.exec())
    

    Can somebody try to help me finding where i did something wrong?

    Thanks to all in advance

    1 Reply Last reply
    1
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi and welcome to devnet,

      Not having a computer at hand right now to verify, let me just make some suggestions:

      • your central widget only exist during ˋgenerateLayout` so I am not sure it is not getting garbage collected
      • MainWin is currently relatively useless as it just contains your other widget. You could directly create your secondary widget in the main function.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      T 1 Reply Last reply
      0
      • SGaistS SGaist

        Hi and welcome to devnet,

        Not having a computer at hand right now to verify, let me just make some suggestions:

        • your central widget only exist during ˋgenerateLayout` so I am not sure it is not getting garbage collected
        • MainWin is currently relatively useless as it just contains your other widget. You could directly create your secondary widget in the main function.
        T Offline
        T Offline
        TommasoFurieriDO
        wrote on last edited by
        #3

        @SGaist
        Thanks for your feedback.

        Based on your suggestions i tried to reduce the example even more (avoiding nesting the windows creation) and making the central widget a property of the class:

        import sys
        
        import numpy as np
        
        from PySide6.QtWidgets import QApplication, QGraphicsPixmapItem, QMainWindow, QVBoxLayout, QWidget, QGraphicsView, QGraphicsScene
        from PySide6.QtGui import QPixmap, QImage
        from PySide6.QtCore import QTimer
        
        def convert_16bit_to_8bit_for_display(imageArray, min_val, max_val):
            image_clipped = np.clip(imageArray, min_val, max_val) - min_val
            image_scaled = ((image_clipped / (max_val - min_val)) * 255.0).astype(np.uint8)
            return image_scaled
        
        class imageVisualizationWindow(QMainWindow):
            def __init__(self, parent=None):
                super().__init__(parent)
                self.setWindowTitle("Image Viewer")
                self.centralWidget = QWidget()
                self.imageLayout = QVBoxLayout()
                self.initImage()
                self.updatingTimer = QTimer()
                self.updatingTimer.timeout.connect(self.updateImage)
                self.generateLayout()
        
        
            def initImage(self):
                self.imageArray = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
                self.imageArray8b = convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
                self.view = QGraphicsView()
                self.scene = QGraphicsScene()
                self.qImage = QImage(self.imageArray8b.data, self.imageArray8b.shape[1], self.imageArray8b.shape[0], QImage.Format_Grayscale8)
                self.pixmapItem = self.scene.addPixmap(QPixmap.fromImage(self.qImage))
                self.view.setScene(self.scene)
        
        
            def updateImage(self):
                newImage = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
                self.imageArray = newImage.copy()
                self.updateImageDisplay()
        
        
            def updateImageDisplay(self):
                self.imageArray8b = convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
                self.qImage = QImage(self.imageArray8b.data, self.imageArray8b.shape[1], self.imageArray8b.shape[0], QImage.Format_Grayscale8)
                self.pixmapItem.setPixmap(QPixmap.fromImage(self.qImage))
                self.scene.update()
        
            
            def generateLayout(self):
                self.imageLayout.addWidget(self.view)
                self.centralWidget.setLayout(self.imageLayout)
                self.setCentralWidget(self.centralWidget)
               
        
        if __name__ == "__main__":
            app = QApplication(sys.argv)
        
            imageVisualizationWindow = imageVisualizationWindow()
            imageVisualizationWindow.initImage()
            imageVisualizationWindow.updatingTimer.start(500)
            imageVisualizationWindow.show()
            
            sys.exit(app.exec())
        

        But still it does not update the image.

        T 1 Reply Last reply
        0
        • T TommasoFurieriDO

          @SGaist
          Thanks for your feedback.

          Based on your suggestions i tried to reduce the example even more (avoiding nesting the windows creation) and making the central widget a property of the class:

          import sys
          
          import numpy as np
          
          from PySide6.QtWidgets import QApplication, QGraphicsPixmapItem, QMainWindow, QVBoxLayout, QWidget, QGraphicsView, QGraphicsScene
          from PySide6.QtGui import QPixmap, QImage
          from PySide6.QtCore import QTimer
          
          def convert_16bit_to_8bit_for_display(imageArray, min_val, max_val):
              image_clipped = np.clip(imageArray, min_val, max_val) - min_val
              image_scaled = ((image_clipped / (max_val - min_val)) * 255.0).astype(np.uint8)
              return image_scaled
          
          class imageVisualizationWindow(QMainWindow):
              def __init__(self, parent=None):
                  super().__init__(parent)
                  self.setWindowTitle("Image Viewer")
                  self.centralWidget = QWidget()
                  self.imageLayout = QVBoxLayout()
                  self.initImage()
                  self.updatingTimer = QTimer()
                  self.updatingTimer.timeout.connect(self.updateImage)
                  self.generateLayout()
          
          
              def initImage(self):
                  self.imageArray = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
                  self.imageArray8b = convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
                  self.view = QGraphicsView()
                  self.scene = QGraphicsScene()
                  self.qImage = QImage(self.imageArray8b.data, self.imageArray8b.shape[1], self.imageArray8b.shape[0], QImage.Format_Grayscale8)
                  self.pixmapItem = self.scene.addPixmap(QPixmap.fromImage(self.qImage))
                  self.view.setScene(self.scene)
          
          
              def updateImage(self):
                  newImage = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
                  self.imageArray = newImage.copy()
                  self.updateImageDisplay()
          
          
              def updateImageDisplay(self):
                  self.imageArray8b = convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
                  self.qImage = QImage(self.imageArray8b.data, self.imageArray8b.shape[1], self.imageArray8b.shape[0], QImage.Format_Grayscale8)
                  self.pixmapItem.setPixmap(QPixmap.fromImage(self.qImage))
                  self.scene.update()
          
              
              def generateLayout(self):
                  self.imageLayout.addWidget(self.view)
                  self.centralWidget.setLayout(self.imageLayout)
                  self.setCentralWidget(self.centralWidget)
                 
          
          if __name__ == "__main__":
              app = QApplication(sys.argv)
          
              imageVisualizationWindow = imageVisualizationWindow()
              imageVisualizationWindow.initImage()
              imageVisualizationWindow.updatingTimer.start(500)
              imageVisualizationWindow.show()
              
              sys.exit(app.exec())
          

          But still it does not update the image.

          T Offline
          T Offline
          TommasoFurieriDO
          wrote on last edited by
          #4

          I managed to make it work. But i don't understand why the other system was not working.

          import sys
          import numpy as np
          from PySide6.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QVBoxLayout, QWidget, QPushButton
          from PySide6.QtGui import QPixmap, QImage
          from PySide6.QtCore import QTimer
          
          class ImageVisualizationWindow(QMainWindow):
              def __init__(self, parent=None):
                  super().__init__(parent)
                  self.setWindowTitle("Image Viewer")
                  self.centralWidget = QWidget()
                  self.setCentralWidget(self.centralWidget)
                  self.layout = QVBoxLayout(self.centralWidget)
                  self.view = QGraphicsView()
                  self.scene = QGraphicsScene()
                  self.view.setScene(self.scene)
                  self.layout.addWidget(self.view)
                  self.button = QPushButton("Change Image")
                  self.layout.addWidget(self.button)
                  self.button.clicked.connect(self.updateImage)
                  self.updatingTimer = QTimer()
                  self.updatingTimer.timeout.connect(self.updateImage)
                  self.updatingTimer.start(500)
                  self.initImage()
          
          
              def initImage(self):
                  self.imageArray = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
                  image8b = self.convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
                  self.qImage = QImage(image8b.data, image8b.shape[1], image8b.shape[0], QImage.Format_Grayscale8)
                  self.pixmap_item = self.scene.addPixmap(QPixmap.fromImage(self.qImage))
          
              def updateImage(self):
                  newImage = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
                  self.imageArray = newImage.copy()
                  self.updateImageDisplay()
          
              def updateImageDisplay(self):
                  image8b = self.convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
                  qImage = QImage(image8b.data, image8b.shape[1], image8b.shape[0], QImage.Format_Grayscale8)
                  self.pixmap_item.setPixmap(QPixmap.fromImage(qImage))
          
              def convert_16bit_to_8bit_for_display(self, imageArray, min_val, max_val):
                  image_clipped = np.clip(imageArray, min_val, max_val) - min_val
                  image_scaled = ((image_clipped / (max_val - min_val)) * 255.0).astype(np.uint8)
                  return image_scaled
          
          if __name__ == "__main__":
              app = QApplication(sys.argv)
              window = ImageVisualizationWindow()
              window.show()
              sys.exit(app.exec())
          

          here the trick was to get the pixmapItem when adding it from the scene

          self.pixmap_item = self.scene.addPixmap(QPixmap.fromImage(self.qImage))
          

          and then directly updating this pixmapItem in the updateImageDisplay()

          self.pixmap_item.setPixmap(QPixmap.fromImage(qImage))
          
          JonBJ 1 Reply Last reply
          0
          • T TommasoFurieriDO

            I managed to make it work. But i don't understand why the other system was not working.

            import sys
            import numpy as np
            from PySide6.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QVBoxLayout, QWidget, QPushButton
            from PySide6.QtGui import QPixmap, QImage
            from PySide6.QtCore import QTimer
            
            class ImageVisualizationWindow(QMainWindow):
                def __init__(self, parent=None):
                    super().__init__(parent)
                    self.setWindowTitle("Image Viewer")
                    self.centralWidget = QWidget()
                    self.setCentralWidget(self.centralWidget)
                    self.layout = QVBoxLayout(self.centralWidget)
                    self.view = QGraphicsView()
                    self.scene = QGraphicsScene()
                    self.view.setScene(self.scene)
                    self.layout.addWidget(self.view)
                    self.button = QPushButton("Change Image")
                    self.layout.addWidget(self.button)
                    self.button.clicked.connect(self.updateImage)
                    self.updatingTimer = QTimer()
                    self.updatingTimer.timeout.connect(self.updateImage)
                    self.updatingTimer.start(500)
                    self.initImage()
            
            
                def initImage(self):
                    self.imageArray = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
                    image8b = self.convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
                    self.qImage = QImage(image8b.data, image8b.shape[1], image8b.shape[0], QImage.Format_Grayscale8)
                    self.pixmap_item = self.scene.addPixmap(QPixmap.fromImage(self.qImage))
            
                def updateImage(self):
                    newImage = np.random.randint(0, 16384, (256, 256), dtype=np.uint16)
                    self.imageArray = newImage.copy()
                    self.updateImageDisplay()
            
                def updateImageDisplay(self):
                    image8b = self.convert_16bit_to_8bit_for_display(self.imageArray, 0, 16384)
                    qImage = QImage(image8b.data, image8b.shape[1], image8b.shape[0], QImage.Format_Grayscale8)
                    self.pixmap_item.setPixmap(QPixmap.fromImage(qImage))
            
                def convert_16bit_to_8bit_for_display(self, imageArray, min_val, max_val):
                    image_clipped = np.clip(imageArray, min_val, max_val) - min_val
                    image_scaled = ((image_clipped / (max_val - min_val)) * 255.0).astype(np.uint8)
                    return image_scaled
            
            if __name__ == "__main__":
                app = QApplication(sys.argv)
                window = ImageVisualizationWindow()
                window.show()
                sys.exit(app.exec())
            

            here the trick was to get the pixmapItem when adding it from the scene

            self.pixmap_item = self.scene.addPixmap(QPixmap.fromImage(self.qImage))
            

            and then directly updating this pixmapItem in the updateImageDisplay()

            self.pixmap_item.setPixmap(QPixmap.fromImage(qImage))
            
            JonBJ Offline
            JonBJ Offline
            JonB
            wrote on last edited by JonB
            #5

            @TommasoFurieriDO said in Image not updating in QGraphicsScene:

            I managed to make it work. But i don't understand why the other system was not working.

            It has taken me hours of painstaking edits to spot why this is! :(
            In your "original", non-working you have:

            class imageVisualizationWindow(QMainWindow):
                def __init__(self, parent=None):
                    self.initImage()
            
            ...
            if __name__ == "__main__":
                imageVisualizationWindow.initImage()
            

            That is two calls to initImage()! So a separate set of view/scene/image etc. are created! Hence the behaviour, things are not what you think they are....

            In the "latest", working you have just the one call to initImage()....

            T 1 Reply Last reply
            3
            • JonBJ JonB

              @TommasoFurieriDO said in Image not updating in QGraphicsScene:

              I managed to make it work. But i don't understand why the other system was not working.

              It has taken me hours of painstaking edits to spot why this is! :(
              In your "original", non-working you have:

              class imageVisualizationWindow(QMainWindow):
                  def __init__(self, parent=None):
                      self.initImage()
              
              ...
              if __name__ == "__main__":
                  imageVisualizationWindow.initImage()
              

              That is two calls to initImage()! So a separate set of view/scene/image etc. are created! Hence the behaviour, things are not what you think they are....

              In the "latest", working you have just the one call to initImage()....

              T Offline
              T Offline
              TommasoFurieriDO
              wrote on last edited by
              #6

              @JonB Oh God.... i feel so stupid! Thanks for your effort! I feel i can close it!

              JonBJ 1 Reply Last reply
              0
              • T TommasoFurieriDO has marked this topic as solved on
              • T TommasoFurieriDO

                @JonB Oh God.... i feel so stupid! Thanks for your effort! I feel i can close it!

                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by JonB
                #7

                @TommasoFurieriDO
                I could not see it for looking. Working the "bad" one towards the "good" one, till it went from not working to working suddenly. I don't know how you came up with the order in which you create/initialise things, and you obviously altered it in the later incarnation (and I think it's much better now), but it was a bit hard to follow. I usually put all the widget creations in the contructor (or perhaps a setupUi()), yours was so "dotted around" neither of us spotted this! :)

                1 Reply Last reply
                1

                • Login

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