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

Shuffling the items on QtableView



  • I made the following model/view structure.
    mymodel=new QAbstractTableModel;
    proxyModel = new StarSortFilterProxyModel();
    proxyModel->setDynamicSortFilter(true);
    tableView=new QTableView(this);
    tableView->setModel(proxyModel);

    I want to shuffle the order of items on tableview without changing the order of items on base model(mymodel). Can anyone explain how I can randomly order it? It seems I need to shuffle the order of indexes of proxyModel. Thank you.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    You need to implement your own QSortFilterProxyModel that will do the shuffling.



  • Which member functions of QSortFilterProxyModel should I reimplement to create shuffling function?



  • @samdol

    protected:
        bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
    

    You can use std::random to determine false and true randomly.

    bool some_class::lessThan(const QModelIndex&, const QModelIndex&) const
    {
        return dis(gen) < 1;
    }
    

    The example of std::random(full version locate at here)

    #include <random>
    #include <iostream>
     
    int main()
    {
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(0, 1);
     
        for (int n=0; n<10; ++n)
            std::cout << dis(gen) << ' ';
        std::cout << '\n';
    }
    

    random_device may or may not generate random seed(unless it do not work for mingw5.3), in this case qrand() could be a replacement.



  • I had a similar problem a couple of months ago and @VRonin suggested I take a look at the KDE API. https://api.kde.org/frameworks/kitemmodels/html/classKExtraColumnsProxyModel.html. While I did not use it directly, I did use the techniques in there to build a custom class that did just what you ask.

    // reorderColProxyModel.h
    #pragma once
    
    #include "constants.h"
    
    #include <QObject>
    #include <QIdentityProxyModel>
    
    class ReOrderColProxyModel : public QIdentityProxyModel
    {
    public:
        ReOrderColProxyModel(QObject *parent = Q_NULLPTR);
        ~ReOrderColProxyModel() = default;
    
        QVariant data(const QModelIndex &proxyIndex, int role) const;
        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    
    protected:
        int columnCount(const QModelIndex &) const;
    
    private:
        constexpr static int _size= 15;
        // list columns in order to be seen
        REPAIR_STATUS_COLUMN _forwardLookup[_size]
        {
            REPAIR_STATUS_COLUMN::RMA_NUMBER,
            REPAIR_STATUS_COLUMN::WORKDAYS,
            REPAIR_STATUS_COLUMN::CUSTOMER_NAME,
            REPAIR_STATUS_COLUMN::LOCATION,
            REPAIR_STATUS_COLUMN::MODEL,
    
            REPAIR_STATUS_COLUMN::RMA_ISSUE_DATE,
            REPAIR_STATUS_COLUMN::RCVD_DATE,
            REPAIR_STATUS_COLUMN::DONE_DATE,
            REPAIR_STATUS_COLUMN::SHIP_DATE,
            REPAIR_STATUS_COLUMN::CURRENT_STATUS,
    
            REPAIR_STATUS_COLUMN::SPECIAL_NOTES,
            REPAIR_STATUS_COLUMN::TECH,
            REPAIR_STATUS_COLUMN::QB_NOTES,
            REPAIR_STATUS_COLUMN::RMA_NOTES,
            REPAIR_STATUS_COLUMN::RMA_UNIT_NOTES
        };
    };
    
    
    // reorderColProxyModel.cpp
    #include "reorderColProxyModel.h"
    
    //----------------------------------------------------------------------
    ReOrderColProxyModel::ReOrderColProxyModel(QObject *parent)
        :QIdentityProxyModel(parent)
    {}
    
    //----------------------------------------------------------------------
    QVariant ReOrderColProxyModel::data(const QModelIndex &proxyIndex, int role) const
    {
        int row= proxyIndex.row();
        int col= proxyIndex.column();
        QModelIndex ix= sourceModel()->index(row,static_cast<int>(_forwardLookup[col]));
        return ix.data(role);
    }
    
    //----------------------------------------------------------------------
    QVariant ReOrderColProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
        return sourceModel()->headerData(static_cast<int>(_forwardLookup[section]),orientation,role);
    }
    
    //----------------------------------------------------------------------
    int ReOrderColProxyModel::columnCount(const QModelIndex &) const
    {
        return _size;
    }
    
    

    Where REPAIR_STATUS_COLUMN is an enum of the columns in the table. _forwardLookup is in the order that you want the columns to appear. Being a proxymodel, you just set the source model and use it to display the columns in the order you like.

    You should look at the code in the link. It is much more generalised and capable than this and just good code to learn from.

    Edit:
    The above link is not the solution to your problem. My original problem was two-fold. First I had to add a computed column which wouldn't reflect back into the original model then I had to reorder the columns. The link to the class which did the re-ordering is here: https://api.kde.org/frameworks/kitemmodels/html/classKRearrangeColumnsProxyModel.html

    You will notice that it subclasses QIdentityProxyModel which is a better fit than QSortFilterProxyModel.

    I ended up using a simplified version of each class, implementing only the bare necessities, but I only had a single use. YMMV

    Mike



  • @tham

    Thank you very much. It works.
    I created extra shuffle_colmun in base model.
    Instead of std::random() I simply used rand() to generate integer between 0-20. So the result rand()%20 < 10 is half true and half false. It means if I sort contents by shuffle_colmun, it shuffles the contents without changing the order of original base model.

    class MySortFilterProxyModel : public QSortFilterProxyModel
    {
    Q_OBJECT

    public:
    MySortFilterProxyModel(){}

    protected:
    bool lessThan(const QModelIndex &left, const QModelIndex &right) const
    {
    if(left.column()== shuffle_column)
    return rand()%20 < 10;
    else
    return QSortFilterProxyModel::lessThan(left, right);
    }
    };

    void shuffling()
    {
    if(tableView->horizontalHeader()->sortIndicatorOrder()==Qt::DescendingOrder)
    tableView->sortByColumn(shuffle_column, Qt::AscendingOrder);
    else
    tableView->sortByColumn(shuffle_column, Qt::DescendingOrder);
    }


  • Lifetime Qt Champion

    You can simplify the shuffling function:

    void shuffling()
    {
    tableView->sortByColumn(shuffle_column, tableView->horizontalHeader()->sortIndicatorOrder());
    }
    

Log in to reply