2011-04-30 21 views
46

Boost.Signals permite various strategies utilizar los valores de retorno de las ranuras para formar el valor de retorno de la señal. P.ej. agregándolos, formando un vector de ellos, o devolviendo el último.¿Las señales de Qt pueden devolver un valor?

La sabiduría popular (expresada en la documentación de Qt [EDIT:, así como algunas respuestas a esta pregunta ]) es que tal cosa no es posible con señales de Qt.

Sin embargo, cuando ejecuto el moc en la siguiente definición de clase:

class Object : public QObject { 
    Q_OBJECT 
public: 
    explicit Object(QObject * parent=0) 
     : QObject(parent) {} 

public Q_SLOTS: 
    void voidSlot(); 
    int intSlot(); 

Q_SIGNALS: 
    void voidSignal(); 
    int intSignal(); 
}; 

No sólo no MOC quejarse de la señal con el tipo de retorno no nula, parece implementar de forma activa en tales de manera que se permita que un valor de retorno de pasar:

// SIGNAL 1 
int Object::intSignal() 
{ 
    int _t0; 
    void *_a[] = { const_cast<void*>(reinterpret_cast<const void*>(&_t0)) }; 
    QMetaObject::activate(this, &staticMetaObject, 1, _a); 
    return _t0; 
} 

Por lo tanto: de acuerdo con los documentos, esto no es posible. Entonces, ¿qué está haciendo moc aquí?

Slots can have return values, entonces, ¿podemos conectar ahora una ranura con un valor de retorno a una señal con un valor de retorno? Que eso sea posible, después de todo? Si es así, ¿es útil?

EDIT: No estoy pidiendo soluciones, así que no proporcione ninguna.

EDIT: Es evidente que no es útil en el modo de Qt::QueuedConnection (tampoco lo es el QPrintPreviewWidget API, sin embargo, y aún existe y es útil). Pero ¿qué pasa con Qt::DirectConnection y Qt::BlockingQueuedConnection (o Qt::AutoConnection, cuando se resuelve en Qt::DirectConnection).

Respuesta

33

OK. Entonces, investigué un poco más. Parece que esto es posible. Pude emitir una señal y recibir valor de la ranura a la que estaba conectada la señal. Sin embargo, el problema era que sólo devuelve el último valor de retorno de las múltiples ranuras conectados:

He aquí una definición de clase sencilla (main.cpp):

#include <QObject> 
#include <QDebug> 

class TestClass : public QObject 
{ 
    Q_OBJECT 
public: 
    TestClass(); 

Q_SIGNALS: 
    QString testSignal(); 

public Q_SLOTS: 
    QString testSlot1() { 
     return QLatin1String("testSlot1"); 
    } 
    QString testSlot2() { 
     return QLatin1String("testSlot2"); 
    } 
}; 

TestClass::TestClass() { 
    connect(this, SIGNAL(testSignal()), this, SLOT(testSlot1())); 
    connect(this, SIGNAL(testSignal()), this, SLOT(testSlot2())); 

    QString a = emit testSignal(); 
    qDebug() << a; 
} 

int main() { 
    TestClass a; 
} 

#include "main.moc" 

Cuando principales carreras, se construye una de las clases de prueba . El constructor conecta dos ranuras a la señal testSignal y luego emite la señal. Captura el valor de retorno de la (s) ranura (s) invocada (s).

Lamentablemente, solo obtiene el último valor de retorno. Si evalúa el código anterior, obtendrá: "testSlot2", el último valor de retorno de las ranuras conectadas de la señal.

He aquí por qué. Las señales de Qt son una interfaz azucarada de sintaxis para el patrón de señalización. Las máquinas tragamonedas son los destinatarios de una señal. En una relación de ranura señal conectada directa, se podría pensar que es similar a la (pseudo-código):

foreach slot in connectedSlotsForSignal(signal): 
    value = invoke slot with parameters from signal 
return value 

Obviamente, la moc hace un poco más para ayudar en este proceso (la comprobación de tipos rudimentaria, etc), pero esto ayuda a pintar la imagen.

+2

gracias por intentarlo :) He editado tu código para que sea más simple y más corto. Sin embargo, la pregunta sigue en pie: si funciona (con la semántica llamada "last-called"), ¿por qué los documentos dicen que no? –

+0

Una buena pregunta. Supongo que los documentos dicen que no funciona porque es solo un valor de retorno parcial. El verdadero valor de retorno de una emisión de señal * debería * ser un agregado de todos los resultados basados ​​en algún tipo de agregador (como en el impulso). Pero, sin eso, es un resultado parcial e indefinido (especialmente en el contexto de una llamada de señal concurrente). Tal vez hay algunas diferencias de compilación también? – jsherer

