2011-04-30 15 views
46

Boost.Signals consente various strategies di utilizzare i valori di ritorno degli slot per formare il valore di ritorno del segnale. Per esempio. aggiungendoli, formando uno vector di questi o restituendo l'ultimo.I segnali Qt possono restituire un valore?

La saggezza comune (espressa nella documentazione Qt [EDIT: così come alcune risposte a questa domanda ]) è che nulla del genere è possibile con i segnali Qt.

Tuttavia, quando ho eseguito il MOC sulla seguente definizione di classe:

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(); 
}; 

Non solo non Moc lamentarsi del segnale con il tipo di ritorno non-vuoto, sembra ad attuare attivamente in tale un modo tale da consentire un valore di ritorno di passare:

// 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; 
} 

Quindi: secondo la documentazione, questa cosa non è possibile. Allora cosa sta facendo moc qui?

Slots can have return values, così possiamo collegare uno slot con un valore di ritorno a un segnale con un valore di ritorno ora? Potrebbe essere possibile, dopotutto? Se è così, è utile?

MODIFICA: Non sto chiedendo soluzioni alternative, quindi non fornire nulla.

EDIT: Ovviamente non è utile in modalità Qt::QueuedConnection (né è la QPrintPreviewWidget API, però, e ancora esiste ed è utile). Ma che dire di Qt::DirectConnection e Qt::BlockingQueuedConnection (o Qt::AutoConnection, quando si risolve in Qt::DirectConnection).

risposta

33

OK. Quindi, ho fatto un po 'più di investigazione. Sembra che questo sia possibile. Sono stato in grado di emettere un segnale e ricevere il valore dallo slot a cui era collegato il segnale. Ma, il problema era che ha restituito solo l'ultimo valore restituito da molteplici slot collegati:

Ecco una definizione semplice classe (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" 

Quando viene eseguito principali, si costruisce una delle classi di test . Il costruttore collega due slot al segnale testSignal e quindi emette il segnale. Cattura il valore di ritorno dagli slot invocati.

Sfortunatamente, si ottiene solo l'ultimo valore restituito. Se valuti il ​​codice sopra, riceverai: "testSlot2", l'ultimo valore restituito dagli slot connessi del segnale.

Ecco perché. I segnali Qt sono un'interfaccia sintattica per il pattern di segnalazione. Gli slot sono i destinatari di un segnale. In un rapporto segnale-scanalatura collegato diretta, si potrebbe pensare simile alla (pseudo-codice):

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

Ovviamente il moc fa un po 'più per aiutare in questo processo (controllo di tipo rudimentale, ecc), ma questo aiuta a dipingere l'immagine.

+2

grazie per aver effettivamente provato :) Ho modificato il tuo codice per essere più semplice e più breve. Tuttavia, la domanda è ancora valida: se funziona (con la semantica chiamata "last-called"), perché i documenti dicono che non funziona? –

+0

Una bella domanda. Direi che i documenti dicono che non funziona perché è solo un valore di ritorno parziale. Il vero valore di ritorno di un'emissione di segnale * dovrebbe * essere un aggregato di tutti i risultati basati su un qualche tipo di aggregatore (come in boost). Ma, senza di ciò, è un risultato parziale e indefinito (specialmente nel contesto di una chiamata di segnale concorrente). Forse ci sono anche alcune differenze nel compilatore? – jsherer

+1

Un comportamento non documentato significa che non si garantisce che funzioni ancora, ad esempio, Qt 5.0 :) – Torp

7

No, non possono.

Boost::signals sono molto diversi da quelli in Qt. I primi forniscono un meccanismo di callback avanzato, mentre i secondi implementano l'idioma di segnalazione. Nel contesto del multithreading, i segnali di Qt (cross-threaded) dipendono dalle code dei messaggi, quindi vengono chiamati in modo asincrono in un punto (sconosciuto al thread dell'emettitore) nel tempo.

+1

Come si può fare affidamento sul tipo di connessioni, che si verificheranno in fase di esecuzione, mentre si scrive il codice che le utilizza? Questi non sono modelli, Qt è principalmente libreria di runtime :) – vines

+0

Quindi si pensa anche che [QPrintPreviewWidget :: paintRequested()] (http://doc.trolltech.com/latest/qprintpreviewwidget.html#paintRequested) sia un'API malefatta. Anche a me. Eppure, è lì e funziona. –

+0

Penso che questa sia l'unica risposta corretta alla domanda, dal momento che il meccanismo di segnali/slot Qt è progettato per funzionare anche in modo asincrono. –

-1

Si può cercare di risolvere questo con i seguenti:

  1. Tutte le tue slot collegati devono salvare i loro risultati in un luogo (contenitore) accessibili dalla segnalazione oggetto
  2. L'ultimo slot connessa deve in qualche modo (selezionare max o ultimo valore) valori di processo raccolti ed espongono l'unico
  3. l'oggetto luminescente può tentare di accedere a questo risultato

Proprio come idea.

+0

Sfortunatamente non è possibile stabilire quale sia l'ultimo slot collegato, poiché la libreria non ti dirà e controllerà attentamente l'ordine di tipo di connessione delle sconfitte allo scopo di utilizzare i segnali in primo luogo. –

+0

Non stavo chiedendo soluzioni alternative, per favore rileggi la mia domanda. –

0

La funzione qt_metacall di Qt restituisce un codice di stato intero. Per questo motivo, ritengo che ciò renda impossibile un effettivo valore di ritorno (a meno che non ci si diverta con il sistema meta oggetto ei file moc dopo la precompilazione).

Tuttavia, sono disponibili parametri di funzionamento normali. Dovrebbe essere possibile modificare il codice in modo tale da utilizzare i parametri "out" che fungono da "ritorno".

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

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

// somewhere else in the code... 

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

Credo che ciò richiederebbe una connessione non asincrona, a meno che tu non sia riuscito in qualche modo a comparire in un oggetto "futuro" con più segnali. – jsherer

1

Si può ottenere un valore di ritorno dalla Qt signal con il seguente codice:

Il mio esempio mostra come usare un Qt signal di leggere il testo di una QLineEdit. Sto semplicemente estendendo ciò che @jordan ha proposto:

Dovrebbe essere possibile modificare il codice in modo tale da utilizzare i parametri "out" che fungono da "ritorno".

#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; 

}; 

Per utilizzare questo, basta chiedere call();.