web-dev-qa-db-ja.com

Qtシグナルは値を返すことができますか?

Boost.Signals は、 さまざまな戦略 スロットの戻り値を使用して信号の戻り値を形成することを許可します。例えば。それらを追加するか、それらからvectorを形成するか、最後のものを返します。

一般的な知恵(Qtのドキュメント[EDIT:で表現されている)と、この質問に対するいくつかの回答])は、Qtシグナルではそのようなことができないということです。

ただし、次のクラス定義でmocを実行すると:

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

Mocは非void戻り値型のシグナルについて文句を言うだけでなく、allowを渡す値を許可するように積極的に実装しているようです:

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

そのため、ドキュメントによると、このことは不可能です。ここでmocは何をしているのでしょうか?

スロットは戻り値を持つことができます 、戻り値を持つスロットを戻り値を持つ信号に接続できますか?結局それが可能かもしれませんか?もしそうなら、それは便利ですか?

EDIT:回避策を求めていないので、提供しないでください。

EDIT:Qt::QueuedConnection モードでは明らかに役に立ちません(どちらも QPrintPreviewWidget APIではありません 、しかし、まだ存在し、有用です)。しかし、Qt::DirectConnectionおよびQt::BlockingQueuedConnection(またはQt::AutoConnectionに解決される場合はQt::DirectConnection)はどうでしょうか。

51

OK。それで、もう少し調査しました。これは可能だと思われます。信号を発信し、信号が接続されたスロットから値を受け取ることができました。しかし、問題は、接続された複数のスロットから最後の戻り値のみを返すことでした。

簡単なクラス定義(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"

Mainが実行されると、テストクラスの1つが構築されます。コンストラクターは2つのスロットをtestSignal信号に接続し、信号を送信します。呼び出されたスロットからの戻り値をキャプチャします。

残念ながら、最後の戻り値しか取得できません。上記のコードを評価すると、「testSlot2」、つまり信号の接続されたスロットからの最後の戻り値が得られます。

その理由は次のとおりです。 Qtシグナルは、シグナルパターンへの構文シュガーインターフェイスです。スロットは信号の受信者です。直接接続された信号とスロットの関係では、(擬似コード)に似ていると考えることができます。

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

明らかに、mocはこのプロセス(初歩的な型チェックなど)を支援するためにもう少し役立ちますが、これは絵を描くのに役立ちます。

38
jsherer

いいえ、できません。

Boost::signalsはQtのものとはまったく異なります。前者は高度なコールバックメカニズムを提供し、後者はシグナリングイディオムを実装します。マルチスレッドのコンテキストでは、Qt(クロススレッド)シグナルはメッセージキューに依存するため、ある時点(エミッターのスレッドには不明)で非同期に呼び出されます。

8
vines

Qtのqt_metacall関数は、整数のステータスコードを返します。このため、これにより、実際の戻り値は不可能になると思います(プリコンパイル後にメタオブジェクトシステムとmocファイルに手を加えない限り)。

ただし、通常の関数パラメーターを自由に使用できます。 「戻り」として機能する「出力」パラメータを使用するように、コードを変更することができるはずです。

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

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

// somewhere else in the code...

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

次のコードを使用すると、_Qt signal_から戻り値を取得できます。

私の例では、Qt signal_を使用してQLineEditのテキストを読み取る方法を示しています。 @jordanが提案したものを拡張しています。

「戻り」として機能する「出力」パラメータを使用するように、コードを変更することができるはずです。

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

};
_

これを使用するには、call();をリクエストするだけです。

1
Antonio Dias