QML update the property of Item inside the ListView - qt

Item {
Component.onComplete: {
for (var i=0;i < 10;i++) {
myModel.append({"myTxt": "SomeThing"+i});
}
}
ListModel {
id: myModel
}
ListView {
id: listView
width: parent.width
height: parent.height
model: myModel
delegate: Rectangle {
property string propItemState: 0;
MyListItem {
id: test
itemText: myText
itemState: propItemState;
}
}
}
}
I want to update the propItemState once the list is displayed.
For that, I have tried the below method but am getting an undefined error.
I am calling this method once the list model is updated.
function updateListItems() {
for (var index=0;index < listView.count;index++) {
console.log("propItemState: "+listView.contentItem.children[index].propItemState);
listView.contentItem.children[index].propItemState = 2;
}
}

You can bind propItemState to something outside of your delegate. Or you can add signal handler (Connections), which listens to your model (for example; or to some another class) and changes state when your conditions met.
Example:
Item {
Component.onComplete: {
for (var i=0;i < 10;i++) {
myModel.append({"myTxt": "SomeThing"+i});
}
internal.state = "1";
}
ListModel {
id: myModel
}
QtObject {
id: internal
property string state: "0"
}
ListView {
id: listView
width: parent.width
height: parent.height
model: myModel
delegate: Rectangle {
property string propItemState: internal.state
MyListItem {
id: test
itemText: myText
itemState: propItemState;
}
}
}
}

Related

Dynamic component inside a scrollview can't work

If I replace component with Rectangle, then the scrollview works.....
Any idea? How to include dynamic parts in ScrollView?
Thanks in advance
ScrollView {
id:template1
contentHeight: 2000
....
Component {
id: routeComp
CruiseRouteButton {
}
}
function createTab() {
}
Component.onCompleted: {
for(var i = 0; i < mainWnd.config.routes.length; i++) {
....
let t = routeComp.createObject(template1, cfg);
routes[i] = t
}
createTab()
}
This component is used to dynamically set some routes for user to choose

Qt QML change item of a page dynamically

I have BasePage.qml like this:
Item {
property alias content: loader.sourceComponent
signal topBarLeftButtonClicked()
TopBar {
...
}
Loader {
id: loader
}
BottomBar {
...
}
}
This way I can change dynamically the content of the page, but I must use Component, and I can't read properties of the content in a DerivedPage.
For example:
DerivedPage.qml
BasePage {
onTopBarLeftIconClicked: item.text //error, item is not defined
content: Component {
TextField {
id: item
}
}
}
Any solution?
You can define an alias to the Loader's item property inside BasePage, like that:
property alias contentItem: loader.item
And refer to it instead of content item within DerivedPage.
Putting it all together:
// BasePage.qml
Item {
property alias content: loader.sourceComponent
property alias contentItem: loader.item
signal topBarLeftButtonClicked()
Loader { id: loader }
}
// DerivedPage.qml
BasePage {
onTopBarLeftIconClicked: { contentItem.text = "clicked" }
content: Component { TextField { } }
}
I found a way to replace loader and components:
//BasePage.qml
Item {
default property alias data: item.data
signal topBarLeftButtonClicked()
TopBar {
...
}
Item{
id: item
}
BottomBar {
...
}
}
//DerivedPage.qml
BasePage {
onTopBarLeftIconClicked: textField.text = "string"
TextField {
anchors.fill: parent
id: textField
}
}
This way textField replace item in BasePage.qml

How to dynamically update a ListView in QML

I have a ListView that displays a list of all notifications for a user. Right now since I'm using Component.onCompleted, if the list updates, the new list is not displayed, but the one which existed during instantiation. How could we solve this? Would using a Loader with a separate component instead help?
property int numNotifications: backend_service.num_notifications
property var notifications: []
onNumNotificationsChanged: {
for(var x=0; x<numNotifications; x++) {
var notif = backend_service.notifications.get(x);
notifications.push(notif)
}
}
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
color: "black"
height: 500
width: 0.95 * parent.width
ListView {
anchors.fill: parent
model: notifModel
delegate: notifDelegate
}
}
ListModel {
id: notifModel
Component.onCompleted: {
for(var i in notifications) {
notifModel.append({"name": notifications[i]})
}
}
}
Component {
id: notifDelegate
Row {
spacing: 10
Text { text: name; color: "white" }
}
}
Component.onCompleted only runs when the object is built and never again. So using that method to add items to the model is useless, instead you should use the function that reports the new data:
onNumNotificationsChanged: {
for(var x=0; x<numNotifications; x++) {
var notif = backend_service.notifications.get(x);
notifModel.append({"name": notif})
}
}

