web-dev-qa-db-fra.com

Les signaux Qt peuvent-ils renvoyer une valeur?

Boost.Signals permet diverses stratégies d'utiliser les valeurs de retour des slots pour former la valeur de retour du signal. Par exemple. en les ajoutant, en formant un vector ou en renvoyant le dernier.

La sagesse commune (exprimée dans la documentation Qt [EDIT: ainsi que quelques réponses à cette question ] ) est que rien de tel n'est possible avec les signaux Qt.

Cependant, lorsque j'exécute le moc sur la définition de classe suivante:

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 seulement moc ne se plaint pas du signal avec le type de retour non nul, mais il semble l'implémenter activement de manière à permettre une valeur de retour à passer:

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

Donc: selon les documents, cette chose n'est pas possible. Alors que fait moc ici?

Les slots peuvent avoir des valeurs de retour , alors pouvons-nous connecter un slot avec une valeur de retour à un signal avec une valeur de retour maintenant? Cela peut-il être possible, après tout? Si oui, est-ce utile?

EDIT: Je ne demande pas de solution de contournement, alors n'en fournissez pas.

EDIT: Ce n'est évidemment pas utile dans Qt::QueuedConnection mode (pas plus que QPrintPreviewWidget API , cependant, et il existe toujours et est utile). Mais qu'en est-il de Qt::DirectConnection et Qt::BlockingQueuedConnection (ou Qt::AutoConnection, quand il se résout en Qt::DirectConnection).

51
Marc Mutz - mmutz

D'ACCORD. Donc, j'ai fait un peu plus d'enquête. Semble que c'est possible. J'ai pu émettre un signal et recevoir de la valeur de la fente à laquelle le signal était connecté. Mais, le problème était qu'il ne renvoyait que la dernière valeur de retour des multiples emplacements connectés:

Voici une définition de classe simple (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"

Lorsque main s'exécute, il construit l'une des classes de test. Le constructeur connecte deux emplacements au signal testSignal, puis émet le signal. Il capture la valeur de retour des emplacements invoqués.

Malheureusement, vous n'obtenez que la dernière valeur de retour. Si vous évaluez le code ci-dessus, vous obtiendrez: "testSlot2", la dernière valeur de retour des emplacements connectés du signal.

Voici pourquoi. Les signaux Qt sont une interface syntaxique simplifiée avec le modèle de signalisation. Les machines à sous sont les destinataires d'un signal. Dans une relation signal-slot connectée directement, vous pourriez penser à cela comme (pseudo-code):

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

Évidemment, le moc fait un peu plus pour aider dans ce processus (vérification de type rudimentaire, etc.), mais cela aide à peindre l'image.

38
jsherer

Non, ils ne peuvent pas.

Boost::signals sont assez différents de ceux de Qt. Les premiers fournissent un mécanisme de rappel avancé, tandis que les seconds mettent en œuvre l'idiome de signalisation. Dans le contexte du multithreading, les signaux de Qt (cross-threaded) dépendent des files d'attente de messages, ils sont donc appelés de manière asynchrone à un moment donné (inconnu du thread de l'émetteur).

8
vines

La fonction qt_metacall de Qt renvoie un code d'état entier. Pour cette raison, je pense que cela rend impossible une valeur de retour réelle (à moins que vous ne fouilliez avec le système de méta-objets et les fichiers moc après la précompilation).

Vous disposez cependant de paramètres de fonctionnement normaux. Il devrait être possible de modifier votre code de manière à utiliser des paramètres "out" qui agissent comme votre "retour".

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

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

// somewhere else in the code...

return_type ret;
emit this->method(&ret);
1
jsherer

Vous pouvez obtenir une valeur de retour de Qt signal Avec le code suivant:

Mon exemple montre comment utiliser un Qt signal Pour lire le texte d'un QLineEdit. J'étends juste ce que @jordan a proposé:

Il devrait être possible de modifier votre code de manière à utiliser des paramètres "out" qui agissent comme votre "retour".

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

};

Pour l'utiliser, il suffit de demander call();.

1
Antonio Dias