Может ли 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
).
Ответы
Ответ 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
Вы можете попытаться обходить это следующим образом:
- Все ваши подключенные слоты должны сохранять свои результаты в некотором месте (контейнере), доступном из объекта сигнализации.
- Последний подключенный слот должен каким-то образом (выбрать максимальное или последнее значение) обрабатывать собранные значения и выставлять только один
- Испускающий объект может попытаться получить доступ к этому результату
Как идея.