How to create dynamic bindings in ListModel?
-
In small example below, I'm trying to create rectangles whose
widthdepends onfoo. As we all know, we cannot use property binding inListElementbecauseListElementdoesn't allow to use script for property value.import QtQuick 2.0 import QtQuick.Controls Window { width: 400 height: 300 visible: true property int foo: 10 ListModel { id: listModel // ListElement { // width: foo // error: ListElement: cannot use script for property value // } } Row { anchors.top: parent.top Repeater { id: repeater model: listModel delegate: Rectangle { width: model.width height: 50 color: "green" } } } Button { text: "add item" anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter onClicked: { listModel.append({ "width": foo // works, but is not a binding }) } } }In order to use script, I have to try to
appendin function. However, in this way, iffoois changed somewhere, thewidthof rectangles won't change accordingly. So anyone knows a better solution? -
In small example below, I'm trying to create rectangles whose
widthdepends onfoo. As we all know, we cannot use property binding inListElementbecauseListElementdoesn't allow to use script for property value.import QtQuick 2.0 import QtQuick.Controls Window { width: 400 height: 300 visible: true property int foo: 10 ListModel { id: listModel // ListElement { // width: foo // error: ListElement: cannot use script for property value // } } Row { anchors.top: parent.top Repeater { id: repeater model: listModel delegate: Rectangle { width: model.width height: 50 color: "green" } } } Button { text: "add item" anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter onClicked: { listModel.append({ "width": foo // works, but is not a binding }) } } }In order to use script, I have to try to
appendin function. However, in this way, iffoois changed somewhere, thewidthof rectangles won't change accordingly. So anyone knows a better solution?@Kamichanw you could use
setPropertyto update your model values. Do this in a function that you call from aon<Property>Changedhandler or use aConnection, whatever makes more sense for your use case. -
In small example below, I'm trying to create rectangles whose
widthdepends onfoo. As we all know, we cannot use property binding inListElementbecauseListElementdoesn't allow to use script for property value.import QtQuick 2.0 import QtQuick.Controls Window { width: 400 height: 300 visible: true property int foo: 10 ListModel { id: listModel // ListElement { // width: foo // error: ListElement: cannot use script for property value // } } Row { anchors.top: parent.top Repeater { id: repeater model: listModel delegate: Rectangle { width: model.width height: 50 color: "green" } } } Button { text: "add item" anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter onClicked: { listModel.append({ "width": foo // works, but is not a binding }) } } }In order to use script, I have to try to
appendin function. However, in this way, iffoois changed somewhere, thewidthof rectangles won't change accordingly. So anyone knows a better solution?@Kamichanw My first instinct would be to not do that.
The philosophy I adhere to when thinking about QML is that the QML objects are a representation of my data, and a way to interact with it. They are not my data.
In your case what does the width of your rectangle represents?
Some time ago I had to develop a UI editor in QML, in this case the state of the UI and where elements were placed was my data.
It was provided as a model and represented by Rectangle delegates.
The delegates where placed according to the model data and when I resized a Rectangle it actually modified the underlying data and the delegate would then update. I never modified my QML items directly, everything was in a model.Here's a video of its PoC : https://gfycat.com/scholarlythosefly
-
@Kamichanw My first instinct would be to not do that.
The philosophy I adhere to when thinking about QML is that the QML objects are a representation of my data, and a way to interact with it. They are not my data.
In your case what does the width of your rectangle represents?
Some time ago I had to develop a UI editor in QML, in this case the state of the UI and where elements were placed was my data.
It was provided as a model and represented by Rectangle delegates.
The delegates where placed according to the model data and when I resized a Rectangle it actually modified the underlying data and the delegate would then update. I never modified my QML items directly, everything was in a model.Here's a video of its PoC : https://gfycat.com/scholarlythosefly
@GrecKo Your point is so elegant, especially when we need to change a lot of data in
ListModel, but I still have some scenarios that make me have to changeListModelslightly and in low cost. For example, when I develope a maximize/restore button on title bar, I hope the icon url can be changed dynamically according to thevisiblityofWindow. My solution is useQt.binding()in js funcion.icon = Qt.binding(() => wnd.visibility === Window.Maximized ? "qrc:/icons/restore.svg" : "qrc:/icons/maximize.svg")For the scenario in the question, we can
// other code... Button { text: "add item" anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter onClicked: { listModel.append({ "width": foo }) listModel.get(0).width = Qt.binding(() => return foo) } } // other code... -
@GrecKo Your point is so elegant, especially when we need to change a lot of data in
ListModel, but I still have some scenarios that make me have to changeListModelslightly and in low cost. For example, when I develope a maximize/restore button on title bar, I hope the icon url can be changed dynamically according to thevisiblityofWindow. My solution is useQt.binding()in js funcion.icon = Qt.binding(() => wnd.visibility === Window.Maximized ? "qrc:/icons/restore.svg" : "qrc:/icons/maximize.svg")For the scenario in the question, we can
// other code... Button { text: "add item" anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter onClicked: { listModel.append({ "width": foo }) listModel.get(0).width = Qt.binding(() => return foo) } } // other code...@Kamichanw https://doc.qt.io/qt-6/qml-qtqml-models-listmodel.html#get-method
Warning: The returned object is not guaranteed to remain valid. It should not be used in property bindings.
for the icon you can do a declarative binding, no need for
Qt.binding.Without more context we can't really help you. The code you pasted is not something that I would find good.
When is foo modified and why can't you modify the model instead?
If you don't want that to be in a model why don't you bind towidth: fooin your delegates instead? -
@Kamichanw https://doc.qt.io/qt-6/qml-qtqml-models-listmodel.html#get-method
Warning: The returned object is not guaranteed to remain valid. It should not be used in property bindings.
for the icon you can do a declarative binding, no need for
Qt.binding.Without more context we can't really help you. The code you pasted is not something that I would find good.
When is foo modified and why can't you modify the model instead?
If you don't want that to be in a model why don't you bind towidth: fooin your delegates instead?@GrecKo Well, here is the thing. I'm writing a custom title bar, which enables users to add new buttons. I use
Repeaterwhose model is aListModelto generate buttons. In order to specify behaviors ofonClicked,onContainsMouseChangedandonContainsPressChanged, I also maintain a list ofobjectcalledcallbacksinRepeater. When I intent to add maximize/restore buttontitle.appendButton({ "name": "Maximize", "icon": "qrc:/icons/maximize.svg", "visible": Boolean(titleButton & ShadowWindow.TitleButton.Maximize), "tooltip": "Maximize" }, button => { button.icon.source = Qt.binding(() => control.visibility === Window.Maximized ? "qrc:\icons\restore.svg" : "qrc:\icons\maximize.svg") button.toolTip.text = Qt.binding(() => control.visibility === Window.Maximized ? "Restore" : "Maximize") if (control.visibility === Window.Maximized) control.showNormal() else control.showMaximized() })As you can see, I set
Qt.bindinginonClicked. Do you think it is a good way to settoolTip.textandicon.sourcedirectly inonClicked? Like thisif (control.visibility === Window.Maximized) { button.toolTip.text = "Maximize" button.icon.source = "qrc:/icons/maximize.svg" control.showNormal() } else { button.toolTip.text = "Restore" button.icon.source = "qrc:/icons/restore.svg" control.showMaximized() }Besides, I still have a problem. I encapsulated a series of initialization in the
init()function. Due to uncertainty of order ofComponentcompletion, I have to make user callinit()at the beginning of theirComponent.onCompleted, do you have a better solution?