Unsolved ListView not Updated properly
-
Hello,
this is my model.cpp
/****************************************************************************** * * Copyright (C) 2018-2019 Marton Borzak <hello@martonborzak.com> * * This file is part of the YIO-Remote software project. * * YIO-Remote software is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * YIO-Remote software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with YIO-Remote software. If not, see <https://www.gnu.org/licenses/>. * * SPDX-License-Identifier: GPL-3.0-or-later *****************************************************************************/ #include "albummodel_mediaplayer.h" ListModel::ListModel(QObject *parent) : QAbstractListModel(parent) { } int ListModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return m_data.size(); } QVariant ListModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= m_data.count()) { return QVariant(); } const ModelItem &item = m_data[index.row()]; switch (role) { case KeyRole: return item.item_key(); case TitleRole: return item.item_title(); case SubTitleRole: return item.item_subtitle(); case TypeRole: return item.item_type(); case ImageUrlRole: return item.item_imageUrl(); case CommandsRole: return item.item_commands(); } return QVariant(); } QHash<int, QByteArray> ListModel::roleNames() const { QHash<int, QByteArray> roles; roles[KeyRole] = "item_key"; roles[TitleRole] = "item_title"; roles[SubTitleRole] = "item_subtitle"; roles[TypeRole] = "item_type"; roles[ImageUrlRole] = "item_image"; roles[CommandsRole] = "item_commands"; return roles; } void ListModel::append(const ModelItem &modelItem) { const int i = m_data.size(); beginInsertRows(QModelIndex(), i, i); m_data.append(modelItem); endInsertRows(); } void BrowseModel::addItem(const QString &key, const QString &title, const QString &subtitle, const QString &type, const QString &imageUrl, const QVariant &commands) { ListModel *model = static_cast<ListModel *>(m_model); const ModelItem item = ModelItem(key, title, subtitle, type, imageUrl, commands); model->append(item); emit modelChanged(); } void BrowseModel::clearItems() { if(m_model) { delete m_model; } m_model = new ListModel(); emit modelChanged(); } void BrowseModel::clearProperties() { m_id.clear(); m_title.clear(); m_subtitle.clear(); m_type.clear(); m_imageUrl.clear(); m_commands.clear(); }
this is my qml file
/****************************************************************************** * * Copyright (C) 2018-2019 Marton Borzak <hello@martonborzak.com> * * This file is part of the YIO-Remote software project. * * YIO-Remote software is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * YIO-Remote software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with YIO-Remote software. If not, see <https://www.gnu.org/licenses/>. * * SPDX-License-Identifier: GPL-3.0-or-later *****************************************************************************/ import QtQuick 2.12 import QtQuick.Controls 2.12 import Style 1.0 import Haptic 1.0 import "qrc:/basic_ui" as BasicUI Rectangle { id: main width: parent.width; height: parent.height color: Style.color.dark radius: Style.cornerRadius //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // VARIABLES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// property var playListBrowseModel property bool isCurrentItem: parent._currentItem property bool start: true property bool filtertagplaylist: false property string filterString: "All" onIsCurrentItemChanged: { if (isCurrentItem && start) { console.debug("LOAD USER PLAYLISTS"); start = false; obj.browseModelChanged.connect(onFirstLoadComplete); obj.getPlaylist("user"); } if (!isCurrentItem) { playListListView.contentY = 0-120; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function onFirstLoadComplete(model) { obj.browseModelChanged.disconnect(onFirstLoadComplete); main.playListBrowseModel = model.model; } function load(album, type) { swipeView.currentIndex++; if (type === "playlist") { obj.browseModelChanged.connect(onBrowseModelChanged); obj.getPlaylist(album); } } function onBrowseModelChanged(model) { if (playlistLoader) { if (playlistLoader.source != "qrc:/components/media_player/ui/AlbumView.qml") playlistLoader.setSource("qrc:/components/media_player/ui/AlbumView.qml", { "albumModel": model }) else if (playlistLoader.item) { playlistLoader.item.albumModel = model; playlistLoader.item.itemFlickable.contentY = 0; } } obj.browseModelChanged.disconnect(onBrowseModelChanged); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UI ELEMENTS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// property alias swipeView: swipeView SwipeView { id: swipeView width: parent.width; height: parent.height currentIndex: 0 interactive: false clip: true Item { property alias playListListView: playListListView Component { id: componentTagFilterPlaylist BasicUI.Tag { id: tagFilterPlaylist tag: "playlist" selected: false MouseArea { anchors.fill: parent onClicked: { if (selected) { selected = false; filterString = "All" tagFilterAll.selected = true; playListListView.update(); } else { filterString = "Playlist" tagFilterAll.selected = false; playListListView.update(); selected = true; } } } } } Row { id: tagRepeaterRow BasicUI.Tag { id: tagFilterAll tag: "All" selected:true MouseArea { anchors.fill: parent onClicked: { if (!tagFilterAll.selected) { tagFilterAll.selected = true; filterString = "All" } } } } Repeater { id: tagRepeater model: main.playListBrowseModel Loader { id: filterLoader sourceComponent: if (item_type === "playlist"){ if (!filtertagplaylist) { filtertagplaylist = true; return componentTagFilterPlaylist } } } } } ListView { id: playListListView width: parent.width; height: parent.height-100 spacing: 20 anchors { top: tagRepeaterRow.bottom; horizontalCenter: parent.horizontalCenter } maximumFlickVelocity: 6000 flickDeceleration: 1000 boundsBehavior: Flickable.DragAndOvershootBounds flickableDirection: Flickable.VerticalFlick clip: true cacheBuffer: 3000 delegate: playListThumbnail model: main.playListBrowseModel ScrollBar.vertical: ScrollBar { opacity: 0.5 } header: Component { Item { width: parent.width; height: 120 Text { id: title color: Style.color.text text: qsTr("My playlists") + translateHandler.emptyString font { family: "Open Sans Bold"; weight: Font.Bold; pixelSize: 40 } lineHeight: 1 anchors { left: parent.left; leftMargin: 30; top: parent.top; topMargin: 30 } } } } populate: Transition { id: popTransition SequentialAnimation { PropertyAction { property: "opacity"; value: 0 } PauseAnimation { duration: popTransition.ViewTransition.index*100 } NumberAnimation { properties: "opacity"; from: 0; to: 1; duration: 300; easing.type: Easing.InExpo } } } } Component { id: playListThumbnail Item { id: trackThumbnailItem width: parent.width-60; height: 80 anchors.horizontalCenter: parent.horizontalCenter visible: if (filterString === "Playlist") { return true; } else if (filterString === "All") { return true; } else { return false; } Rectangle { id: albumImage width: 80; height: 80 Image { source: item_image anchors.fill: parent fillMode: Image.PreserveAspectCrop asynchronous: true } } Text { id: albumTitleText text: item_title elide: Text.ElideRight width: itemFlickable.width-60-albumImage.width-20-80 wrapMode: Text.NoWrap color: Style.color.text anchors { left: albumImage.right; leftMargin: 20; top: albumImage.top; topMargin: item_subtitle == "" ? 26 : 12 } font { family: "Open Sans Regular"; pixelSize: 25 } lineHeight: 1 } Text { id: albumSubTitleText text: item_subtitle elide: Text.ElideRight visible: item_subtitle == "" ? false : true width: albumTitleText.width wrapMode: Text.NoWrap color: Style.color.text opacity: 0.6 anchors { left: albumTitleText.left; top: albumTitleText.bottom; topMargin: 5 } font { family: "Open Sans Regular"; pixelSize: 20 } lineHeight: 1 } MouseArea { anchors.fill: parent onClicked: { Haptic.playEffect(Haptic.Click); load(item_key, item_type); } } BasicUI.ContextMenuIcon { anchors { right: parent.right; verticalCenter: parent.verticalCenter } mouseArea.onClicked: { Haptic.playEffect(Haptic.Click); contextMenuLoader.setSource("qrc:/basic_ui/ContextMenu.qml", { "width": itemFlickable.width, "id": item_key, "type": item_type, "list": item_commands }) } } } } } Item { Loader { id: playlistLoader asynchronous: true anchors.fill: parent } Text { id: backButton color: Style.color.text text: Style.icon.left_arrow renderType: Text.NativeRendering width: 70; height: 70 verticalAlignment: Text.AlignVCenter; horizontalAlignment: Text.AlignHCenter font {family: "icons"; pixelSize: 80 } anchors { left: parent.left; leftMargin: 10; top: parent.top; topMargin: 20 } MouseArea { id: backButtonMouseArea width: parent.width + 20; height: parent.height + 20 anchors.centerIn: parent onClicked: { Haptic.playEffect(Haptic.Click); swipeView.currentIndex = 0; } } } } } }
and the album.qml file
/****************************************************************************** * * Copyright (C) 2018-2019 Marton Borzak <hello@martonborzak.com> * * This file is part of the YIO-Remote software project. * * YIO-Remote software is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * YIO-Remote software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with YIO-Remote software. If not, see <https://www.gnu.org/licenses/>. * * SPDX-License-Identifier: GPL-3.0-or-later *****************************************************************************/ import QtQuick 2.12 import QtQuick.Controls 2.12 import Style 1.0 import Haptic 1.0 import MediaPlayerUtils 1.0 import "qrc:/basic_ui" as BasicUI Rectangle { id: main width: parent.width; height: parent.height color: mediaplayerUtils.pixelColor radius: Style.cornerRadius Behavior on color { ColorAnimation { duration: 300 } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // VARIABLES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// property var albumModel //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILITIES //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// MediaPlayerUtils { id: mediaplayerUtils imageURL: albumModel.imageUrl } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UI ELEMENTS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// property alias itemFlickable: itemFlickable Flickable { id: itemFlickable width: parent.width; height: parent.height-100 maximumFlickVelocity: 6000 flickDeceleration: 1000 contentHeight: 150 + image.height + title.height + artist.height + trackListView.height boundsBehavior: Flickable.DragAndOvershootBounds flickableDirection: Flickable.VerticalFlick clip: true Behavior on contentY { PropertyAnimation { duration: 300 easing.type: Easing.OutExpo } } ScrollBar.vertical: ScrollBar { opacity: 0.5 } BasicUI.CustomImageLoader { id: image width: 280; height: 280 anchors { horizontalCenter: parent.horizontalCenter; top: parent.top; topMargin: 100 } url: albumModel.imageUrl === "" ? "qrc:/images/mini-music-player/no_image.png" : albumModel.imageUrl } Text { color: Style.color.text text: Style.icon.play renderType: Text.NativeRendering width: 70; height: 70 verticalAlignment: Text.AlignVCenter; horizontalAlignment: Text.AlignHCenter font {family: "icons"; pixelSize: 80 } anchors.centerIn: image MouseArea { width: parent.width + 20; height: parent.height + 20 anchors.centerIn: parent onClicked: { Haptic.playEffect(Haptic.Click); obj.playMedia(albumModel.id, albumModel.type); } } } Text { id: title color: Style.color.text text: albumModel.title horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter elide: Text.ElideRight wrapMode: Text.NoWrap width: parent.width-80 font { family: "Open Sans Bold"; weight: Font.Bold; pixelSize: 30 } lineHeight: 1 anchors { horizontalCenter: parent.horizontalCenter; top: image.bottom; topMargin: 20 } } Text { id: artist color: Style.color.text text: albumModel.subtitle horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter elide: Text.ElideRight wrapMode: Text.NoWrap width: parent.width-80 font: Style.font.button anchors { top: title.bottom; horizontalCenter: parent.horizontalCenter } } ListView { id: trackListView width: parent.width-60; height: childrenRect.height spacing: 20 interactive: false anchors { top: artist.bottom; topMargin: 40; horizontalCenter: parent.horizontalCenter } model: albumModel.model delegate: trackThumbnail } Component { id: trackThumbnail Item { width: parent.width; height: 80 Text { id: trackNumber text: index+1 color: Style.color.text anchors { left: parent.left; verticalCenter: parent.verticalCenter } font { family: "Open Sans Regular"; pixelSize: 25 } lineHeight: 1 } Text { id: albumTitleText text: item_title elide: Text.ElideRight width: parent.width-100 wrapMode: Text.NoWrap color: Style.color.text anchors { left: parent.left; leftMargin: 45; top: parent.top } font { family: "Open Sans Regular"; pixelSize: 25 } lineHeight: 1 } Text { id: albumSubTitleText text: item_subtitle elide: Text.ElideRight width: albumTitleText.width wrapMode: Text.NoWrap color: Style.color.text opacity: 0.6 anchors { left: albumTitleText.left; top: albumTitleText.bottom; topMargin: 5 } font { family: "Open Sans Regular"; pixelSize: 20 } lineHeight: 1 } MouseArea { anchors.fill: parent onClicked: { Haptic.playEffect(Haptic.Click); obj.playMedia(item_key, item_type); } } BasicUI.ContextMenuIcon { colorBg: mediaplayerUtils.pixelColor anchors { right: parent.right; verticalCenter: parent.verticalCenter } mouseArea.onClicked: { Haptic.playEffect(Haptic.Click); contextMenuLoader.setSource("qrc:/basic_ui/ContextMenu.qml", { "width": main.width, "id": item_key, "type": item_type, "list": item_commands }) } } } } } }
When i call
m_userPlaylistBrowseModel->clearItems(); m_userPlaylistBrowseModel->clearProperties(); m_userPlaylistBrowseModel->addItem(playlists[i].toMap().value("id").toString(), playlists[i].toMap().value("name").toString(), "", type, image, commands);
The model is not updated.
Any help appriciated :)
-
You have to call the functions that trigger signals ListView will look for. For a clear of the data you have to do the beginresetmodel and endresetmodel. For the additem you have to use the begin/endinsertrows functions.
https://doc.qt.io/qt-5/qabstractitemmodel.html#protected-functions
Look for examples of how to build a model that shows how to do this. I think those are referenced in the model/view docs.
-
I changed is
/****************************************************************************** * * Copyright (C) 2018-2019 Marton Borzak <hello@martonborzak.com> * * This file is part of the YIO-Remote software project. * * YIO-Remote software is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * YIO-Remote software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with YIO-Remote software. If not, see <https://www.gnu.org/licenses/>. * * SPDX-License-Identifier: GPL-3.0-or-later *****************************************************************************/ #include "albummodel_mediaplayer.h" ListModel::ListModel(QObject *parent) : QAbstractListModel(parent) { } int ListModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return m_data.size(); } QVariant ListModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= m_data.count()) { return QVariant(); } const ModelItem &item = m_data[index.row()]; switch (role) { case KeyRole: return item.item_key(); case TitleRole: return item.item_title(); case SubTitleRole: return item.item_subtitle(); case TypeRole: return item.item_type(); case ImageUrlRole: return item.item_imageUrl(); case CommandsRole: return item.item_commands(); } return QVariant(); } QHash<int, QByteArray> ListModel::roleNames() const { QHash<int, QByteArray> roles; roles[KeyRole] = "item_key"; roles[TitleRole] = "item_title"; roles[SubTitleRole] = "item_subtitle"; roles[TypeRole] = "item_type"; roles[ImageUrlRole] = "item_image"; roles[CommandsRole] = "item_commands"; return roles; } void ListModel::append(const ModelItem &modelItem) { const int i = m_data.size(); beginInsertRows(QModelIndex(), i, i); m_data.append(modelItem); endInsertRows(); } void ListModel::clear() { beginResetModel(); m_data.clear(); endResetModel(); } void BrowseModel::addItem(const QString &key, const QString &title, const QString &subtitle, const QString &type, const QString &imageUrl, const QVariant &commands) { ListModel *model = static_cast<ListModel *>(m_model); const ModelItem item = ModelItem(key, title, subtitle, type, imageUrl, commands); model->append(item); emit modelChanged(); } void BrowseModel::clearItems() { if(m_model) { ListModel *model = static_cast<ListModel *>(m_model); model->clear(); delete m_model; } m_model = new ListModel(); emit modelChanged(); } void BrowseModel::clearProperties() { m_id.clear(); m_title.clear(); m_subtitle.clear(); m_type.clear(); m_imageUrl.clear(); m_commands.clear(); }
But still not working
-
What is BrowseModel ? Why do you have two models at all?
-
@Christian-Ehrlicher how to change it to one model?