pythonpyqt5python-multithreadingsignals-slotsqimage

Use Signal to pass QImage from a Worker Thread to Main UI Thread to Display in pyqt5 GUI (Python)


I'm trying to define my own signal to display QImage (converted from raw data) upon a button press. The GUI has a button called as connect, and a QLabel that's named as DisplayRect. I'd like to draw this QImage on DisplayRect. The code below prints 'signal received' upon pressing the connect button. I'd like to make it pass QImage so that I can self.DisplayRect.setPixmap(the passed QImage ) Please have a look:

class DPSignal(QObject):
    dpsig = pyqtSignal()
    
    def run(self):
        self.dpsig.emit()
        
        
        
class DisplayThread(QThread):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        
        
    def run(self):
        dps = DPSignal()
        dps.dpsig.connect(self.parent.dpupdate)
        dps.run
        
class WindowClass(QMainWindow, form_class):
    def __init__(self):
        super().__init__()
        self.setupUI(self)
        
       self.connectButton.clicked.connect(self.connectFunction)
       
       
       
       
       
    @pyqtSlot()
    def connectFunction(self):
        dpThread = DisplayThread(self)
        dpThread.start()
        
    def dpupdate(self):
        print("signal received")
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    myWindow = WindowClass()
    myWindow.show()
    app.exec_()

This piece of code below reads binary data from a file and I'd like to add this code to somewhere in DisplayThread so that it passes the processed QImage to the main UI thread. Then main UI thread can (I suppose) self.DisplayRect.setPixmap to display. Thanks for reading!

raw = rawpy.imread(...path...)
src = raw.postprocess()
buf = src.data.tobytes()
h, w, ch = src.shape
bytesperline = ch * whileimage = QImage(buf, 1024, 768, bytesperline, QImage.Format_RGB888)

self.DisplayRect.setPixmap(QPixmap.fromImage(image))
self.DisplayRect.show()

Solution

  • You can try move the preprocessing logic to the DisplayThread and in the run method. Then transfer the raw bytes through the signal to the Main Window and create and show the image from the Main Window.

    class DisplayThread(QThread):
        dpsig = pyqtSignal([bytes, int])
    
        def __init__(self, parent):
            super().__init__()
            self.parent = parent
    
        def run(self):
            raw = rawpy.imread("./file.nef")
            src = raw.postprocess()
            buf = src.data.tobytes()
            h, w, ch = raw.shape
            bpl = w * ch
            self.dpsig.emit(buf, bpl)
    
    class WindowClass(QMainWindow):
        def __init__(self):
            super().__init__()
            self.connectButton.clicked.connect(self.connectFunction)
    
        @pyqtSlot()
        def connectFunction(self):
            self.dpThread = DisplayThread(self)
            self.dpThread.dpsig.connect(self.dpupdate)
            self.dpThread.finished.connect(self.dpThread.deleteLater)
            self.dpThread.start()
    
        def dpupdate(self, buf, bpl):
            print("signal received")
            image = QImage(buf, 1024, 768, bpl, QImage.Format.Format_RGB888)
            self.DisplayRect.setPixmap(QPixmap.fromImage(image))
            self.DisplayRect.show()
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        myWindow = WindowClass()
        myWindow.show()
        app.exec_()