Qml: How to set a property to all GridView items

I am trying to make a change to all items of a GridView.
I have tried to iterate through either the model or the grid, I looked at similar examples on the web, but everything I try ends with Cannot read property 'buttonText' of undefined.
It seems to me that the problem is that the interpreter can't figure out that the item from the grid or model is a Button. But I don't know how to cast it.
If I change the log to only display the item, not any property, (see code snippet), it seems that it knows it is an item... see my experiments below.
The only thing I can make work is set a property (or call a signal, or a function) from the delegate. But that only affects one grid item, not all.
How can I set a property on every item of the grid ? Alternatively, how can I send a signal, or call a function, on every item?
My experiments are in function changeEverythingFunction()
file: Button.qml
Item
{
id: itemButton
signal changeEverything
property int buttonIndex
property string buttonText
...
}
file: Model.qml
Item
{
id: modelItem
ListModel
{
id: listModel
}
property int buttonCount: listModel.count
function changeEverythingFunction()
{
// for (var i = 0; i < buttonCount; i++)
// listModel.setProperty(i, buttonText, "abc")
for(var childIndex in gridItems.contentItem.children)
{
console.log(listModel.get(childIndex).buttonText) // Cannot read property 'buttonText' of undefined
console.log(gridItems.contentItem.children[childIndex].buttonText) // Cannot read property 'buttonText' of undefined
console.log(gridItems.contentItem.children[childIndex]["buttonText"]) // undefined (I saw this in a SO example)
var item = gridItems.contentItem.children[childIndex]
console.log(item) // qml: QQuickItem(0xe496370)
}
}
MouseArea
{
....
Rectangle
{
...
GridView
{
id: gridItems
anchors.fill: parent
clip: true
model: listModel
delegate: Item
{
id: buttonDelegate
Button
{
buttonIndex: gridId
buttonText: itemText
onChangeEverything:
{
changeEverythingFunction();
}
}
}
}
}
}
}
Your approach is in the opposite direction: Your approach is to obtain the item of the view and modify it, but the approach that Qt points out is that the view reflects the information of the model and modifies it when necessary.
The following is a simple example where every time you press on the button with "change me" text increasing the number it shows, but if you press the button with "change all" text it will change all the numbers. As it is observed everything is done through the model, not through the view that are only used to display information or receive user interaction.
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
Window {
visible: true
width: 640
height: 480
ListModel{
id: listmodel
}
function changeAll(){
for(var i=0; i< listmodel.count; ++i){
listmodel.setProperty(i, "number", listmodel.get(i).number + 1)
}
}
GridView{
id: grid
anchors.fill: parent
clip: true
model: listmodel
cellHeight: 120
cellWidth: 120
delegate: Item {
width: grid.cellWidth; height: grid.cellHeight
Column {
anchors.fill: parent
Text { text: model.number; anchors.horizontalCenter: parent.horizontalCenter }
Button{text: "change me"; onClicked: model.number +=1}
Button{text: "change all"; onClicked: changeAll()}
}
}
}
Component.onCompleted: {
for(var i=0; i < 10; ++i){
listmodel.append({"number": 0});
}
}
}

How to know "y" property of an Item when it is created in a ListView

I use a ListView and I need to fill my Window with the last Item that was inserted inside it.
In this case, I set the ListView property: contentY with the y value of myItem
My problem is that when you insert a new Item, I don't know how to know the y value of the Item because it is not yet set.
This is what I try:
ScrollView {
id: scroll
ListView {
model: DelegateModel {
id: visualModel
model: myModel //Model is set in the cpp
delegate: Rectangle {
id: rectangleDelegate
Component.onCompleted: {
rectangleDelegate.ListView.view.contentY = rectangleDelegate.y
console.log(rectangleDelegate.y ) //Not set yet (y = 0)
}
Button {
onClicked {
rectangleDelegate.ListView.view.contentY = rectangleDelegate.y
console.log(rectangleDelegate.y ) //Now it is ok (y = rightValue)
}
}
}
}
}
}
How could I proceed ?
Thanks to #derM, here is a simple example that works:
ScrollView {
id: scroll
ListView {
model: DelegateModel {
id: visualModel
model: myModel //Model is set in the cpp
delegate: Rectangle {
id: rectangleDelegate
onYchanged: {
rectangleDelegate.ListView.view.contentY = rectangleDelegate.y
}
}
}
}
}

Resources