Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-9386

QDeclarativePropertyMap::valueChanged() emitted too early

    XMLWordPrintable

Details

    • 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()).

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            brasser Michael Brasser (closed Nokia identity) (Inactive)
            wiecko Marek Wieckowski
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes