Могут ли сигналы Qt возвращать значение? - PullRequest
51 голосов
/ 30 апреля 2011

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

Не толькоМос не жалуется на сигнал с типом возврата, не являющимся пустым, он, кажется, активно реализует его таким образом, чтобы разрешить передать возвращаемое значение:

// 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?

Слоты могут иметь возвращаемые значения , поэтому можно ли сейчас подключить слот с возвращаемым значением к сигналу с возвращаемым значением?Может ли это быть возможным, в конце концов?Если да, то полезно ли это?

РЕДАКТИРОВАТЬ: Я не прошу обходных путей, поэтому, пожалуйста, не предоставляйте никаких.

РЕДАКТИРОВАТЬ: Очевидно, что это бесполезно в режиме Qt::QueuedConnection (как и QPrintPreviewWidget API , хотя он все еще существует и полезен).Но как насчет Qt::DirectConnection и Qt::BlockingQueuedConnection (или Qt::AutoConnection, когда он разрешается до Qt::DirectConnection).

Ответы [ 5 ]

37 голосов
/ 05 мая 2011

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"

Когда основной прогон, онконструирует один из тестовых классов.Конструктор подключает два слота к сигналу testSignal, а затем испускает сигнал.Он захватывает возвращаемое значение из вызванного слота (ов).

К сожалению, вы получаете только последнее возвращаемое значение.Если вы оцените приведенный выше код, вы получите: «testSlot2», последнее возвращаемое значение из подключенных слотов сигнала.

Вот почему.Сигналы Qt являются интерфейсом с синтаксическим сахаром к шаблону сигнализации.Слоты являются получателями сигнала.В прямом соединении отношения сигнал-слот вы можете подумать, что это похоже на (псевдокод):

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

Очевидно, что moc немного больше помогает в этом процессе (элементарная проверка типов и т. Д.), но это помогает нарисовать картину.

8 голосов
/ 30 апреля 2011

Нет, они не могут.

Boost::signals сильно отличаются от тех, что в Qt. Первые обеспечивают усовершенствованный механизм обратного вызова, тогда как вторые реализуют идиому сигнализации. В контексте многопоточности сигналы Qt (перекрестные) зависят от очередей сообщений, поэтому они вызываются асинхронно в некоторый (неизвестный потоку эмитента) момент времени.

1 голос
/ 18 июля 2013

Вы можете получить возвращаемое значение из Qt signal с помощью следующего кода:

В моем примере показано, как использовать Qt signal для чтения текста QLineEdit.Я просто расширяю то, что предложил @jordan:

Должна быть возможность изменить ваш код таким образом, чтобы использовать "out" параметры, которые действуют как ваш "return".

#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 голос
/ 05 мая 2011

Функция Qt qt_metacall возвращает целочисленный код состояния.Поэтому я считаю, что это делает фактическое возвращаемое значение невозможным (если только вы не возитесь с системой мета-объектов и файлами moc после предварительной компиляции).

Однако у вас есть нормальные параметры функции.Должна быть возможность изменить ваш код таким образом, чтобы использовать параметры «out», которые действуют как ваш «возврат».

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

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

// somewhere else in the code...

return_type ret;
emit this->method(&ret);
0 голосов
/ 30 апреля 2011

Вы можете попытаться обойти это следующим образом:

  1. Все ваши подключенные слоты должны сохранить свои результаты в каком-то месте (контейнере), доступном из объекта сигнализации
  2. Последний подключенный слот долженкаким-то образом (выберите максимальное или последнее значение) обработайте собранные значения и выставьте единственное
  3. Излучающий объект может попытаться получить доступ к этому результату

Просто как идея.

...