15

I want to create a custom QML component with two properties one and two, which should have default values when left uninitialized. In particular, if two should get an initial value depeding on one. The following code

Rectangle {
  property int one: 1
  property int two: 2 * one
}

however creates a property binding: Whenever one changes, two is updated to the new value of 2 * one. How can I initialize two to the value of 2 * one without creating a binding?

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Tobias
  • 6,388
  • 4
  • 39
  • 64
  • 5
    one way you can do is initialize it manually on Component.onCompleted, but that does not seems ideal way – Kunal Oct 24 '13 at 01:15

5 Answers5

12

A way to explicitly tell that you don't want a binding is to call an assignment in an expression block:

Rectangle {
  property int one: 1
  property int two: {two = 2 * one}
}

Unlike the approach of breaking the binding in onCompleted, the expression block avoids the creation and then destruction of a binding object, and it looks cleaner.

David Foley
  • 121
  • 1
  • 4
  • Very clever answer. I've always been dealing with this problem. Many times, I get into a binding loop because of initialization binding. This way, I have a very clean code, without ugly onCompleted tricks. Thank you very much. Big like! – Soheil Armin Sep 06 '21 at 16:35
8

Explicitly break the binding upon component completion:

Rectangle {
    property int one: 1
    property int two: 2 * one
    Component.onCompleted: two = two
}

The two = two assignment breaks the binding and two is no longer updated as one changes.

pixelgrease
  • 1,940
  • 23
  • 26
4

Double check that there is not need to Binding and be careful about not making codes dirty.
You can fill property with value very soon as follows:

window {
    id: win
    width: 300; height: 450
    color: "#d8d8d8"
    Item {
        property int val1
        property int val2
        property int val3: parent.width    //<-- Binding
        Component.onCompleted: {
            val1 = win.width;    //<---|
            val2 = win.height;   //<---|=== There is no binding. Just changes value
            /* ... */
        }
    }
}

(I am not sure, you may be able to set initial value using Component.onStatusChanged and Component.Ready status)

Notice for Performance: Signal and Javascript codes have some performance impact. It may be more performant to use bindings. Use Profiler to check that. If you want to set initial values of multiple property or you have already used onCompleted signal, so this will improve the performance!

S.M.Mousavi
  • 5,013
  • 7
  • 44
  • 59
0

Short answer: Don't define them ;)

Binding is created automatically if property initial value statement depends on other properties. You have no control over this behavior but there are at least 2 ways to avoid it.

1. Do not refer to other properties when providing initial value

Rectangle {
    property int one: 1
    property int two: 2 // == 2 * one
}

If value initialization is all you care about then values of all properties are known for that moment and you can simply provide proper number literals.

2. Leave property uninitialized, then setup values once object is created

Rectangle {
    property int one: 1
    property int two
    Component.onCompleted: {
        two = 2 * one
    }
}

I would recommend method 1 whenever it's feasible and if not fallback to method 2. Although it's more typing you won't mistake initial value setting for binding definition.

Keep in mind that it will no help if you put statement for property initial value in curly brackets or even call function there. As long as expression contains other properties names there will be binding created. So if you don't need it, use 2 methods above.

Referrences:

-2

In fact, you just shouldn't. Binding is the base behaviour of QML, if you try to avoid it, then that's because you're not thinking the good way.

For exemple, if property two initial value is calculated with property one initial value but not property one value,

Then that mean you want to bind on Initial value not value, you should create a readonly property which value will be property one initial value :

readonly property int initialOne : 1;
property int one : initialOne;
property int two : 2 * initialOne;

It could seem a little heavy, but if you think about it, the initial value is what you wanna use, and so, the concept of the property is what you really want

TheBootroo
  • 7,408
  • 2
  • 31
  • 43
BlueMagma
  • 2,392
  • 1
  • 22
  • 46
  • What if initialOne depends on parent.width (for example) like readonly property int initialOne : parent.width? – Korchkidu Jul 10 '15 at 10:13
  • If I can avoid unnecessary bindings in some situations, so why should I have? In some situations there is not need to binding. Imagine creating an app with explicit or unchangeable size (like what is happens on mobile devices). In this case there is no need to binding width or height and so on. You might need to change a property in moment and it is not important any later value changes. Imagine using `PropertyChanges` and value changes commonly. Is binding and its overhead necessary?! – S.M.Mousavi Mar 28 '16 at 06:33
  • @S.M.Mousavi : no you are right, binding are not always necessary. But Qml property are not the same as variables, when you defined a property base on a calculation. you are defining the relation between these two property. If you want a property to have a value based on a calculation, that won't change later on, you shouldn't use property, you should use a variable – BlueMagma Mar 31 '16 at 08:26
  • @BlueMagma : and you are right! But as far as I remember, documentations suggests for avoiding global javascript variables for performance issues and using QML properties instead. Also see this http://stackoverflow.com/a/20934781/1074799 – S.M.Mousavi Mar 31 '16 at 08:54