Hi,
I came across this phenomenon of duplicate keys when trying to persistently store a ListModel to a dconf key.
Semi-Minimal example which shows this below.
The “bug” happens here:
// some model has data like [ { "text": ¨value" }, { "text": "othervalue" }, ... ]
// what ends up in Dconf is:
// [ { "text": ¨value", "text": "value" }, { "text": "othervalue", "text": "othervalue" }... ]
property ListModel secondModel: ListModel{}
function addToSecondModel(o) {
secondModel.append(o); // duplication seems to EITHER happen here
}
// save model to persistent key
function saveSecondModel() {
var ds = [];
for (var i=0; i<secondModel.count;i++) {
const e = secondModel.get(i); // ... OR here
ds.push(e);
}
// this is a dconf key:
config.test = JSON.stringify(ds);
}
ConfigurationGroup { id: config
path: "/some/app"
property string test: '[]'
}
Full woking example below, start as qmlscene minitest.qml
:
import QtQuick 2.6
import Sailfish.Silica 1.0
import Nemo.Configuration 1.0
ApplicationWindow {
id: app
property ListModel secondModel: ListModel{} // things to be saved are added to this
// add something to the model
function addToSecondModel(o) {
console.debug("secondModel:", JSON.stringify(secondModel,null,2));
console.debug("adding:", o, JSON.stringify(o,null,2));
secondModel.append(o);
console.debug("secondModel:", JSON.stringify(secondModel,null,2));
for (var i=0; i<secondModel.count;i++) {
const e = secondModel.get(i);
console.debug("elements:", JSON.stringify(e,null,2));
}
}
// save model to persistent key
function saveSecondModel() {
var ds = [];
console.debug("secondModel:", JSON.stringify(secondModel,null,2));
for (var i=0; i<secondModel.count;i++) {
const e = secondModel.get(i);
console.debug("pushing:", JSON.stringify(e,null,2));
ds.push(e);
}
console.debug("saving:", JSON.stringify(ds,null,2));
config.test = JSON.stringify(ds);
console.debug("saved:", config.test);
}
Component.onCompleted: {
var testData = [
[ "first-string-is-this", "23107-4571-17794", "true", "active", 0, "", ],
[ "second-string-is-this", "11479-28990-12380", "true", "passive", 1, "", ],
[ "third-string-is-this", "6768-21821-19354", "false", "active", 1, "", ]
];
// fill data model:
testModel.fill(testData);
}
// application settings:
ConfigurationGroup {
id: settings
path: "/org/nephros/testing/" + "saveTest"
}
ConfigurationGroup {
id: config
scope: settings
path: "app"
property string test: '[]'
}
ListModel { id: testModel
function fill(data) {
console.debug("fill model got: " + data.length + " entries.");
const o = {};
data.forEach(function(e) {
o = {
"text1": e[0],
"text2": e[1],
"text3": e[2],
}
append(o);
});
console.debug("model has now: " + count + " entries.");
}
}
initialPage: Component { Page { id: page
SilicaFlickable { id: flick
anchors.fill: parent
contentHeight: col.height
Column { id: col
width: parent.width - Theme.horizontalPageMargin
anchors.horizontalCenter: parent.horizontalCenter
PageHeader { id: head ; title: qsTr(Qt.application.name); }
Repeater {
model: testModel
delegate: Component {
ListItem {
contentHeight: content.height
hidden: false
menu: ContextMenu {
MenuItem { text: qsTr("save this");
onClicked: {
hidden=true ;
console.debug("click-adding:", model.text1, JSON.stringify(model.text1,null,2));
app.addToSecondModel({ "text1": model.text1 });
app.saveSecondModel()
}
}
}
Column { id: content
width: parent.width
spacing: Theme.paddingSmall
Label { text: text1 ; elide: Text.ElideLeft; font.pixelSize: Theme.fontSizeSmall}
Label { text: text2 ; elide: Text.ElideRight; width: parent.width}
Label { text: text3 ; font.pixelSize: Theme.fontSizeSmall}
}
}
}
}
}
}
} }
}
// vim: ft=javascript expandtab ts=4 sw=4 st=4
which results in this output:
11:10:08.484 qml: file:///home/nemo/devel/git/systemd-watcher/test/minitest.qml:97 onClicked click-adding: second-string-is-this "second-string-is-this"
11:10:08.485 qml: file:///home/nemo/devel/git/systemd-watcher/test/minitest.qml:12 addToSecondModel secondModel: {
"objectName": "",
"count": 0,
"dynamicRoles": false
}
11:10:08.485 qml: file:///home/nemo/devel/git/systemd-watcher/test/minitest.qml:13 addToSecondModel adding: [object Object] {
"text1": "second-string-is-this"
}
11:10:08.487 qml: file:///home/nemo/devel/git/systemd-watcher/test/minitest.qml:15 addToSecondModel secondModel: {
"objectName": "",
"count": 1,
"dynamicRoles": false
}
11:10:08.487 qml: file:///home/nemo/devel/git/systemd-watcher/test/minitest.qml:18 addToSecondModel elements: {
"text1": "second-string-is-this",
"text1": "second-string-is-this"
}
11:10:08.488 qml: file:///home/nemo/devel/git/systemd-watcher/test/minitest.qml:24 saveSecondModel secondModel: {
"objectName": "",
"count": 1,
"dynamicRoles": false
}
11:10:08.488 qml: file:///home/nemo/devel/git/systemd-watcher/test/minitest.qml:27 saveSecondModel pushing: {
"text1": "second-string-is-this",
"text1": "second-string-is-this"
}
11:10:08.489 qml: file:///home/nemo/devel/git/systemd-watcher/test/minitest.qml:30 saveSecondModel saving: [
{
"text1": "second-string-is-this",
"text1": "second-string-is-this"
}
]
11:10:08.490 qml: file:///home/nemo/devel/git/systemd-watcher/test/minitest.qml:32 saveSecondModel saved: [{"text1":"second-string-is-this","text1":"second-string-is-this"}]