Подтвердить что ты не робот

Может ли 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 return, он, как представляется, активно реализует его таким образом, чтобы разрешить возвращаемое значение:

// 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: Я не прошу об обходах, поэтому, пожалуйста, не указывайте.

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

4b9b3361

Ответ 1

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 Signals - это синтаксический сахаризованный интерфейс для шаблона сигнализации. Слоты являются получателями сигнала. В прямом соединении с сигналом-слотом вы можете думать о нем аналогично (псевдокоду):

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

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

Ответ 2

Нет, они невозможны.

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

Ответ 3

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

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

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

#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();.

Ответ 4

Функция 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);

Ответ 5

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

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

Как идея.