Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dealing with the "depends on non-NOTIFYable properties" warning

I have a C++ object exposed to QML with a "sort of" read-only property, except that the property still needs to be set from QML, so it has a WRITE method defined, but other than the initial mandatory setting it never changes, so I feel like a NOTIFY is redundant, as it will already have that value set at the time of its usage, and it will never change.

However, QML doesn't share my feeling, and regardless it spews out a "Expression depends on non-NOTIFYable properties" warning.

Due to the way objects are instantiated using a property is the only applicable way of setting that value, it is not possible to use an invokable setter as such would require the object to already be "completed" and it cannot really complete without that value. Thus the need of the property mechanism and a WRITE method, which unfortunately leads Qt to believe that the property will change. I tried setting the property as CONSTANT but that doesn't seem to stack with having a WRITE method.

I've put a dummy signal as a temporary measure to deal with the flood of warnings, but I'd prefer if there was a more elegant way to deal with this. Again, the very design practically eliminates the possibility of setting this value after its initial setting, but since it is a C++ object and the value needs to be set on a per-object basis, it is not applicable to use a QML readonly property either. Is there a work-around for this that doesn't involve the redundant signal?


Just to clarify, the underlying problem here is that in its current state, QML has pretty much no way to initialize a read only property of an object, implemented in C++. Such a property wouldn't have a setter (if it would then it would spew the "depends on..." warning), and there is no way to pass a value to the object's constructor. As described in my answer below, there are hacks around this, but they are either limited in application or rather inconvenient. In C++ you can do even initialization of const members on a per-instance basis by passing the value to the constructor which does the initialization in the initializer list, but not in QML. Even in pure QML, read only properties must be initialized on the spot, it is not possible to leave them uninitialized (readonly property type name without : statement) and delay that until the instantiation and get per-instance initialization (Obj { name : initValue }). You could do this:

Item { // T.qml
  readonly property int r : w
  property int w
}
...
T { w: value }

but it... kind of defeats the purpose... And considering that a property can be "bound" only once in the body of its instantiation, I think this could be seen as an act of initialization, while further assignment or imperative rebinding (Qt.binding()) is prohibited. A setter without the redundant notification signal would do the trick, however qtquick's implementation design assumes that if it has a setter it will be changing, thus will complain that it doesn't have a notify signal, which IMO is a design oversight - that is, not providing any way to initialize a read only property on the QML side on per-instance basis. And currently, as I already mentioned, the 3 possible solutions come either with overheads (have a notify signal), limitations (set value before object creation - only applicable for dynamic instantiation) or major inconveniences (use setter and getter slots without implementing the actual property interface).

like image 803
dtech Avatar asked Dec 17 '15 16:12

dtech


1 Answers

It depends a bit on what you need the value for.

If the property is used in any property binding then you will need the notification, otherwise you end up in a situtation where you rely on the order of object instantiations and binding evaluations.

E.g. if it is used in a binding elsewhere without having an associated notification signal, then you rely on the initial setting happending before any such binding is evaluated for the first time.

If, on the other hand, the value is only used to initialize the C++ side but never used in QML elsewhere, you could consider using a "setter" function/slot.

like image 99
Kevin Krammer Avatar answered Dec 11 '22 07:12

Kevin Krammer