¿Enviando señales personalizadas de PyQt?

Estoy practicando hilos PyQt y (Q) haciendo un cliente de Twitter simple. Tengo dos Qthreads.

  1. Hilo principal / GUI.

  2. Recostackción de mensajes de Twitter: obtiene datos de Twitter cada X minutos.

Por lo tanto, cada X minutos de mi hilo de Twitter descarga un nuevo conjunto de actualizaciones de estado (una lista de Python). Quiero entregar esta lista al hilo principal / GUI, para que pueda actualizar la ventana con estos estados.

Supongo que debería usar el sistema de señal / ranura para transferir los “estados” de la lista de Python desde el hilo de Twitter, al hilo principal / GUI. Por lo tanto, mi pregunta es doble:

  1. ¿Cómo envío los estados del hilo de Twitter?

  2. ¿Cómo los recibo en el hilo principal / GUI?

Por lo que sé, PyQt solo puede enviar objetos PyQt de forma predeterminada a través de señales / ranuras. Creo que se supone que debo registrar una señal personalizada que luego puedo enviar, pero la documentación sobre esto que he encontrado no es muy clara para un novato como yo. Tengo un libro de PyQt en orden, pero no llegará en otra semana, y no quiero esperar hasta entonces. 🙂

Estoy usando PyQt 4.6-1 en Ubuntu

Actualizar:

Este es un ejercicio del código que no funciona. Primero, trato de “conectar” la señal (“newStatuses”, un nombre que acabo de hacer) a la función self.update_tweet_list en el hilo principal / GUI:

QtCore.QObject.connect(self.twit_in, QtCore.SIGNAL("newStatuses (statuses)"), self.update_tweet_list) 

Luego, en el hilo de Twitter, hago esto:

 self.emit(SIGNAL("newStatuses (statuses)"), statuses) 

Cuando se llama a esta línea, recibo el siguiente mensaje:

 QObject::connect: Cannot queue arguments of type 'statuses' (Make sure 'statuses' is registered using qRegisterMetaType().) 

Hice una búsqueda de qRegisterMetaType () pero no encontré nada relacionado con Python que pudiera entender.

De este ejemplo:

http://doc.qt.digia.com/4.5/qmetatype.html

  int id = QMetaType.type("MyClass"); 

Puedes escribir en Python

 from PyQt4 import QtCore id = QtCore.QMetaType.type('MyClass') 

Editar

La respuesta extraída del comentario:

 self.emit(SIGNAL("newStatuses(PyQt_PyObject)"), statuses) 

También puedes hacer esto, que es mucho más pythonico (¡y legible!).

 # create a signal equivalent to "void someSignal(int, QWidget)" someSignal = QtCore.pyqtSignal(int, QtGui.QWidget) # define a slot with the same signature @QtCore.pyqtSlot(int, QtGui.QWidget) def someSlot(status, source): pass # connect the signal to the slot self.someSignal.connect(self.someSlot) 

Echa un vistazo a esta pregunta que le pregunté hace un tiempo. Hay un ejemplo de código que podría ayudarlo a descubrir qué necesita hacer.

Lo que dijo sobre el registro de su señal me hace pensar en este código (de la pregunta mencionada anteriormente):

 class ProcessingThread(threading.Thread, QtCore.QObject): __pyqtSignals__ = ( "progressUpdated(str)", "resultsReady(str)") 

Estoy pasando cadenas en mi ejemplo, pero deberías poder reemplazar str con list .

Si resulta que no puedes pasar objetos mutables, puedes manejar tus resultados como lo hago en mi ejemplo (es decir, establecer una variable de results en el hilo, decirle al hilo principal que están listos y tener el hilo principal ” recogelos”).

Actualizar:

Recibirá el mensaje QObject::connect: Cannot queue arguments of type 'statuses' poner en QObject::connect: Cannot queue arguments of type 'statuses' porque necesita definir el tipo de argumento que pasará cuando emita su señal. El tipo que desea pasar es list no statuses .

Cuando conectes tu señal debería verse así:

 QtCore.QObject.connect(self.twit_in, QtCore.SIGNAL("newStatuses(list)"), self.update_tweet_list) 

Cuando emites tu señal debería verse así:

 self.emit(SIGNAL("newStatuses(list)"), statuses) 

statuses que los statuses es una lista. Tenga en cuenta que es posible que desee emitir una copia profunda de su lista en función de su situación.

Actualización 2:

Ok, usando la list como el tipo no es correcto. De la referencia de ayuda de PyQt4:

Señales PyQt y Señales Qt

Las señales Qt se definen estáticamente como parte de una clase C ++. Se referencian utilizando la función QtCore.SIGNAL() . Este método toma un solo argumento de cadena que es el nombre de la señal y su firma C ++. Por ejemplo::

 QtCore.SIGNAL("finished(int)") 

El valor devuelto normalmente se pasa al método QtCore.QObject.connect() .

PyQt permite definir nuevas señales dinámicamente. El acto de emitir una señal PyQt lo define implícitamente. Las señales de PyQt v4 también se referencian utilizando la función QtCore.SIGNAL() .

El PyQt_PyObject argumento de la señal PyQt_PyObject

Es posible pasar cualquier objeto Python como un argumento de señal especificando PyQt_PyObject como el tipo del argumento en la firma. Por ejemplo::

 QtCore.SIGNAL("finished(PyQt_PyObject)") 

Si bien esto normalmente se usaría para pasar objetos como listas y diccionarios como argumentos de señal, puede usarse para cualquier tipo de Python. Su ventaja al pasar, por ejemplo, un número entero es que no se requieren las conversiones normales de un objeto Python a un número entero de C ++ y viceversa.

El recuento de referencia del objeto que se pasa se mantiene automáticamente. No es necesario que el emisor de una señal mantenga una referencia al objeto después de la llamada a QtCore.QObject.emit() , incluso si una conexión está en cola.

Cuando utiliza estas señales / ranuras de estilo antiguo en PyQt, en realidad no hay necesidad de declarar tipos. Estos deberían funcionar:

 QtCore.QObject.connect(self.twit_in, QtCore.SIGNAL("newStatuses"), self.update_tweet_list) ... self.emit(SIGNAL("newStatuses"), statuses) 

En este caso, PyQt creará un nuevo tipo de señal para usted sobre la marcha cuando emita la señal. Si quisiera usar las señales de nuevo estilo, entonces se vería más como:

 class TwitterThread(QThread): newStatuses = pyqtSignal(object) .... self.newStatuses.emit(statuses) 

(Py) Las señales Qt y las ranuras funcionan con hilos cruzados de la misma manera que en un solo hilo. Así que no hay nada realmente especial para configurar:

Defina una ranura (método) en el hilo principal y conecte la señal del hilo a esta ranura (la conexión también se realizará en el hilo principal). Luego, en el hilo, cuando lo desee, simplemente emita la señal y debería funcionar. Aquí hay un tutorial sobre el uso de señales y ranuras con subprocesos en PyQt.

Le recomiendo que lo pruebe primero con un pequeño juguete, antes de pasar a su aplicación.