[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.
-