How to sort items in listview in qml



  • Hello Guys!

    I want to know how could I sort items in a listview populated from locally available data in local db in list model.
    BTW I cannot sort on sqlite query since after loading data from db I need to make some changes in for example replace "Book Of" with " ". So after that I need to sort my data but not in sql query.

    So simply if you have a populated listview in qml how could we sort its contents alphabetically?

    Thank You



  • Sorry, AFAIK, this is not (yet) possible to do in pure QML. Instead, you could create your model from C++ and use a [[doc:QSortFilterProxyModel]] to do the sorting, and then expose that model to your QML.



  • What Andre said is right and probably the best way. I tried another solution that works, but this implies a little more resources and a custom sort function.
    When the component is completed you can fill another ListModel with sorted elements of your first one and use it.
    I used a XmlListModel here, but the concept is the same.
    buttonsGridView is the GridView and you can fill listModel by:

    @Component:onCompleted: { fillListModel(); sortModel(); }@

    here there is what I did:

    @function sortModel()
    {
    var n;
    var i;
    for (n=0; n < buttonsGridView.count; n++)
    for (i=n+1; i < buttonsGridView.count; i++)
    {
    if (buttonsGridView.model.get(n).nameCat > buttonsGridView.model.get(i).nameCat)
    {
    buttonsGridView.model.move(i, n, 1);
    n=0; // Repeat at start since I can't swap items i and n
    }
    }
    }

    // Use a ListModel to sort out the list
    function fillListModel()
    {
        var n;
        for (n=0; n&lt; xmlModel.count; n++)
            listModel.append({"idCat": xmlModel.get(n).idCat, "nameCat": xmlModel.get(n).nameCat})
    }
    
    XmlListModel {
        id: xmlModel
        source: ...
        query: ...
    
        XmlRole { name: "idCat";  query: "id/string()" }
        XmlRole { name: "nameCat"; query: "name/string()" }
    
        onStatusChanged: if (status === XmlListModel.Ready) { fillListModel(); sortModel(); }
    }
    
    
    ListModel {
        id: listModel
    }@


  • Nice trick, but it will not work in the current form if the underlying model is dynamic (and that is what you need sorting for in the first place, otherwise you'd just use a static, pre-sorted model in the first place) and the sorting routine looks painfully slow. Not an issue if you need to sort a small list, but this will quickly start to be a performance hit I think.



  • Yes, you are right. I used it for just 20~30 items and the bubble sort method isn't efficient because the swap element is not supported, but sort function could/should be reimplemented.
    If the model is dynamic something like this could be of some help ?

    @ Timer {
    id: waitTimer
    interval: 50
    repeat: false
    triggeredOnStart: false
    running: false
    onTriggered: { fillListModel(); sortModel(); }
    }

    ListModel {
        id: listModelToSort
        onItemsInserted: waitTimer.restart()
    }@


  • Hallo, I was looking for soemthing really similar. I tried it but I am gettin error. could not rectify it. Kindly help me out!

    @import QtQuick 1.1
    //import "check.js" as Chk

    Rectangle{

    width:400
    height:width

    Component.onCompleted: { sortModel(); fillListModel();}
    function sortModel()
    {
    var n;
    var i;
    for (n=0; n < grdview.count; n++)
    for (i=n+1; i < grdview.count; i++)
    {
    if (grdview.model.get(n).id> grdview.model.get(i).id)
    {
    // console.log("checking for the id ")
    // console.log(grdview.model.get(n).title)
    grdview.model.move(i, n, 1);
    n=0;
    }
    }}

    function fillListModel()
    {
        var n;
        for (n=0; n &lt; xmlModel.count; n++)
        {
            console.log(xmlModel.count)
            console.log("Sfasd")
            listModel.append({"title": xmlModel.get(n).title, "pubDate":xmlModel.get(n).pubDate, "id":xmlModel.get(n).id})
    }}
    

    XmlListModel {
    id: xmlModel
    source: "example.xml"
    query: "/rss/channel/item"
    XmlRole { name: "id"; query: "id/string()" }
    XmlRole { name: "title"; query: "title/string()" }
    XmlRole { name: "pubDate"; query: "pubDate/string()" }
    onStatusChanged: if (status === XmlListModel.Ready) {sortModel();fillListModel(); }
    }

    GridView{
    id:grdview
    width: 180; height: 300
    model:xmlModel
    delegate: Text { text:title+ ": " + pubDate+id
    }
    visible:false

    }

    ListModel {
    id: listModel

    }
    ListView{

    model:listModel
    width: 180; height: 300
    delegate: Text { text:title+ ": " + pubDate+id}

    }

    }
    @

    and the error i get is :TypeError: Result of expression 'grdview.model.move' [undefined] is not a function. kindly let me know what is the mistake. I can also send u the xml file if required.



  • @<?xml version="1.0" encoding="utf-8"?>
    <rss version="2.0">
    ...
    <channel>
    <item>
    <title>A blog post</title>
    <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate>
    <id> 3 </id>
    </item>
    <item>
    <title>A blog post</title>
    <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate>
    <id> 4 </id>
    </item>
    <item>
    <title>A blog post</title>
    <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate>
    <id> 2 </id> </item>
    <item>
    <title>A blog post</title>
    <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate>
    <id> 1 </id>
    </item>
    <item>
    <title>Another blog post</title>
    <pubDate>Sat, 07 Sep 2010 15:35:01 GMT</pubDate>
    <id> 0 </id>
    </item>
    </channel>
    </rss>@

    This is the xml file am using



  • Hi,

    I found something wrong in you code: the dummy model listModel should be filled before sorting it and gridView model is a XmlModelList that is read only, so in the sort function you can't move elements.
    I come into this implementation and added also a listModel.clear(); in the fillListModel() function because if the xml model get updated, the new reads will be appended to the already filled model.

    regards

    @import QtQuick 1.1
    //import "check.js" as Chk

    Rectangle{

    width:400
    height:width
    
    function sortModel()
    {
        var n;
        var i;
        for (n=0; n < listModel.count; n++)
            for (i=n+1; i < listModel.count; i++)
            {
                if (listModel.get(n).id> listModel.get(i).id)
                {
                    listModel.move(i, n, 1);
                    n=0;
                }
            }
    }
    
    function fillListModel()
    {
        var n;
        listModel.clear();
        for (n=0; n &lt; xmlModel.count; n++)
        {
            listModel.append({"title": xmlModel.get(n).title, "pubDate":xmlModel.get(n).pubDate, "id":xmlModel.get(n).id})
        }
    }
    
    
    XmlListModel {
        id: xmlModel
        source: "example.xml"
        query: "/rss/channel/item"
        XmlRole { name: "id"; query: "id/string()" }
        XmlRole { name: "title"; query: "title/string()" }
        XmlRole { name: "pubDate"; query: "pubDate/string()" }
        onStatusChanged: if (status === XmlListModel.Ready) { console.log("xml read: ", count); fillListModel(); sortModel(); }
    }
    
    GridView{
        id:grdview
        width: 180; height: 300
        model: xmlModel
        delegate: Text { text:title+ ": " + pubDate+id }
        visible:false
    }
    
    ListModel {
        id: listModel
    
    }
    
    ListView{
    
        model:listModel
        width: 180; height: 300
        delegate: Text { text:title+ ": " + pubDate+id}
    }
    

    }@



  • Hey, thanks you. works great.. I appreciate it. [Solved]



  • Your welcome, but remember what Andre wrote:

    bq. the sorting routine looks painfully slow. Not an issue if you need to sort a small list, but this will quickly start to be a performance hit I think.

    regards



  • In my projetc i add the quick sort algorythm to ListModel.

    It works pretty well.
    Save this code into a qml file , for example SortListModel.qml
    and use @SortListModel.sortColumnName="productName";
    SortListModel.quick_sort();@

    @import QtQuick 2.0

    ListModel {

    property string sortColumnName: ""
    
    id: sortlistmodel
    
    function swap(a,b) {
        if (a<b) {
            move(a,b,1);
            move (b-1,a,1);
        }
        else if (a>b) {
            move(b,a,1);
            move (a-1,b,1);
        }
    }
    
    function partition(begin, end, pivot)
    {
        var piv=get(pivot)[sortColumnName];
        swap(pivot, end-1);
        var store=begin;
        var ix;
        for(ix=begin; ix<end-1; ++ix) {
            if(get(ix)[sortColumnName] < piv) {
                swap(store,ix);
                ++store;
            }
        }
        swap(end-1, store);
    
        return store;
    }
    
    function qsort(begin, end)
    {
        if(end-1>begin) {
            var pivot=begin+Math.floor(Math.random()*(end-begin));
    
            pivot=partition( begin, end, pivot);
    
            qsort(begin, pivot);
            qsort(pivot+1, end);
        }
    }
    
    function quick_sort() {
        qsort(0,count)
    }
    

    }@



  • @remy67 I know it's been a long time, but thank you!! I was able to use this and it was very helpful

    I added a sort order argument as well (super easy with your setup)


    import QtQuick 2.0

    ListModel {

    property string sortColumnName: ""
    property string order: "desc" //set to either asc or desc
    id: sortlistmodel
    
    function swap(a,b) {
        if (a<b) {
            move(a,b,1);
            move (b-1,a,1);
        }
        else if (a>b) {
            move(b,a,1);
            move (a-1,b,1);
        }
    }
    
    function partition(begin, end, pivot)
    {
        var piv=get(pivot)[sortColumnName];
        swap(pivot, end-1);
        var store=begin;
        var ix;
        for(ix=begin; ix<end-1; ++ix) {
            if (order ==="asc"){
                if(get(ix)[sortColumnName] < piv) {
                    swap(store,ix);
                    ++store;
                }
            }else if (order ==="desc"){
                if(get(ix)[sortColumnName] > piv) {
                    swap(store,ix);
                    ++store;
                }
            }
        }
        swap(end-1, store);
    
        return store;
    }
    
    function qsort(begin, end)
    {
        if(end-1>begin) {
            var pivot=begin+Math.floor(Math.random()*(end-begin));
    
            pivot=partition( begin, end, pivot);
    
            qsort(begin, pivot);
            qsort(pivot+1, end);
        }
    }
    
    function quick_sort() {
        qsort(0,count)
    }
    

    }


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.