Могут ли сигналы Qt возвращать значение?
Boost.Signals позволяет использовать различные стратегии использования возвращаемых значений слотов для формирования возвращаемого значения сигнала. Например, добавляя их, образуя vector
из них или возвращая последний.
Общая мудрость (выраженная в документации Qt [РЕДАКТИРОВАТЬ: а также некоторые ответы на этот вопрос ]) заключается в том, что с сигналами 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, но и, по-видимому, активно реализует его так, чтобы возвращаемое значение передавалось:
// 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;
}
Итак, согласно документам, это невозможно. Тогда что Мос делает здесь?
Слоты могут иметь возвращаемые значения, поэтому можем ли мы сейчас соединить слот с возвращаемым значением с сигналом с возвращаемым значением? Разве это возможно, в конце концов? Если это так, полезно ли это?
РЕДАКТИРОВАТЬ: я не прошу обходных путей, поэтому, пожалуйста, не предоставляйте их.
РЕДАКТИРОВАТЬ: Это, очевидно, не полезно в Qt::QueuedConnection
режим (хотя и API QPrintPreviewWidget, и все же он существует и полезен). Но что насчет Qt::DirectConnection
а также Qt::BlockingQueuedConnection
(или же Qt::AutoConnection
когда он разрешает Qt::DirectConnection
).
5 ответов
ХОРОШО. Итак, я немного больше расследовал. Кажется, это возможно. Мне удалось испустить сигнал и получить значение из слота, к которому был подключен сигнал. Но проблема была в том, что он возвращал только последнее возвращаемое значение из нескольких подключенных слотов:
Вот простое определение класса (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 запускается, он создает один из тестовых классов. Конструктор подключает два слота к сигналу testSignal, а затем выдает сигнал. Он захватывает возвращаемое значение из вызванного слота (ов).
К сожалению, вы получаете только последнее возвращаемое значение. Если вы оцените приведенный выше код, вы получите: "testSlot2", последнее возвращаемое значение из подключенных слотов сигнала.
Вот почему Сигналы Qt являются интерфейсом с синтаксическим сахаром к шаблону сигнализации. Слоты являются получателями сигнала. В прямом соединении отношения сигнал-слот вы можете думать об этом как (псевдокод):
foreach slot in connectedSlotsForSignal(signal):
value = invoke slot with parameters from signal
return value
Очевидно, что moc делает немного больше, чтобы помочь в этом процессе (элементарная проверка типов и т. Д.), Но это помогает нарисовать картину.
Нет, они не могут.
Boost::signals
сильно отличаются от тех, что в Qt. Первые обеспечивают усовершенствованный механизм обратного вызова, тогда как вторые реализуют идиому сигнализации. В контексте многопоточности сигналы Qt (перекрестные) зависят от очередей сообщений, поэтому они вызываются асинхронно в некоторый (неизвестный потоку эмитента) момент времени.
Функция qt_metacall в Qt возвращает целочисленный код состояния. Поэтому я считаю, что это делает реальное возвращаемое значение невозможным (если только вы не возитесь с системой мета-объектов и файлами moc после предварительной компиляции).
У вас, однако, есть нормальные функциональные параметры в вашем распоряжении. Должна быть возможность изменить ваш код таким образом, чтобы использовать параметры "out", которые действуют как ваш "возврат".
void ClassObj::method(return_type * return_)
{
...
if(return_) *return_ = ...;
}
// somewhere else in the code...
return_type ret;
emit this->method(&ret);
Вы можете получить возвращаемое значение от 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();
,
Вы можете попытаться обойти это с помощью следующего:
- Все ваши подключенные слоты должны сохранять свои результаты в каком-то месте (контейнере), доступном из объекта сигнализации
- Последний подключенный слот должен каким-то образом (выбрать максимальное или последнее значение) обрабатывать собранные значения и выставлять только одно
- Излучающий объект может попытаться получить доступ к этому результату.
Просто как идея.