python - qthread pyqt5
¿Enviando señales personalizadas de PyQt? (5)
(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.
Estoy practicando hilos de PyQt y (Q) haciendo un cliente de Twitter simple. Tengo dos Qthreads.
Hilo principal / GUI.
Recopilación de mensajes de Twitter: obtiene datos de Twitter cada X minutos.
Entonces, 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:
¿Cómo envío los estados desde el hilo de Twitter?
¿Cómo los recibo en el hilo principal / GUI?
Por lo que sé, PyQt puede enviar por defecto solo objetos PyQt 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 encontré 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.
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)
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)
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ñalPyQt_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 la conexión está en cola.
También puedes hacer esto, que es mucho más pitónico (¡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)