Simplest way for PyQT Threading

user3696412 picture user3696412 · May 16, 2016 · Viewed 25.9k times · Source

I have a GUI in PyQt with a function addImage(image_path). Easy to imagine, it is called when a new image should be added into a QListWidget. For the detection of new images in a folder, I use a threading.Thread with watchdog to detect file changes in the folder, and this thread then calls addImage directly.

This yields the warning that QPixmap shouldn't be called outside the gui thread, for reasons of thread safety.

What is the best and most simple way to make this threadsafe? QThread? Signal / Slot? QMetaObject.invokeMethod? I only need to pass a string from the thread to addImage.

Answer

Brendan Abel picture Brendan Abel · May 16, 2016

You should use the built in QThread provided by Qt. You can place your file monitoring code inside a worker class that inherits from QObject so that it can use the Qt Signal/Slot system to pass messages between threads.

class FileMonitor(QObject):

    image_signal = QtCore.pyqtSignal(str)

    @QtCore.pyqtSlot()
    def monitor_images(self):
        # I'm guessing this is an infinite while loop that monitors files
        while True:
            if file_has_changed:
                self.image_signal.emit('/path/to/image/file.jpg')


class MyWidget(QtGui.QWidget):

    def __init__(self, ...)
        ...
        self.file_monitor = FileMonitor()
        self.thread = QtCore.QThread(self)
        self.file_monitor.image_signal.connect(self.image_callback)
        self.file_monitor.moveToThread(self.thread)
        self.thread.started.connect(self.file_monitor.monitor_images)
        self.thread.start()

    @QtCore.pyqtSlot(str)
    def image_callback(self, filepath):
        pixmap = QtGui.QPixmap(filepath)
        ...