problems with signal/slot
-
Hi,
I'm working on conditional constructs, that update the result based on valueChanged signals.
I have an abstract base class, which registers itself for valueChanged events. In response to that event the class calls a virtual abstract memberfunction and if the result differs from previous evaluation it vomits a signal.
In my testcase only EqualCondition works as expected. Trying Smaller- or GreaterCondition fails before the evaluation is performed.
Actually I don't understand the behaviour and for that, I don't know what to change or where to search for alternatives. I tried to debug testcases, but that does not work.
(I use qtcreator and qt5.15 on linux debian 11)So I hope, someone can shine me a light ...
The abstract declaration:
class AbstractCondition : public QObject { Q_OBJECT public: explicit AbstractCondition(ValueModel* model, const QVariant& value, QObject *parent = nullptr); bool result() { return met; } public slots: virtual bool eval() = 0; virtual void update(); signals: void conditionChanged(bool result); protected: ValueModel* model() { return m; } QVariant value() { return v; } private: ValueModel* m; QVariant v; bool met; };
the corresponding implementation:
AbstractCondition::AbstractCondition(ValueModel* model, const QVariant& value, QObject *parent) : QObject(parent) , m(model) , v(value) { connect(m, &ValueModel::valueChanged, this, &AbstractCondition::update); } void AbstractCondition::update() { qDebug() << "AbstractCondition::update() ..."; bool rv = eval(); qDebug() << "\teval returned: " << (rv ? "true" : "false"); if (rv != met) { met = rv; qDebug() << "\tgonna fire condition changed event ..."; emit conditionChanged(met); } }
The working EqualCondition:
EqualCondition::EqualCondition(ValueModel* model, const QVariant& value, QObject *parent) : AbstractCondition(model, value, parent) { } bool EqualCondition::eval() { qDebug() << "EqualCondition::eval() ..."; if (model()->getValue().type() == value().type()) return model()->getValue() == value(); else return false; }
the working Testcase:
class TestEngine : public QObject { Q_OBJECT public slots: void updateCondition(bool result) { this->result = result; }; private slots: void testEqualCondition(); void testSmallerCondition(); void testGreaterCondition(); private: volatile bool result; };
void TestEngine::testEqualCondition() { ValueModel vm("equal", 3); EqualCondition c(&vm, 7); connect(&c, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); QCOMPARE(result, false); vm.setValue(7); QCOMPARE(result, true); this->disconnect(&c, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); }
ValueModel is a wrapper around a QVariant which emits valueChanged events when the value changes.
The failing testcase for SmallerCondition:
void TestEngine::testSmallerCondition() { ValueModel v1("small", 3.14); SmallerCondition cS(&v1, 2.0); connect(&cS, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); QCOMPARE(result, false); v1.setValue(3.15); QCOMPARE(result, true); this->disconnect(&cS, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); }
and finally the smaller condition implementation:
SmallerCondition::SmallerCondition(ValueModel* model, const QVariant& value, QObject *parent) : AbstractCondition(model, value, parent) { } bool SmallerCondition::eval() { qDebug() << "SmallerCondition::eval() ..."; switch (model()->getValue().type()) { case QMetaType::Char: case QMetaType::Short: case QMetaType::Int: case QMetaType::Long: case QMetaType::LongLong: qDebug() << "compare signed integers (<): " << model()->getValue().toLongLong() << "\tvalue: " << value().toLongLong(); return model()->getValue().toLongLong() < value().toLongLong(); case QMetaType::UChar: case QMetaType::UShort: case QMetaType::UInt: case QMetaType::ULong: case QMetaType::ULongLong: qDebug() << "compare unsigned integers (<): " << model()->getValue().toLongLong() << "\tvalue: " << value().toLongLong(); return model()->getValue().toULongLong() < value().toULongLong(); case QMetaType::Double: qDebug() << "compare decimals (<): " << model()->getValue().toDouble() << "\tvalue: " << value().toDouble(); return model()->getValue().toDouble() < value().toDouble(); } return false; }
Following the debug output the smallercondition does not receive a valueChanged event and for so evaluation is not performed.
What am I missing/doing wrong?
-
What's your implementation of ValueModel? Where do you emit the valueChanged signal?
And please provide a minimal example, not your whole code so it gets easier to debug your problem. -
ValueModel
ValueModel::ValueModel(const QString& name, const QVariant& value) : QObject(nullptr) , v(value) , n(name) { } void ValueModel::setValue(const QVariant& value) { if (v != value) { v = value; emit valueChanged(v); } }
@Christian-Ehrlicher said in problems with signal/slot:
And please provide a minimal example, not your whole code so it gets easier to debug your problem.
Less than the posted code?
That's not possible! -
I recently was able to debug the testcase and it looks like AbstractCondition::update runs into the part to emit conditionChanged - but that event is not fired.
Although I connected the signal with the updateCondition method.
There was no error message at console about failed connect, so I expect that it should work.
-
ValueModel
ValueModel::ValueModel(const QString& name, const QVariant& value) : QObject(nullptr) , v(value) , n(name) { } void ValueModel::setValue(const QVariant& value) { if (v != value) { v = value; emit valueChanged(v); } }
@Christian-Ehrlicher said in problems with signal/slot:
And please provide a minimal example, not your whole code so it gets easier to debug your problem.
Less than the posted code?
That's not possible!@django-Reinhard said in problems with signal/slot:
That's not possible!
It is.... But it's up to you. I won't debug your code above.
-
Hi,
I'm working on conditional constructs, that update the result based on valueChanged signals.
I have an abstract base class, which registers itself for valueChanged events. In response to that event the class calls a virtual abstract memberfunction and if the result differs from previous evaluation it vomits a signal.
In my testcase only EqualCondition works as expected. Trying Smaller- or GreaterCondition fails before the evaluation is performed.
Actually I don't understand the behaviour and for that, I don't know what to change or where to search for alternatives. I tried to debug testcases, but that does not work.
(I use qtcreator and qt5.15 on linux debian 11)So I hope, someone can shine me a light ...
The abstract declaration:
class AbstractCondition : public QObject { Q_OBJECT public: explicit AbstractCondition(ValueModel* model, const QVariant& value, QObject *parent = nullptr); bool result() { return met; } public slots: virtual bool eval() = 0; virtual void update(); signals: void conditionChanged(bool result); protected: ValueModel* model() { return m; } QVariant value() { return v; } private: ValueModel* m; QVariant v; bool met; };
the corresponding implementation:
AbstractCondition::AbstractCondition(ValueModel* model, const QVariant& value, QObject *parent) : QObject(parent) , m(model) , v(value) { connect(m, &ValueModel::valueChanged, this, &AbstractCondition::update); } void AbstractCondition::update() { qDebug() << "AbstractCondition::update() ..."; bool rv = eval(); qDebug() << "\teval returned: " << (rv ? "true" : "false"); if (rv != met) { met = rv; qDebug() << "\tgonna fire condition changed event ..."; emit conditionChanged(met); } }
The working EqualCondition:
EqualCondition::EqualCondition(ValueModel* model, const QVariant& value, QObject *parent) : AbstractCondition(model, value, parent) { } bool EqualCondition::eval() { qDebug() << "EqualCondition::eval() ..."; if (model()->getValue().type() == value().type()) return model()->getValue() == value(); else return false; }
the working Testcase:
class TestEngine : public QObject { Q_OBJECT public slots: void updateCondition(bool result) { this->result = result; }; private slots: void testEqualCondition(); void testSmallerCondition(); void testGreaterCondition(); private: volatile bool result; };
void TestEngine::testEqualCondition() { ValueModel vm("equal", 3); EqualCondition c(&vm, 7); connect(&c, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); QCOMPARE(result, false); vm.setValue(7); QCOMPARE(result, true); this->disconnect(&c, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); }
ValueModel is a wrapper around a QVariant which emits valueChanged events when the value changes.
The failing testcase for SmallerCondition:
void TestEngine::testSmallerCondition() { ValueModel v1("small", 3.14); SmallerCondition cS(&v1, 2.0); connect(&cS, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); QCOMPARE(result, false); v1.setValue(3.15); QCOMPARE(result, true); this->disconnect(&cS, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); }
and finally the smaller condition implementation:
SmallerCondition::SmallerCondition(ValueModel* model, const QVariant& value, QObject *parent) : AbstractCondition(model, value, parent) { } bool SmallerCondition::eval() { qDebug() << "SmallerCondition::eval() ..."; switch (model()->getValue().type()) { case QMetaType::Char: case QMetaType::Short: case QMetaType::Int: case QMetaType::Long: case QMetaType::LongLong: qDebug() << "compare signed integers (<): " << model()->getValue().toLongLong() << "\tvalue: " << value().toLongLong(); return model()->getValue().toLongLong() < value().toLongLong(); case QMetaType::UChar: case QMetaType::UShort: case QMetaType::UInt: case QMetaType::ULong: case QMetaType::ULongLong: qDebug() << "compare unsigned integers (<): " << model()->getValue().toLongLong() << "\tvalue: " << value().toLongLong(); return model()->getValue().toULongLong() < value().toULongLong(); case QMetaType::Double: qDebug() << "compare decimals (<): " << model()->getValue().toDouble() << "\tvalue: " << value().toDouble(); return model()->getValue().toDouble() < value().toDouble(); } return false; }
Following the debug output the smallercondition does not receive a valueChanged event and for so evaluation is not performed.
What am I missing/doing wrong?
-
Hi,
You're checking for almost all numerical types except float.
-
@Christian-Ehrlicher said in problems with signal/slot:
btw: This value is uninitialized.
@SGaist said in problems with signal/slot:
You're checking for almost all numerical types except float.
Both issues are fixed.
I uploaded a stripped testprojectConsole output with debug messages enabled:
QDEBUG : TestEngine::testEqualCondition() AbstractCondition::update() ... QDEBUG : TestEngine::testEqualCondition() EqualCondition::eval() ... QDEBUG : TestEngine::testEqualCondition() eval returned: false QDEBUG : TestEngine::testEqualCondition() gonna fire condition changed event ... QDEBUG : TestEngine::testEqualCondition() AbstractCondition::update() ... QDEBUG : TestEngine::testEqualCondition() EqualCondition::eval() ... QDEBUG : TestEngine::testEqualCondition() eval returned: true QDEBUG : TestEngine::testEqualCondition() gonna fire condition changed event ... PASS : TestEngine::testEqualCondition() QDEBUG : TestEngine::testSmallerCondition() AbstractCondition::update() ... QDEBUG : TestEngine::testSmallerCondition() SmallerCondition::eval() ... QDEBUG : TestEngine::testSmallerCondition() compare decimals (<): 3.14 value: 2 QDEBUG : TestEngine::testSmallerCondition() eval returned: false QDEBUG : TestEngine::testSmallerCondition() gonna fire condition changed event ... FAIL! : TestEngine::testSmallerCondition() Compared values are not the same Loc: [../Scratch/src/test/testengine.cpp(50)] QDEBUG : TestEngine::testGreaterCondition() AbstractCondition::update() ... QDEBUG : TestEngine::testGreaterCondition() GreaterCondition::eval() ... QDEBUG : TestEngine::testGreaterCondition() compare signed integers (>): 97 value: 114 QDEBUG : TestEngine::testGreaterCondition() eval returned: false QDEBUG : TestEngine::testGreaterCondition() gonna fire condition changed event ... FAIL! : TestEngine::testGreaterCondition() Compared values are not the same Loc: [../Scratch/src/test/testengine.cpp(65)]
which shows, that EqualCondition emits the signal, whereas SmallerCondition and GreaterCondition don't emit the signal.
-
volatile bool result;
still an uninitialized variable and what should
volatile
do here? You should alAnd wrt your problem. Your conditionChanged event is only fired when the condition changes which is not the case for testSmallerCondition(). Also you can't catch the first conditionChanged event in each of your functions because it's fired in the ctor when you not yet connect the signal.
-
@Christian-Ehrlicher said in problems with signal/slot:
still an uninitialized variable
That may be true, but I don't use the variable before it is set.
@Christian-Ehrlicher said in problems with signal/slot:
what should volatile do here?
It was a try to disable possible compiler optimizations ...@Christian-Ehrlicher said in problems with signal/slot:
Also you can't catch the first conditionChanged event in each of your functions because it's fired in the ctor when you not yet connect the signal.
If that would be true, then please explain, why does it work for EqualCondition? It does not accidently pass the test.
Since the beginning this test always has been passed. -
@Christian-Ehrlicher said in problems with signal/slot:
still an uninitialized variable
That may be true, but I don't use the variable before it is set.
@Christian-Ehrlicher said in problems with signal/slot:
what should volatile do here?
It was a try to disable possible compiler optimizations ...@Christian-Ehrlicher said in problems with signal/slot:
Also you can't catch the first conditionChanged event in each of your functions because it's fired in the ctor when you not yet connect the signal.
If that would be true, then please explain, why does it work for EqualCondition? It does not accidently pass the test.
Since the beginning this test always has been passed.@django-Reinhard said in problems with signal/slot:
It does not accidently pass the test.
It does because your setup is different there. Your second call to setValue() actually changes the condition whereas it does not for testSmallerCondition()
-
You where right indeed!
I changed the testcase like this:
ValueModel v("small", 3.14); SmallerCondition c(&v, 2.0); connect(&c, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); QCOMPARE(result = c.result(), false); v.setValue(1.15); QCOMPARE(result, true); this->disconnect(&c, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition);
and now it works.
So the call from constructor can't change testresult - but with Equalcondition it did it nevertheless ...Anyway - I understand the source of evil and with the changed code it works reliably.
Thank you!
-
You where right indeed!
I changed the testcase like this:
ValueModel v("small", 3.14); SmallerCondition c(&v, 2.0); connect(&c, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition); QCOMPARE(result = c.result(), false); v.setValue(1.15); QCOMPARE(result, true); this->disconnect(&c, &AbstractCondition::conditionChanged, this, &TestEngine::updateCondition);
and now it works.
So the call from constructor can't change testresult - but with Equalcondition it did it nevertheless ...Anyway - I understand the source of evil and with the changed code it works reliably.
Thank you!
@django-Reinhard said in problems with signal/slot:
but with Equalcondition it did it nevertheless ...
No it does not.