Deleting items from primary ListModel
-
I am working on a ListModel for rss feeds and need to be able to delete ListElements and eventually add items too. I am struggling to be able to properly implement this. When I run my RssEdit.ui.qml I am able to see the list of elements it gets from RssList.qml and have a remove button next to each item, however when I click the delete button it only seems to delete that element/item from the ListView in RssEdit.ui.qml and not the parent ListModel in RssList.qml. I have tried to implement many different ideas and I am stuck. Hoping someone can help with this one. How do I get my button to not only delete the current item from the current view, but also from the main ListModel in RssList.qml? Any assistance would be very much appreciated. I have been searching for similar stuff for days hoping to find an answer, and have not found anything I was able to get working.
RssList.qml
import QtQuick 2.0 ListModel { ListElement { name: "CNN"; feed: "news.yahoo.com/rss/world" } ListElement { name: "New York Post"; feed: "nypost.com/feed" } }
RssEdit.ui.qml
import QtQuick 2.12 import QtQuick.Controls 2.5 Page { title: qsTr("Feeds") RssList { id: rssFeeds } ListView { id: listView anchors.fill: parent model: rssFeeds delegate: Item { x: 5 width: 80 height: 40 Row { id: row1 spacing: 10 Rectangle { width: 40 height: 40 color: colorCode } Text { text: name anchors.verticalCenter: parent.verticalCenter font.bold: true } Button { id: deleterss visible: true text: "Delete From List" display: AbstractButton.TextOnly Connections { target: deleterss onClicked: { rssFeeds.remove(index) } } } } } } }
-
remove is correct, but I'm not sure that is how you'd get that ListModel... you could use an alias to expose it... for now, what about if you do, instead of:
RssList { id: rssFeeds }
use
ListModel { id: rssFeeds; ListElement { name: "CNN"; feed: "news.yahoo.com/rss/world" } ListElement { name: "New York Post"; feed: "nypost.com/feed" } }
Then does your remove work.
-
@The3DmaN I am not sure what you mean when you say this: "it only seems to delete that element/item from the ListView in RssEdit.ui.qml and not the parent ListModel in RssList.qml". What is it that you are expecting to see? What are you doing in the rest of your code that makes you think it is not working properly?
Based on what I can see and what you have described, it seems to be working correctly and I am wondering if you might have a conceptual misunderstanding. When you do this in your RssEdit.ui.qml:
RssList { id: rssFeeds }
you are creating a new instance of the model. Generally speaking, unless you do extra work, it is only really going to be visible in the component where you create it. If you want to share the model with some other view, for example, you will have to do something like instantiating the model higher up and assigning it to properties of the views that need to access it.
-
@6thC I tried it the way you mention, and as @Bob64 stated maybe it's that I am not conceptually understanding how the ListModel works. Even when I try it this way while I am in the app on the RssEdit.ui.qml page, when I click remove, it removes it from the visual list in the running page only. As soon as I exit the page and come back or restart the app the list is right back to what it was before the item deleted. Its like it never happened.
I guess my thought was that if I were to delete say the NY Post entry with the delete button, on the back end the list would go from
This:
ListModel { id: rssFeeds; ListElement { name: "CNN"; feed: "news.yahoo.com/rss/world" } ListElement { name: "New York Post"; feed: "nypost.com/feed" } }
To This:
ListModel { id: rssFeeds; ListElement { name: "CNN"; feed: "news.yahoo.com/rss/world" } }
so that when I went back to the page or restarted the app, the list would retain the delete, but that does not seem to be happening.
-
@The3DmaN here is a working code snipped showing adding and removing news feed. (btw you might be interested in XmlListModel instead of ListModel )
import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 Window { width: 640; height: 480; visible: true; title: qsTr("Hello World") ListModel { id:theModel ListElement { name: "CNN"; feed: "news.yahoo.com/rss/world" } ListElement { name: "New York Post"; feed: "nypost.com/feed" } } ListView{ id:theView anchors.fill:parent anchors.bottomMargin: addPanel.height model:theModel delegate:RowLayout{ width : theView.width Label{ text:name} Label{ text:feed; Layout.fillWidth: true} Button{ text:"-"; onClicked: theModel.remove(index)} } } RowLayout{ id:addPanel anchors.top: theView.bottom width : theView.width TextEdit{ id: newSource; text:"source"} TextEdit{ id: newFeed; text:"feed"; Layout.fillWidth: true} Button{ text:"+"; onClicked: theModel.append({ name: newSource.text, feed: newFeed.text })} } }
-
@The3DmaN OK, I think I understand what you are observing a bit better now. The issue is that if you leave the page after deleting an item, the deleted item has reappeared when you revisit the page. Correct?
If that is the case, it sounds to me as if your page must be being recreated each time you open it. If that were the case, because the model is scoped to the page, it also would be recreated, so it would appear to be back in its initial state.
How are you instantiating your
Page
objects? Are you usingcreateObject
anywhere or anything like that? -
@Charby Isn't XmlListModel for read only models? I tested out your code above, and it has the same issue as mine. When clicking remove in the running app, it removes it while running the app, but as soon as you reopen the app the item removed is back. Where I am struggling is how to update the actual ListModel with the change so that when you reopen the app or page the item removed is still gone.
@Bob64 You are correct in your assumptions. I am not using createObject anywhere that I know of. I uploaded my full original project to Gitlab (this was my code before making any suggested changes from here) if that helps with the analysis, but it is pretty basic so far. Just a generic Qt Quick StackView template as its base with only 2 pages and 1 ListModel qml. My main goal is to be able to edit the list of rss feeds so that I can then use that to pull feeds from those sites and display them on the currently blank DisplayFeed.ui.qml page in the future. Pulling the feeds and displaying them is not an issue, I have done that before, I am just stuck figuring out how to implement the user editable list of feeds and have it stick on re-opening of the app/page.
Gitlab: https://gitlab.com/The3DmaN/rss-feed
The only change I have made to the gitlab original code is I moved the ListModel from its own RssList.qml file to being directly in the RssEdit.ui.qml page so that I was not creating a full new instance of the model with " RssList { id: rssFeeds }" as I was informed of above, however, I am wondering if I should keep it in its own qml since I will eventually need to pull this list model in other pages for displaying the feeds. My apologizes for my inexperience with all of this. I appreciate all the help and guidance.
-
as @Bob64 pointed out, what you are experiencing is a normal behavior of the stackview recreating when requested the RssEdit.ui.qml page. As the list model is part of the page, it will be recreated with its default each time the page is loaded.
Here are a few options you could follow :
- the easiest (Quick & dirty) : move the ListModel at the root of main QML, give it a unique id name, and use this id in from RssEdit.ui.qml.
- same but cleaner : same as before but instead of using the id directly from RssEdit.ui.qml, create a property in RssEdit.ui.qml that will be list model to use. In main.qml this property will be affected with your actual ListModel
- put your list model in a singleton so it could be used wherever you want
-
From your edited last post I now understand you also want the list model content to be persisting after the lifetime of your application.
In that case, you will in addition need to take care of this persisting state (saving when the app quit, loading when the app start) somewhere (a file, a database...) -
@Charby So it sounds like I will need to create an actual XML or database file and then have the app edit that file directly. Thank you, I will give this a shot. I think my misunderstanding was me assuming changes to a ListModel would be persistent after the lifetime of the app. I will mark this as solved since my main question was answered.
-
@The3DmaN The following code shows how to use a Settings to keep the feeds sources after apps lifetime
import QtQuick 2.12 import Qt.labs.settings 1.0 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import QtQuick.XmlListModel 2.12 ApplicationWindow { width: 640; height: 480; visible: true; title: qsTr("News reader") // The list of feeds sources (static content is only used if persisting is not available ListModel { id:rssFeeds ListElement { name: "CNN"; feed: "http://news.yahoo.com/rss/world"; colorCode:"blue"} ListElement { name: "New York Post"; feed: "http://nypost.com/news/feed"; colorCode:"green" } } // Load the feeds sources at startup Component.onCompleted: { var tmpArray =[]; tmpArray = JSON.parse(settings.rssFeedsSavedAsString) if (tmpArray.length !== 0) rssFeeds.clear(); for (var i = 0; i < tmpArray.length; ++i) rssFeeds.append(tmpArray[i]) } // Have the feeds source persisting after app lifetime onClosing: { var tmpArray =[]; for (var i = 0; i < rssFeeds.count; ++i) tmpArray.push(rssFeeds.get(i)); settings.rssFeedsSavedAsString = JSON.stringify(tmpArray) } // The serialized string used for persisting the feeds sources Settings{ id:settings property string rssFeedsSavedAsString : "" } // The selected feed XmlListModel{ id:feedContent source: rssFeeds.get(feedsSelector.currentIndex).feed query: "/rss/channel/item" XmlRole { name:"title"; query: "title/string()"} } //The 3 UI elements : selection of feeds, selected feed content and control panel ColumnLayout{ anchors.fill:parent; anchors.margins: 5 ListView{ id:feedsSelector currentIndex: 0 Layout.fillWidth: true Layout.fillHeight: true model:rssFeeds highlight:Rectangle{ color: "lightblue"} delegate:RowLayout{ width : feedsSelector.width Label{ text:name; color:colorCode; MouseArea{ anchors.fill:parent; onClicked: feedsSelector.currentIndex = index}} Label{ text:feed; Layout.fillWidth: true; MouseArea{ anchors.fill:parent; onClicked: feedsSelector.currentIndex = index}} Button{ text:"-"; onClicked: rssFeeds.remove(index)} } } ListView{ Layout.fillWidth: true Layout.fillHeight: true model:feedContent delegate:RowLayout{ width : feedsSelector.width Label{ text:title; color:rssFeeds.get(feedsSelector.currentIndex).colorCode} } } RowLayout{ id:addPanel Layout.fillWidth: true Layout.fillHeight: true TextEdit{ id: newSource; text:"BBC"} TextEdit{ id: newFeed; text:"http://feeds.bbci.co.uk/news/world/rss.xml"; Layout.fillWidth: true} Button{ text:"+"; onClicked: rssFeeds.append({ name: newSource.text, feed: newFeed.text, colorCode:"orange" })} } } }