Manera más simple para el enhebrado de PyQT

Tengo una GUI en PyQt con una función addImage(image_path) . Fácil de imaginar, se llama cuando se debe agregar una nueva imagen a un QListWidget. Para la detección de nuevas imágenes en una carpeta, utilizo un threading.Thread Hilo con watchdog para detectar cambios en el archivo en la carpeta, y este hilo luego llama a addImage directamente.

Esto produce la advertencia de que no se debe llamar a QPixmap fuera del hilo gui, por razones de seguridad del hilo.

¿Cuál es la forma mejor y más sencilla de hacer que este threadsafe? Qhilo? Señal / Ranura? QMetaObject.invokeMethod? Solo necesito pasar una cadena desde el hilo a addImage .

Creo que el mejor enfoque es usar el mecanismo de señal / ranura. Aquí hay un ejemplo. (Nota: consulte el EDITAR a continuación que señala una posible debilidad en mi enfoque).

 from PyQt4 import QtGui from PyQt4 import QtCore # Create the class 'Communicate'. The instance # from this class shall be used later on for the # signal/slot mechanism. class Communicate(QtCore.QObject): myGUI_signal = QtCore.pyqtSignal(str) ''' End class ''' # Define the function 'myThread'. This function is the so-called # 'target function' when you create and start your new Thread. # In other words, this is the function that will run in your new thread. # 'myThread' expects one argument: the callback function name. That should # be a function inside your GUI. def myThread(callbackFunc): # Setup the signal-slot mechanism. mySrc = Communicate() mySrc.myGUI_signal.connect(callbackFunc) # Endless loop. You typically want the thread # to run forever. while(True): # Do something useful here. msgForGui = 'This is a message to send to the GUI' mySrc.myGUI_signal.emit(msgForGui) # So now the 'callbackFunc' is called, and is fed with 'msgForGui' # as parameter. That is what you want. You just sent a message to # your GUI application! - Note: I suppose here that 'callbackFunc' # is one of the functions in your GUI. # This procedure is thread safe. ''' End while ''' ''' End myThread ''' 

En su código de aplicación GUI, debe crear el nuevo subproceso, asignarle la función de callback correcta y hacer que se ejecute.

 from PyQt4 import QtGui from PyQt4 import QtCore import sys import os # This is the main window from my GUI class CustomMainWindow(QtGui.QMainWindow): def __init__(self): super(CustomMainWindow, self).__init__() self.setGeometry(300, 300, 2500, 1500) self.setWindowTitle("my first window") # ... self.startTheThread() '''''' def theCallbackFunc(self, msg): print('the thread has sent this message to the GUI:') print(msg) print('---------') '''''' def startTheThread(self): # Create the new thread. The target function is 'myThread'. The # function we created in the beginning. t = threading.Thread(name = 'myThread', target = myThread, args = (self.theCallbackFunc)) t.start() '''''' ''' End CustomMainWindow ''' # This is the startup code. if __name__== '__main__': app = QtGui.QApplication(sys.argv) QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique')) myGUI = CustomMainWindow() sys.exit(app.exec_()) ''' End Main ''' 

EDITAR

El Sr. three_pineapples y el Sr. Brendan Abel señalaron una debilidad en mi enfoque. De hecho, el enfoque funciona bien para este caso en particular, porque usted genera / emite la señal directamente. Cuando trata con las señales Qt incorporadas en los botones y widgets, debe adoptar otro enfoque (como se especifica en la respuesta del Sr. Brendan Abel).

El Sr. three_pineapples me aconsejó comenzar un nuevo tema en StackOverflow para hacer una comparación entre los diversos enfoques de comunicación segura para subprocesos con una GUI. Voy a profundizar en el asunto, y lo haré mañana 🙂

Debe usar el QThread proporcionado por Qt. Puede colocar su código de monitoreo de archivos dentro de una clase de trabajador que se hereda de QObject para que pueda usar el sistema Qt Signal / Slot para pasar mensajes entre subprocesos.

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