Search code examples
pythonpython-3.xpyqtpyqt5pyqtgraph

Pyqtgraph ImageView not updating after QSlider value changed


I'm working on a PyQt5 + pyqtgraph user interface. The main idea is to have an image viewer that I can update using widgets from the Main Window, like sliders, line edits, buttons... By now I'm having trouble updating the image when I move the slicer. It connects to the expected functions, but the image on the ImageViewer keeps always the same (the first one). How can I solve this? I know that pyqtgraph ImageViewer can deal with 3D arrays, but in the future, I will have to add more than one viewer that will be updated by the same slider. This is the code I have:

This HDF5 file that I'm using is a (2048, 2048, 10) size array (10 uint16 images inside).

from PyQt5 import QtGui, QtWidgets, uic
import pyqtgraph as pg
import numpy as np
import os, sys, h5py

class ZoomGUI:
    def __init__(self, parent=None):
        self.ui = uic.loadUi(os.path.dirname(os.path.realpath(__file__)) + '/GUI/ZoomGUI.ui')

        self.file_path = "recon/tomo-2048x2048x10_16bit.h5"

        #Configure slices slicer
        self.number_of_slices = self.get_number_of_slices()
        self.ui.slicesSlider.setMaximum(self.number_of_slices-1)
        self.ui.slicesSlider.valueChanged.connect(self.imageView)
        self.updateImage()

        #Add image widget
        self.ui.ImageWidget.setLayout(QtGui.QVBoxLayout())
        self.ui.ImageWidget.layout().addWidget(self.imv)


    def imageView(self,slice_number):
        self.imv = pg.ImageView()
        self.imagedata = self.get_image(slice_number)
        print(self.imagedata)
        self.imv.setImage(self.imagedata)

    def updateImage(self):
        actual_slice = int(self.ui.slicesSlider.value())
        self.imageView(actual_slice)

    #File related functions
    def get_image(self, slice_number):
        h5 = h5py.File(self.file_path, "r")
        data = h5["slices"][slice_number]
        h5.close()
        return(data)

    def get_number_of_slices(self):
        h5 = h5py.File(self.file_path, "r")
        size = len(h5["slices"])
        h5.close()
        return(size)

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)      
    ZoomGUI = ZoomGUI()               
    ZoomGUI.ui.show()                  
    app.exec_()

This is the .ui file. It only has the slider and a QWidget widget.

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1132</width>
    <height>880</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout_2">
    <item row="0" column="0">
     <layout class="QGridLayout" name="gridLayout">
      <item row="0" column="0">
       <layout class="QGridLayout" name="ImageWidgetLayout">
        <item row="0" column="0">
         <widget class="QWidget" name="ImageWidget" native="true"/>
        </item>
       </layout>
      </item>
      <item row="2" column="0">
       <widget class="QPushButton" name="pushButton">
        <property name="text">
         <string>PushButton</string>
        </property>
       </widget>
      </item>
      <item row="1" column="0">
       <widget class="QSlider" name="slicesSlider">
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>1132</width>
     <height>26</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

Solution

  • You are creating a new ImageView that has not been added to the window so you do not see the change, the solution is to create the ImageView only once and then reuse it

    class ZoomGUI:
        def __init__(self, parent=None):
            self.ui = uic.loadUi(
                os.path.dirname(os.path.realpath(__file__)) + "/GUI/ZoomGUI.ui"
            )
    
            self.file_path = "recon/tomo-2048x2048x10_16bit.h5"
    
            # Add image widget
            lay = QtGui.QVBoxLayout(self.ui.ImageWidget)
            self.imv = pg.ImageView()
            lay.addWidget(self.imv)
    
            # Configure slices slicer
            self.number_of_slices = self.get_number_of_slices()
            self.ui.slicesSlider.setMaximum(self.number_of_slices - 1)
            self.ui.slicesSlider.valueChanged.connect(self.imageView)
    
            self.imageView(self.ui.slicesSlider.value())
    
        def imageView(self, slice_number):
    
            imagedata = self.get_image(slice_number)
            print(imagedata)
            self.imv.setImage(imagedata)
    
        def get_image(self, slice_number):
            h5 = h5py.File(self.file_path, "r")
            data = h5["slices"][slice_number]
            h5.close()
            return data
    
        def get_number_of_slices(self):
            h5 = h5py.File(self.file_path, "r")
            size = len(h5["slices"])
            h5.close()
            return size