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

How to group items?



  • Any ideas how to do something like in the screenshot below in qml?
    I have a list of songs and want to group them by album (or something else), with the album cover on the left and the songs on the right.

    The list is a QMap<int, Song*>

    #ifndef SONG_H
    #define SONG_H
    
    #include <QObject>
    
    class Song : public QObject
    {
        Q_OBJECT
    public:
        explicit Song(QObject *parent = nullptr);
    
        QString artist() const;
        void setArtist(const QString &artist);
    
        QString title() const;
        void setTitle(const QString &title);
    
        QString album() const;
        void setAlbum(const QString &album);
    
        QString disc() const;
        void setDisc(const QString &disc);
    
        QString year() const;
        void setYear(const QString &year);
    
        QString trackNumber() const;
        void setTrackNumber(const QString &trackNumber);
    
        QString length() const;
        void setLength(const QString &length);
    
    private:
        QString m_artist;
        QString m_title;
        QString m_album;
        QString m_disc;
        QString m_year;
        QString m_trackNumber;
        QString m_length;
    };
    
    #endif // SONG_H
    
    

    screenshot



  • @fbg13 Hello, have you checked out QSortFilterProxyModel? With it you can sort and filter any model:
    https://doc.qt.io/qt-5/qsortfilterproxymodel.html
    You will have to add the songs to a model and access them from it:
    https://doc.qt.io/qt-5/qtquick-modelviewsdata-modelview.html



  • @rrlopez What I need help with is how to display the groups in qml.

    I managed to do it by changing my model to hold the groups only and each group will have the songs.
    And in qml I have a repeater for the groups and for each group a repeater for the songs.

    Here's a repo https://gitlab.com/g-fb/music-player-layout-example with what I managed so far.

    Don't know how good this approach is and I can't think of another way.



  • @fbg13 I would use a QAbstractProxyModel to act as a model of your table view and change the way you define stuff in your QML.
    Basically add a row on the model for each song you want to display (artist, songname, artist, etc) and then access these roles on QML as model.artist, model.songname, etc.
    As you'll be using a QAbstractProxyModel you will have access to its sorting and filtering methods, so you will be able to filter out songs on artist. You basically will have N items where N is the number of artists, and each item from N will have a different filter to show only songs related to the corresponding artist.



  • @rrlopez said in How to group items?:

    @fbg13 I would use a QAbstractProxyModel to act as a model of your table view and change the way you define stuff in your QML.
    Basically add a row on the model for each song you want to display (artist, songname, artist, etc) and then access these roles on QML as model.artist, model.songname, etc.

    You mean the data for the model should be:
    data {song1, song2, .... } instead of data {album1 {song1, song2 ...}, album2 {song1, song2 ...}, album3 {song1, song2 ...}}

    As you'll be using a QAbstractProxyModel you will have access to its sorting and filtering methods, so you will be able to filter out songs on artist. You basically will have N items where N is the number of artists, and each item from N will have a different filter to show only songs related to the corresponding artist.

    But I don't want to filter the results. I want to show all songs, but they should be grouped and all groups should be visible at the same time.
    Like in the screenshot, the red rectangles are the groups (image on left and songs on the right).

    I guess my problem is how to add the image, since it should be added only when the value on which the songs are grouped changes, and have the layout I want, image on left and songs on the right.



  • @fbg13 said in How to group items?:

    You mean the data for the model should be:
    data {song1, song2, .... } instead of data {album1 {song1, song2 ...}, album2 {song1, song2 ...}, album3 {song1, song2 ...}}

    No, data should be {{song1, album1, etc},{song2,album2,etc}}. So that each row of the model contains everything that fully describes a song.

    I want to show all songs, but they should be grouped and all groups should be visible at the same time.

    QAbstractProxyModel also has ordering capabilities, so you should be able to sort them on any role of your model, in this case you want to use the album role.

    But I don't want to filter the results.

    I was talking about filtering because a way to show the information as the screenshot you sent is to have some sort of item for each album, so you would filter on album and show only the songs from that specific album in each item, but having a single item and sorting it out also works.



  • @rrlopez said in How to group items?:

    I was talking about filtering because a way to show the information as the screenshot you sent is to have some sort of item for each album, so you would filter on album and show only the songs from that specific album in each item, but having a single item and sorting it out also works.

    How would that look in qml though?
    Do I give my item's (TableView, Repeater etc) model the filtered-by-album model, and in the delegate I get the songs for the respective album?



  • @fbg13 said in How to group items?:

    Do I give my item's (TableView, Repeater etc) model the filtered-by-album model, and in the delegate I get the songs for the respective album?

    Yes, your delegate will just get all songs the model has since results will be filtered on the model, so QML will not view the filtered out items.



  • @rrlopez said in How to group items?:

    Yes, your delegate will just get all songs the model has since results will be filtered on the model, so QML will not view the filtered out items.

    But if I give the filtered model to a TableView I will get only songs from 1 album in the TableView.
    How do I get the other songs?

    Or should this TableView be a delegate for another item?

        Repeater {
            // model with each album
            model: albumsModel
            delegate: TableView {
                // model with songs of the current album in the repeater
                model: songsModel
                delegate: Song {}
            }
        }
    


  • @fbg13 said in How to group items?:

    Or should this TableView be a delegate for another item?

    Yeah, that's what I meant.



  • @rrlopez I tried what you suggested, or what I understood at least, but it's not working.

    At the core what I have done is:

    Repeater {
        id: group
        model: ["album1", "album2"]
        delegate: Repeater {
            model: songsProxyModel
            Component.onCompleted: songsProxyModel.setFilterFixedString(modelData)
            delegate: Label {
                text: model.artist + " - " + model.title
            }
        }
    }
    

    But all groups have the same songs, from the last filtering.
    Updated the repo too.



  • Hi, your model should be something like:
    {{song1, album1, etc},{song2,album2,etc}}
    So each entry is a song which has an album related.



  • @rrlopez said in How to group items?:

    Hi, your model should be something like:
    {{song1, album1, etc},{song2,album2,etc}}
    So each entry is a song which has an album related.

    If I understand you correctly I should have a single model songsModel = {{song1, album1, etc},{song2,album2,etc}} ,
    whose model is songsModel filtered to only hold the albums.

    The delegate of that repeater should be another repeater (or another item with a model),
    whose model is songsModel filtered to hold the songs of the album in the first repeater.
    Is that correct?



  • If I understand you correctly I should have a single model songsModel = {{song1, album1, etc},{song2,album2,etc}}
    whose model is songsModel filtered to only hold the albums.

    With this implementation you will have to sort each item, instead of filtering them out.
    ListView has something named sections that will allow you to create a section for each album.



  • @rrlopez said in How to group items?:

    With this implementation you will have to sort each item, instead of filtering them out.

    Then I really don't understand what you're suggesting with the filtering.
    Can you provide a simple example of what you mean?

    ListView has something named sections that will allow you to create a section for each album.

    That's kinda what I want, but it doesn't look like I can get the songs on the right of the section, the height of the section would be ~300px.



  • @fbg13, this is what I meant:

    ColumnLayout {
            anchors.fill: parent
            RowLayout {
                id: group
                Layout.alignment: Qt.AlignTop
                Layout.fillHeight: true
                Layout.fillWidth: true
                ColumnLayout {
                    id: songs
                    Layout.alignment: Qt.AlignTop
                    Layout.fillWidth: true
                    Label {}
                    ListView {
                        height: contentHeight
                        section.property: "album"
                        section.delegate: Text {
                            text: section
                            font.bold: true
                        }
                        model: songsModel
                        delegate: SongRow {
                            artist: model.artist
                            title: model.title
                            album: model.album
                        }
                    }
                }
            }
        }
    

    I used a text as a delegate, but you can use whatever delegate you want. Let me know if it works for you.



  • @rrlopez I understood the ListView sections, but I don't think it fits my needs, since the section should hold the album cover and songs should be on the right of this image (like in the screenshot). If I add the image in the section I get this
    99185144-5191-459c-bb4d-799d6a89dfb3-image.png

    Gray area is the section, red is the "cover image".


    What I don't understand is your QSortFilterProxyModel and QAbstractProxyModel suggestions.

    @rrlopez said in How to group items?:

    @fbg13 I would use a QAbstractProxyModel to act as a model of your table view and change the way you define stuff in your QML.
    Basically add a row on the model for each song you want to display (artist, songname, artist, etc) and then access these roles on QML as model.artist, model.songname, etc.
    As you'll be using a QAbstractProxyModel you will have access to its sorting and filtering methods, so you will be able to filter out songs on artist. You basically will have N items where N is the number of artists, and each item from N will have a different filter to show only songs related to the corresponding artist.


Log in to reply