3

Assume we have the following custom QML Components.

MyComponent.qml

//Contents for MyComponent.qml
import QtQuick 2.0

QtObject{
    property real myProperty
    ...
}


Test.qml

import QtQuick 2.0

Item {
    property var myComponent: MyComponent
    onMyComponentChanged: console.log("MyComponent changed!");
}


When changing any of the properties in myComponent, I want onMyComponentChanged() to be triggered. What is the best way to accomplish this?

nusic
  • 371
  • 1
  • 3
  • 10

3 Answers3

2

In QML, most of the properties have onChanged events. For example;

MyComponent {
   property string name: "Super"
}

i.e, on + Property + Changed signal will be emitted (first letter of the property will be Upper case) - resulting in onNameChanged


Item {
    id: mainItem
    property MyComponent myCustomComponent: MyComponent {
       onNameChanged: mainItem.handleMyComponentChange()
    }
    function handleMyComponentChange() {
     -----
    }

    myCustomComponent.name="Duper"  // triggers handleMyComponentChange()
}
ramtheconqueror
  • 1,907
  • 1
  • 22
  • 35
1

There is kind of a limitation in QML in this regard.

Since your property is itself an object rather than a primitive, its changed signal will emit only when the property is changed to be assigned to another object, it WILL NOT reflect internal changes to this object. Also, you cannot manually emit the signal either, it will only automatically emit when the property is changed.

myComponent.myProperty = 1.1 // this will not trigger onMyComponentChanged
myComponent = anotherObject // this will trigger onMyComponentChanged

Since your component has only a single property and it already has a change notification, you can use that:

property var myComponent : myComp // you can omit that if you don't need it as a property 

MyComponent {
    id: myComp
    myProperty: 13.37
    onMyPropertyChanged: console.log("changed")
}

or...

property var myComponent : MyComponent {
    myProperty: 13.37
}

Connections {
    target: myComponent
    onMyPropertyChanged: console.log("changed")
}

If your component has multiple properties, you should implement a signal changed() in it and emit it on every property changed and use that signal to reflect internal changes instead of the one automatically generated by the QML property, which will not reflect them:

QtObject {
    property real myProperty
    property bool otherProperty

    onMyPropertyChanged: changed()
    onOtherPropertyChanged: changed()

    signal changed()
}

...

property var myComponent : MyComponent {
    myProperty: 13.37
}

Connections {
    target: myComponent
    onChanged: console.log("changed")
}
dtech
  • 47,916
  • 17
  • 112
  • 190
  • //this will not emit onMyComponentChanged(), this is a slot, you cant emit a slot, you should say emit myComponentChanged, also there is no such limitation you can emit when you want a property signal I will add an example in my answer. – Mido Aug 15 '15 at 13:54
  • @Mido - technically it is a handler, it is not a slot and it is not a function. However, it is "interesting" that you can emit a property change signal manually, that wasn't the case back when I was trying that for the first time... it was a long ago However, it sounds like a bug, you shouldn't really be able to emit a change notification without an actual change, it could lead to infinite loops. Also, unlike the handler, you don't get autocomplete for it, further leading be to believe it is not a feature. I will investigate the matter further when I find the time. – dtech Aug 15 '15 at 14:12
  • I used slot cause in c++ Qt , whe use signals/slot, yes it's a handler, I dont think it's a bug, it is useful to use it some time ☺ – Mido Aug 15 '15 at 14:15
  • @Mido - that was in Qt4, in Qt5 you can connect signals to any function, be that a free funciton, a member function or a lambda. In QML the handler effectively binds the signal to an expression, just a connection shortcut. – dtech Aug 15 '15 at 14:17
  • it could lead to infinite loop if it's wrongly used, I think you don't get autocomplete for it to avoid being wrongly used, but it exist and we can use it, in some cases I prefer not to create a signal and a function when I can use an existing signal and handler, specially when we are developing an app to be run on an embedded system where the performance is critical. – Mido Aug 15 '15 at 14:28
  • @Mido - manual emission of signals is only intended to happen outside of the context of changing properties. In the case of properties, the changed signal should only be emitted by the property setter. In QML that is either assigning or binding the property to a new value. I will contact the people working on QtQuick to see their take on the matter. – dtech Aug 15 '15 at 16:39
0

You can connect the signal myPropertyChanged to the signal myComponentChanged, the same of c++. to do that :

import QtQuick 2.2

Item {
    id:idRoot
    width: 800
    height: 480

    property var myComponent: Item
                                 {
                                   id:item
                                   property int    myProperty: 13
                                   // timer to change myProperty
                                   Timer{
                                        running: true
                                        interval: 2000
                                        repeat: true
                                        onTriggered: item.myProperty += 1
                                        }
                                    }
    onMyComponentChanged: console.log("MyComponent changed!");

    Component.onCompleted:{
        // connect signals
        // info: you can connect signal to signal or signal to function
        item.myPropertyChanged.connect(idRoot.myComponentChanged)
        // disconnect signals
        //item.myPropertyChanged.disconnect(idRoot.myComponentChanged)
    }
}

Edit for ddriver:

try this, you can see that I emit the signal without changing the property:

import QtQuick 2.2


Item
{
    id:idRoot
    width: 800
    height: 480
    // when you create a property its signal myPropertyChanged()
    // and its slot onMyPropertyChanged are created
    // you can emit this signal when you want
    property int myProperty: 13
    // timer to emit myPropertyChanged()
    Timer{
        running: true
        interval: 2000
        repeat: true
        onTriggered: myPropertyChanged()
    }

    onMyPropertyChanged:  console.log("MyProperty changed!",myProperty); 
}
Mido
  • 1,092
  • 1
  • 9
  • 14