+1

Comportamiento no documentado significa que no está garantizado que todavía funcione en, digamos, Qt 5.0 :) – Torp

7

No, no pueden.

Boost::signals son bastante diferentes de las de Qt. Los primeros proporcionan un mecanismo de devolución de llamada avanzada, mientras que los últimos implementan el modismo de señalización. En el contexto del multihilo, las señales de Qt (rosca cruzada) dependen de las colas de mensajes, por lo que se llaman asíncronamente en algún punto (desconocido para el hilo del emisor) en el tiempo.

+1

¿Cómo puede confiar en el tipo de conexiones, que ocurrirán en tiempo de ejecución, mientras escribe el código? Estas no son plantillas, Qt es principalmente una biblioteca de tiempo de ejecución :) – vines

+0

Así que también crees que [QPrintPreviewWidget :: paintRequested()] (http://doc.trolltech.com/latest/qprintpreviewwidget.html#paintRequested) es una API especialmente mala. Yo también. Aún así, está allí y funciona. –

+0

Creo que esta es la única respuesta correcta a la pregunta, ya que el mecanismo Qt de señales/ranuras está diseñado para funcionar también de forma asíncrona. –

-1

Usted puede tratar de solucionar esto con lo siguiente:

  1. Todos sus ranuras conectados deben guardar sus resultados en algún lugar (contenedor), accesible desde la señalización objeto
  2. La última ranura conectado de alguna manera debe (select max o el último valor) valores de proceso recogidos y exponen el único
  3. el objeto emisor puede tratar de acceder a este resultado

del mismo modo que una idea.

+0

Desafortunadamente no se puede saber cuál es la última ranura conectada, porque la biblioteca no le avisará y controlar cuidadosamente el orden de la conexión, en primer lugar, frustra el uso de señales. –

+0

No estaba pidiendo soluciones alternativas, por favor vuelva a leer mi pregunta. –

0

La función qt_metacall de Qt devuelve un código de estado entero. Debido a esto, creo que esto hace que un valor de retorno real sea imposible (a menos que te vuelvas loco con el sistema meta objeto y los archivos moc después de la precompilación).

Sin embargo, tiene parámetros de función normales a su disposición. Debería ser posible modificar su código de tal manera que use parámetros de "salida" que actúen como su "devolución".

void ClassObj::method(return_type * return_) 
{ 
    ... 

    if(return_) *return_ = ...; 
} 

// somewhere else in the code... 

return_type ret; 
emit this->method(&ret); 
+0

Creo que esto requeriría una conexión no asíncrona, a menos que de alguna manera se las arreglara para disputar un objeto "futuro" con más señales. – jsherer

1

Usted puede obtener un valor de retorno de Qt signal con el siguiente código:

Mi ejemplo muestra cómo utilizar un Qt signal para leer el texto de una QLineEdit. Estoy extendiendo lo que @jordan ha propuesto:

Debería ser posible modificar su código de tal manera que use parámetros de "salida" que actúen como su "retorno".

#include <QtCore> 
#include <QtGui> 

class SignalsRet : public QObject 
{ 
    Q_OBJECT 

public: 
    SignalsRet() 
    { 
     connect(this, SIGNAL(Get(QString*)), SLOT(GetCurrentThread(QString*)), Qt::DirectConnection); 
     connect(this, SIGNAL(GetFromAnotherThread(QString*)), SLOT(ReadObject(QString*)), Qt::BlockingQueuedConnection); 
     edit.setText("This is a test"); 
    } 

public slots: 
    QString call() 
    { 
     QString text; 
     emit Get(&text); 
     return text; 
    } 

signals: 
    void Get(QString *value); 
    void GetFromAnotherThread(QString *value); 

private slots: 
    void GetCurrentThread(QString *value) 
    { 
     QThread *thread = QThread::currentThread(); 
     QThread *mainthread = this->thread(); 
     if(thread == mainthread) //Signal called from the same thread that SignalsRet class was living 
      ReadObject(value); 
     else //Signal called from another thread 
      emit GetFromAnotherThread(value); 
    } 

    void ReadObject(QString *value) 
    { 
     QString text = edit.text(); 
     *value = text; 
    } 

private: 
    QLineEdit edit; 

}; 

Para utilizarlo, simplemente solicitar call();.

Cuestiones relacionadas