PyQt: conexión de una señal a una ranura para iniciar una operación en segundo plano

Tengo el siguiente código que realiza una operación en segundo plano ( scan_value ) al actualizar una barra de progreso en la interfaz de usuario ( progress ). scan_value itera sobre algún valor en obj , emitiendo una señal ( value_changed ) cada vez que se cambia el valor. Por razones que no son relevantes aquí, tengo que envolver esto en un objeto ( Scanner ) en otro hilo. Se llama al Escáner cuando se clicked un botón de scan . Y aquí viene mi pregunta … el siguiente código funciona bien (es decir, la barra de progreso se actualiza a tiempo).

 # I am copying only the relevant code here. def update_progress_bar(new, old): fraction = (new - start) / (stop - start) progress.setValue(fraction * 100) obj.value_changed.connect(update_progress_bar) class Scanner(QObject): def scan(self): scan_value(start, stop, step) progress.setValue(100) thread = QThread() scanner = Scanner() scanner.moveToThread(thread) thread.start() scan.clicked.connect(scanner.scan) 

Pero si cambio la última parte a esto:

 thread = QThread() scanner = Scanner() scan.clicked.connect(scanner.scan) # This was at the end! scanner.moveToThread(thread) thread.start() 

La barra de progreso se actualiza solo al final (supongo que todo se está ejecutando en el mismo subproceso). Debería ser irrelevante si conecto la señal a una ranura antes o después de mover el objeto que recibe el objeto al hilo.

No debería importar si la conexión se realiza antes o después de mover el objeto de trabajo al otro hilo. Para citar de los documentos Qt :

Qt :: AutoConnection : si la señal se emite desde un subproceso diferente al del objeto receptor, la señal se pone en cola y se comporta como Qt :: QueuedConnection . De lo contrario, la ranura se invoca directamente, actuando como Qt :: DirectConnection . El tipo de conexión se determina cuando se emite la señal . [énfasis añadido]

Por lo tanto, siempre que el argumento de type de connect se establezca en QtCore.Qt.AutoConnection (que es el valor predeterminado), Qt debe garantizar que las señales se emiten de la manera adecuada.

El problema con el código de ejemplo es más probable que sea con la ranura que con la señal . El método python al que se conecta la señal probablemente deba marcarse como una ranura Qt, utilizando el decorador pyqtSlot :

 from QtCore import pyqtSlot class Scanner(QObject): @pyqtSlot() def scan(self): scan_value(start, stop, step) progress.setValue(100) 

EDITAR :

Se debe aclarar que solo en versiones bastante recientes de Qt, el tipo de conexión se determina cuando se emite la señal. Este comportamiento se introdujo (junto con varios otros cambios en el soporte multihilo de Qt) con la versión 4.4.

Además, podría valer la pena expandirse aún más en el problema específico de PyQt. En PyQt, se puede conectar una señal a una ranura Qt, a otra señal o a cualquier python que se pueda llamar. Para el último caso, un objeto proxy se crea internamente que envuelve el Python invocable y proporciona la ranura que requiere el mecanismo de señal / ranura Qt.

Este es el objeto proxy que es la causa del problema. Una vez que se crea el proxy, PyQt simplemente hará esto:

  if (rx_qobj) proxy->moveToThread(rx_qobj->thread()); 

lo cual está bien si la conexión se realiza después de que el objeto receptor se haya movido a su hilo; pero si se hace antes , el proxy permanecerá en el hilo principal.

El uso del decorador @pyqtSlot evita este problema por completo, ya que crea una ranura Qt más directamente y no utiliza un objeto proxy en absoluto.

Finalmente, también se debe tener en cuenta que este problema no afecta actualmente a PySide.

Mi problema se resolvió movinf la conexión al lugar donde se inicializa el subproceso de trabajo, en mi caso porque estoy accediendo a un objeto que solo existe después de la creación de instancias de mi clase de Objeto de trabajador que está en otro subproceso.

Simplemente conecte la señal después de self.createWorkerThread()

Saludos

Esto tiene que ver con los tipos de conexión de Qt.

http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html#connect

http://qt-project.org/doc/qt-4.8/qt.html#ConnectionType-enum

En caso de que ambos objetos vivan en el mismo hilo, se crea un tipo de conexión estándar, lo que resulta en una llamada de función simple. En este caso, la operación que consume tiempo se lleva a cabo en el subproceso de la GUI y los bloques de la interfaz.

En caso de que el tipo de conexión sea una conexión de estilo de paso de mensaje, la señal se emite utilizando un mensaje que se maneja en el otro hilo. El hilo de la GUI ahora es libre de actualizar la interfaz de usuario.

Cuando no especifica el tipo de conexión en la función de conexión, el tipo se detecta automáticamente.