Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

[Solved] Editing and saving indexed roles to Settings



  • Hi, I want to create a user editable menu.

    The idea is the user can edit the price, name and type of the label and when the app is closed/opened, the edited data remains.

    I'm struggling, as this involves indexes and I'm not sure how to access/edit/save these elements (roles of list element) and save them for next time the app is opened.

    What am I missing?, not doing?

    Here's what I have so far;

    import QtQuick 2.12
    import QtQuick.Window 2.12
    import QtQuick.Controls 2.12
    import Qt.labs.settings 1.1
    
    ApplicationWindow {
        id: mainWindow
        visible: true
        width: 960
        height: 540
        color: "darkgreen"
        title: qsTr("Pudding price list")
    
        Flickable {
            anchors.fill: parent
            contentHeight: grid.height
    
            Grid {
                id: grid
                rows: 2
                rowSpacing: 60            
                columns: 2
                columnSpacing: 60
                topPadding: 30
                anchors.horizontalCenter: parent.horizontalCenter
                Repeater {
                    model: ListModel {
                        id: sweetModel
                        ListElement {
                            name: "Fudge Cake"
                            type: "cake"
                            price: "3"
                            image: "images/fudge-cake.jpg"
                        }
                        ListElement {
                            name: "Gateaux"
                            type: "sponge cake"
                            price: "4"
                            image: "images/gateaux.jpg"
                        }
                        ListElement {
                            name: "Cookies"
                            type: "biscuits"
                            image: "images/choc-chip-cookies.jpg"
                            price: "4"
                        }
                        ListElement {
                            name: "Ice Cream"
                            type: "dairy"
                            price: "5"
                            image: "images/ice-cream.jpg"
                        }
                    }
                    Rectangle {
                        id: background
                        width: 300
                        height: 200
                        color: "maroon"
                        radius: 10
    
                        TextField {
                            id: cost
                            text: price
                            background: Rectangle {
                                anchors.fill: parent
                                color: "#00000000"
                            }
                            font.pixelSize: 36
                            color: "white"
                            onEditingFinished: sweetModel.get([index]).price = cost.text
                        }
                        TextField {
                            id: label
                            text: name
                            font.pixelSize: 36
                            background: Rectangle {
                                anchors.fill: parent
                                color: "#00000000"
                            }
                            color: "white"
                            anchors {
                                margins: 15
                                bottom: parent.bottom
                                horizontalCenter: parent.horizontalCenter
                            }
                            onEditingFinished: sweetModel.get([index]).name = label.text
                        }
                        TextField {
                            id: kind
                            text: type
                            anchors {
                                bottom: parent.bottom
                                horizontalCenter: parent.horizontalCenter
                            }
                            color: "white"
                            background: Rectangle {
                                anchors.fill: parent
                                color: "#00000000"
                            }
                            font {
                                italic: true
                                pixelSize: 14
                                letterSpacing: 2
                                capitalization: Font.AllUppercase
                            }
                            onEditingFinished: sweetModel.get([index]).type = kind.text
                        }
                        Settings {
                            id: settings
                            category: "Menu"
                        }
                        Component.onCompleted:  {
                            cost.text[index] = settings.value("costText", cost.text[index])
                            console.log("Price: " + " " + sweetModel.get([index]).price);
                            //console.log("Name: " + " " + sweetModel.get([index]).name);
                            //console.log("Type: " + " " + sweetModel.get([index]).type);
                        }                    
                        Component.onDestruction: {
                            settings.setValue("costText", cost.text[index])
                            console.log("Price: " + " " + sweetModel.get([index]).price);
                            //console.log("Name: " + " " + sweetModel.get([index]).name);
                            //console.log("Type: " + " " + sweetModel.get([index]).type);
                        }
                    }
                }
            }
        }
    }


  • Your indexing for editing seems to be working. I think the settings should be a global and will most likely require serialization.

    import QtQuick 2.12
    import QtQuick.Window 2.12
    import QtQuick.Controls 2.12
    import Qt.labs.settings 1.1
    
    ApplicationWindow {
        id: mainWindow
        visible: true
        width: 960
        height: 540
        color: "darkgreen"
        title: qsTr("Pudding price list")
    
        property bool save: true
    
        onClosing: {
            if(save){
                let tlist = []
                for(let idx=0; idx<listmodelsource.model.count; ++idx){
                    let obj = listmodelsource.model.get(idx)
                    let temp = {name:obj.name, type:obj.type, price:obj.price, image:obj.image}
                    tlist.push(temp)
                }
    
                let stringed = JSON.stringify(tlist)
                console.log("written", stringed)
                listmodelsourcedata = stringed
            }
        }
    
        property string listmodelsourcedata: ""
    
        Settings {
            id: settings
            category: "Menu"
    
            property alias listmodelsource: mainWindow.listmodelsourcedata
        }
    
        Component.onCompleted: {
            if(listmodelsourcedata){
                let tlist = JSON.parse(listmodelsourcedata)
                console.log("read", listmodelsourcedata)
                sweetModel.clear()
                for(let idx in tlist){
                    sweetModel.append(tlist[idx])
                }
            }
        }
    
        Flickable {
            anchors.fill: parent
            contentHeight: grid.height
    
            Grid {
                id: grid
                rows: 2
                rowSpacing: 60
                columns: 2
                columnSpacing: 60
                topPadding: 30
                anchors.horizontalCenter: parent.horizontalCenter
    
                Repeater {
                    id: listmodelsource
                    model: ListModel {
                        id: sweetModel
                        ListElement {
                            name: "Fudge Cake"
                            type: "cake"
                            price: "3"
                            image: "images/fudge-cake.jpg"
                        }
                        ListElement {
                            name: "Gateaux"
                            type: "sponge cake"
                            price: "4"
                            image: "images/gateaux.jpg"
                        }
                        ListElement {
                            name: "Cookies"
                            type: "biscuits"
                            image: "images/choc-chip-cookies.jpg"
                            price: "4"
                        }
                        ListElement {
                            name: "Ice Cream"
                            type: "dairy"
                            price: "5"
                            image: "images/ice-cream.jpg"
                        }
                    }
                    Rectangle {
                        id: background
                        width: 300
                        height: 200
                        color: "maroon"
                        radius: 10
    
                        TextField {
                            id: cost
                            text: price
                            background: Rectangle {
                                anchors.fill: parent
                                color: "#00000000"
                            }
                            font.pixelSize: 36
                            color: "white"
                            onEditingFinished: sweetModel.get([index]).price = cost.text
                        }
                        TextField {
                            id: label
                            text: name
                            font.pixelSize: 36
                            background: Rectangle {
                                anchors.fill: parent
                                color: "#00000000"
                            }
                            color: "white"
                            anchors {
                                margins: 15
                                bottom: parent.bottom
                                horizontalCenter: parent.horizontalCenter
                            }
                            onEditingFinished: sweetModel.get([index]).name = label.text
                        }
                        TextField {
                            id: kind
                            text: type
                            anchors {
                                bottom: parent.bottom
                                horizontalCenter: parent.horizontalCenter
                            }
                            color: "white"
                            background: Rectangle {
                                anchors.fill: parent
                                color: "#00000000"
                            }
                            font {
                                italic: true
                                pixelSize: 14
                                letterSpacing: 2
                                capitalization: Font.AllUppercase
                            }
                            onEditingFinished: sweetModel.get([index]).type = kind.text
                        }
    
                        /*
                        Component.onCompleted:  {
                            cost.text[index] = settings.value("costText", cost.text[index])
                            console.log("Price: " + " " + sweetModel.get([index]).price);
                            //console.log("Name: " + " " + sweetModel.get([index]).name);
                            //console.log("Type: " + " " + sweetModel.get([index]).type);
                        }
                        Component.onDestruction: {
                            settings.setValue("costText", cost.text[index])
                            console.log("Price: " + " " + sweetModel.get([index]).price);
                            //console.log("Name: " + " " + sweetModel.get([index]).name);
                            //console.log("Type: " + " " + sweetModel.get([index]).type);
                        }
                        */
                    }
                }
            }
        }
    }
    

    I found this SO approach. I tried serializing a QObject. It didn't like it at all.
    https://stackoverflow.com/questions/48730166/how-to-save-and-restore-the-content-of-a-listmodel



  • Wow!, thank you. There is little to no way i would have worked that lot out. I looked at so many examples, created many examples but my head was starting to hurt and I was getting very muddled.

    One more question if you don't mind; I've added a 'quit' button to the UI, but upon 'quitting' the app, any data that has been changed, say a price from 7 to 8, is not updated. . . . What do I need to add to my 'close' button to apply the change?

            /* other code */
            contentHeight: grid.height + -180
    
            Rectangle {
                id: close
                width: 20
                height: width
                color: "red"
                radius: width/2
                x: 10; y: x
            }
            Label {
                text: "X"
                color: "white"
                anchors.centerIn: close
            }
    
            MouseArea {
                anchors.fill:  close
                onClicked: {
                    Qt.quit()
                }
            }
    
            Grid {
                id: grid
                /* etc. . .  */
    

    Regards,



  • Since you are using TextFields you have to get the onEditingFinished to fire. In order to do that you have to press enter or have the TextField lose focus.

    https://doc.qt.io/qt-5/qml-qtquick-controls-textfield.html#editingFinished-signal

    So you could cause another Item to gain focus at the beginning of the onClosing method. I don't know if that is setActiveFocus or another call. Maybe just setting an Item's focus to true might do it.



  • I made a dummy object and set the focus on it at the start of the onClosing function:

    onClosing: {
            dummyfocus.focus = true
    
            if(save){
                let tlist = []
                for(let idx=0; idx<listmodelsource.model.count; ++idx){
                    let obj = listmodelsource.model.get(idx)
                    let temp = {name:obj.name, type:obj.type, price:obj.price, image:obj.image}
                    tlist.push(temp)
                }
    
                let stringed = JSON.stringify(tlist)
                console.log("written", stringed)
                listmodelsourcedata = stringed
            }
        }
    
        Item {
            id: dummyfocus
        }
    

    This takes focus from any currently being edited objects. Of course a button would probably do this if pressed as well. So it probably would have sorted itself out. But at least you can force it this way.



  • @fcarney - thanks again, this is taking shape now. There are a few odd behaviours which I will try to nail down, but largely, I am now able to edit price, name and type, close and open again with all my changes being present! yay!

    Regards,


Log in to reply