Details
-
Bug
-
Resolution: Done
-
P2: Important
-
4.7.1
-
None
-
git clone of qt 4.7 branch
-
418bd7fa550da97ac27a34c72e75ec7ab0448d78
Description
I have the following structure:
- QDeclarativePropertyMap with a property "my_prop"
- QDeclarativePropertyMap::valueChanged(const QString & key) connected to a slot
Now,
- I set new value to this property from qml (basically, by binding to another property of a different object)
- the signal fires and slot is executed
- but if I check value of "my_prop" in this slot, I still see the old value
Basically this means that in the slot called as result of valueChanged() there is no way to get to know what this new value will be (ofc, valueChanged() argument informs about the key, i.e. property which is being changed, and not about the new value).
I think in all other cases in Qt if a slot is connected to some valueChanged() signal, then this slot would already see the new value. So besides lacking functionality (no info about new value when we are in the slot, which could in principle be covered by e.g. extra argument "new_value" of the signal), this behavior goes against a natural expectation a Qt developer would have.
===========================
Now, the signal is emitted from
void QDeclarativePropertyMapPrivate::emitChanged(const QString &key)
{
Q_Q(QDeclarativePropertyMap);
emit q->valueChanged(key);
}
which is called from
void QDeclarativePropertyMapMetaObject::propertyWrite(int index)
{
priv->emitChanged(QString::fromUtf8(name(index)));
}
Now, propertyWrite is called only from the base class:
int QDeclarativeOpenMetaObject::metaCall(QMetaObject::Call c, int id, void **a) { if (( c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) && id >= d->type->d->propertyOffset) { int propId = id - d->type->d->propertyOffset; if (c == QMetaObject::ReadProperty) { propertyRead(propId); *reinterpret_cast<QVariant *>(a[0]) = d->getData(propId); } else if (c == QMetaObject::WriteProperty) { if (d->data[propId].first != *reinterpret_cast<QVariant *>(a[0])) { propertyWrite(propId); d->writeData(propId, *reinterpret_cast<QVariant *>(a[0])); activate(d->object, d->type->d->signalOffset + propId, 0); } } return -1; } else { if (d->parent) return d->parent->metaCall(c, id, a); else return d->object->qt_metacall(c, id, a); } }
It seems that basically the problem is that in this piece of code propertyWrite(propId) is called (and thus triggers signal emission) BEFORE d->writeData() is called.
===========================
The default implementation of QDeclarativeOpenMetaObject::propertyRead() is empty and as far as I can tell it is redefined only once, in QDeclarativePropertyMapMetaObject. From this point of view, one solution would (probably) be to change the order of lines in QDeclarativeOpenMetaObject::metaCall so that the relevant part looked like this:
if (d->data[propId].first != *reinterpret_cast<QVariant *>(a[0])) {
d->writeData(propId, *reinterpret_cast<QVariant *>(a[0]));
propertyWrite(propId);
activate(d->object, d->type->d->signalOffset + propId, 0);
}
But the question is what was the original intention of propertyWrite(): maybe in principle there are some cases where one would like to make preliminary preparation work before "d->writeData"? In this case I would suggest having two functions, beforePropertyWrite() and afterPropertyWrite() and have code like this:
if (d->data[propId].first != *reinterpret_cast<QVariant *>(a[0])) {
beforePropertyWrite(propId);
d->writeData(propId, *reinterpret_cast<QVariant *>(a[0]));
afterPropertyWrite(propId);
activate(d->object, d->type->d->signalOffset + propId, 0);
}
(or something in this style). If so, then QDeclarativePropertyMapMetaObject::propertyWrite(int index) would become QDeclarativePropertyMapMetaObject::afterPropertyWrite(int index) (and NOT beforePropertyWrite()).