Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QSortFilterProxyModel is filterning, but not sorting

QSortFilterProxyModel is filterning, but not sorting

Scheduled Pinned Locked Moved Solved General and Desktop
11 Posts 2 Posters 5.2k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • dijunaD Offline
    dijunaD Offline
    dijuna
    wrote on last edited by
    #1

    I have four QTableView/QStyledItemDelegate/QSortFilterProxyModel groups pointing to single QAbstractTableModel object; different filtering and separate sorting. And, well... sorting is not working at all. This is how I connect everything:

        //My QAbstractTableModel:
        UnitFormsTableModel *source_model = new UnitFormsTableModel(p_curr_meetState, ulist, masks, use_cards, this);
    
        //My QSortFilterProxyModels:
        QList<UnitFormProxyModel*> models;
        models.append(new UnitFormProxyModel(ModelType::MAIN, this));
        models.append(new UnitFormProxyModel(ModelType::SPEAKERS, this));
        models.append(new UnitFormProxyModel(ModelType::Q1, this));
        models.append(new UnitFormProxyModel(ModelType::Q2, this));
    
        QList<QTableView*> views;
        views.append(ui->MAIN);
        views.append(ui->SPEAKERS);
        views.append(ui->Q1);
        views.append(ui->Q2);
    
        for (qint32 i = 0; i < 4; ++i) {
            UnitFormProxyModel *model = models.at(i);
            QTableView *view = views.at(i);
    
            view->setAlternatingRowColors(true);
            view->setSelectionMode(QAbstractItemView::ExtendedSelection);
            view->setSelectionBehavior(QAbstractItemView::SelectRows);
            view->setSortingEnabled(true);
            view->verticalHeader()->hide();
    
            QHeaderView* header = view->horizontalHeader();
            header->setSectionResizeMode(QHeaderView::ResizeToContents);
            header->setStretchLastSection(true);
    
            model->setSourceModel(source_model);
            model->setDynamicSortFilter(true);
    
            //My QStyledItemDelegate:
            UnitFormDelegate *delegate = new UnitFormDelegate(use_cards, model);
            connect(delegate, &UnitFormDelegate::buttonCommand, this, &ConferenceForm::buttonCommand);
            view->setItemDelegate(delegate);
    
            view->setModel(model);
        }
    

    Now, after I click on the QTableView's horizontal header, the arrow indicating the change of the sort order is behaving correctly. The generic QAbstractTableModel::sort is called, and custom QSortFilterProxyModels::lessThan is called different number of times... so something is going on after I click the header. Still, I can see no change in rows' order of the corresponding view, even though what I see lessThan() is returning differs a lot between every click. What am I missing? I was checking with some examples, but with no luck... should I reimplement sort()? How?

    1 Reply Last reply
    0
    • VRoninV Offline
      VRoninV Offline
      VRonin
      wrote on last edited by
      #2

      Difficult to tell given you use a custom proxy. can you show us what UnitFormProxyModel is?

      did you set the sort role?

      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
      ~Napoleon Bonaparte

      On a crusade to banish setIndexWidget() from the holy land of Qt

      dijunaD 1 Reply Last reply
      2
      • dijunaD Offline
        dijunaD Offline
        dijuna
        wrote on last edited by dijuna
        #3

        @VRonin I've prepared minimal application showing my problem, no filtering or other app-related stuff there as it would be just too much of a code:
        https://github.com/Dijuna/SortExample
        It works like that: you click on the buttons to change the underlying data's values, and then you click on the header... it should sort the rows based on the buttons' state, but it does nothing. You can see sort() and lessThat() methods being called.

        About the sort role, how does it work? I want my own sorting, depending on more than one role and I've thought lessThan() does it.

        1 Reply Last reply
        0
        • VRoninV Offline
          VRoninV Offline
          VRonin
          wrote on last edited by VRonin
          #4

          bool out = (left.data(qint32(UserData::NO)).toBool() < right.data(qint32(UserData::NO)).toBool()); are you sure you want to use toBool there instead of comparing the variants? mydata->no is qint32 so that will return true only if left.data(qint32(UserData::NO)) is 0. is that what you want?

          Also, see https://pastebin.com/H1GAgB7V for a general multi-column sort implementation

          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
          ~Napoleon Bonaparte

          On a crusade to banish setIndexWidget() from the holy land of Qt

          1 Reply Last reply
          2
          • VRoninV VRonin

            Difficult to tell given you use a custom proxy. can you show us what UnitFormProxyModel is?

            did you set the sort role?

            dijunaD Offline
            dijunaD Offline
            dijuna
            wrote on last edited by dijuna
            #5

            @VRonin it's just a mistake made when I was preparing this example for you, it looks different in my original application and I was testing with boolean UserData::SPEAKING values anyway. So it's an non-issue, but I've fixed it. :)

            Setting two buttons to "1", leaving all the others as "0", and pressing B1 header twice gives that:

            sorting...
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: true
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: true
            ...by button1: true
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: true
            ...by button1: true
            ...by button1: true
            ...by button1: true
            ...by button1: true
            sorting...
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: true
            ...by button1: false
            ...by button1: true
            ...by button1: true
            ...by button1: true
            ...by button1: false
            ...by button1: true
            ...by button1: true
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false
            ...by button1: false

            1 Reply Last reply
            0
            • VRoninV Offline
              VRoninV Offline
              VRonin
              wrote on last edited by VRonin
              #6

              Disclaimers:

              • isVariantLessThan is copy-pasted from Qt sources
              • The below does not mean I approve in any way your design choices in the rest of the program
              • instead of using filterAcceptsColumn() (and all that comes with it) you could just call setColumnVisible() on the view and simplify the proxy even more

              myproxymodel.h

              #ifndef MYPROXYMODEL_H
              #define MYPROXYMODEL_H
              
              #include <QSortFilterProxyModel>
              
              class MyProxyModel : public QSortFilterProxyModel
              {
                  Q_OBJECT
                  Q_DISABLE_COPY(MyProxyModel)
              public:
                  explicit MyProxyModel(bool second_button, QObject *parent = nullptr);
                  const bool second_button;
              protected:
                  bool filterAcceptsColumn(int source_column, const QModelIndex &sourceParent = QModelIndex()) const override;
                  bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
              private:
                  bool isVariantLessThan(const QVariant &left, const QVariant &right, Qt::CaseSensitivity cs, bool isLocaleAware) const;
              };
              
              #endif // MYPROXYMODEL_H
              

              myproxymodel.cpp

              #include "myproxymodel.h"
              #include "enums.h"
              #include <QDateTime>
              MyProxyModel::MyProxyModel(bool second_button, QObject *parent)
                  : QSortFilterProxyModel(parent)
                  ,  second_button { second_button }
              {
              
              }
              
              
              bool MyProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
              {
                  qint32 srtRole;
                  switch (source_left.column()) {
                  case qint32(ModelColumns::NUMBER):
                      srtRole = qint32(UserData::NO);
                  break;
                  case qint32(ModelColumns::BUTTON2):
                  case qint32(ModelColumns::BUTTON1):
                      srtRole=qint32(UserData::SPEAKING);
                  break;
                  default:
                      Q_UNREACHABLE(); // Unsupportd column
                  }
              
                  const QVariant l = (source_left.model() ? source_left.model()->data(source_left, srtRole) : QVariant());
                  const QVariant r = (source_right.model() ? source_right.model()->data(source_right, srtRole) : QVariant());
                  return isVariantLessThan(l, r, sortCaseSensitivity(), isSortLocaleAware());
              }
              
              bool MyProxyModel::isVariantLessThan(const QVariant &left, const QVariant &right, Qt::CaseSensitivity cs, bool isLocaleAware) const
              {
                  if (left.userType() == QVariant::Invalid)
                      return false;
                  if (right.userType() == QVariant::Invalid)
                      return true;
                  switch (left.userType()) {
                  case QVariant::Int:
                      return left.toInt() < right.toInt();
                  case QVariant::UInt:
                      return left.toUInt() < right.toUInt();
                  case QVariant::LongLong:
                      return left.toLongLong() < right.toLongLong();
                  case QVariant::ULongLong:
                      return left.toULongLong() < right.toULongLong();
                  case QMetaType::Float:
                      return left.toFloat() < right.toFloat();
                  case QVariant::Double:
                      return left.toDouble() < right.toDouble();
                  case QVariant::Char:
                      return left.toChar() < right.toChar();
                  case QVariant::Date:
                      return left.toDate() < right.toDate();
                  case QVariant::Time:
                      return left.toTime() < right.toTime();
                  case QVariant::DateTime:
                      return left.toDateTime() < right.toDateTime();
                  case QVariant::String:
                  default:
                      if (isLocaleAware)
                          return left.toString().localeAwareCompare(right.toString()) < 0;
                      else
                          return left.toString().compare(right.toString(), cs) < 0;
                  }
              }
              
              bool MyProxyModel::filterAcceptsColumn(int source_column, const QModelIndex &sourceParent) const
              {
                  Q_UNUSED(sourceParent)
              
                  if (source_column == qint32(ModelColumns::BUTTON1)) {
                      return !second_button;
                  }
                  else if (source_column == qint32(ModelColumns::BUTTON2)) {
                      return second_button;
                  }
                  return true;
              }
              

              Basically the problem was that lessThan allready gets an index of the source model as input and your mapToSource implementation disregarded this

              "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
              ~Napoleon Bonaparte

              On a crusade to banish setIndexWidget() from the holy land of Qt

              1 Reply Last reply
              1
              • dijunaD Offline
                dijunaD Offline
                dijuna
                wrote on last edited by dijuna
                #7

                @VRonin I'm trying to go with your approach. I've inherited my proxy model from your MultiProxyModel and not from its original base class. Now it looks like this (for now just testing with one column):

                void UnitFormProxyModel::sort(int column, Qt::SortOrder order)
                {
                    clearSortPriority();
                    qint32 source_column = mapToSource(this->index(0, column)).column();
                    switch (source_column) {
                    case qint32(ModelColumns::UNIT_NO):
                        addSortPriority(column, qint32(UserData::UNIT_NO));
                        break;
                    default:
                        Q_UNREACHABLE();
                    }
                
                    MultiSortProxyModel::sort(column, order); //renamed it for aesthetic reasons, but it's your MultyProxyModel class
                }
                
                bool UnitFormProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
                {
                    return MultiSortProxyModel::lessThan(mapToSource(left), mapToSource(right));
                }
                

                And again, all the methods are being called, but I can see no other change than the header's arrow... what am I missing? The proxy is comparing things, but not sorting them... or the view is ignoring the changes, I can't tell.

                Basically the problem was that lessThan allready gets an index of the source model as input and your mapToSource implementation disregarded this

                I will test that, but now I'm trying to sort the first column (with 0 as index) which is 0 for both source and proxy... and I can't see the changes anyway.

                About the hideColumn() and my bad design choices - I agree, but let's solve the main issue, with ANY approach... I just want it to work. ;_;

                1 Reply Last reply
                0
                • VRoninV Offline
                  VRoninV Offline
                  VRonin
                  wrote on last edited by VRonin
                  #8

                  @VRonin said in QSortFilterProxyModel is filterning, but not sorting:

                  Basically the problem was that lessThan allready gets an index of the source model as input and your mapToSource implementation disregarded this

                  that means you don't have to mapToSource inside lessThan. The fact that you don't get an assertion makes me doubt the correctness of your mapToSource implementation. just delete lessThan altogether and use the base class' default

                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                  ~Napoleon Bonaparte

                  On a crusade to banish setIndexWidget() from the holy land of Qt

                  dijunaD 1 Reply Last reply
                  1
                  • VRoninV VRonin

                    @VRonin said in QSortFilterProxyModel is filterning, but not sorting:

                    Basically the problem was that lessThan allready gets an index of the source model as input and your mapToSource implementation disregarded this

                    that means you don't have to mapToSource inside lessThan. The fact that you don't get an assertion makes me doubt the correctness of your mapToSource implementation. just delete lessThan altogether and use the base class' default

                    dijunaD Offline
                    dijunaD Offline
                    dijuna
                    wrote on last edited by
                    #9

                    @VRonin said in QSortFilterProxyModel is filterning, but not sorting:

                    that means you don't have to mapToSource inside lessThan. The fact that you don't get an assertion makes me doubt the correctness of your mapToSource implementation. just delete lessThan altogether and use the base class' default

                    I had it only to qDebug() and see the action: I knew for sure it was just sending (0, 0) indexes. Now it's deleted and nothing changed... still, my mapToSource() is called somewhere anyway and it's changing

                    QModelIndex(0,0,0x0,UnitFormsTableModel(0x1705e268))

                    to

                    QModelIndex(0,0,0x1d762d68,UnitFormProxyModel(0x1c626940)).

                    Is that okay?

                    1 Reply Last reply
                    0
                    • VRoninV Offline
                      VRoninV Offline
                      VRonin
                      wrote on last edited by
                      #10

                      It is but something might go wrong if you reimplemented mapToSource()

                      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                      ~Napoleon Bonaparte

                      On a crusade to banish setIndexWidget() from the holy land of Qt

                      dijunaD 1 Reply Last reply
                      1
                      • VRoninV VRonin

                        It is but something might go wrong if you reimplemented mapToSource()

                        dijunaD Offline
                        dijunaD Offline
                        dijuna
                        wrote on last edited by
                        #11

                        @VRonin said in QSortFilterProxyModel is filterning, but not sorting:

                        It is but something might go wrong if you reimplemented mapToSource()

                        How? If the output is as it should be... it's const method, it can't break things, right? Still, I've tested it and you were right: generic mapToSource() and mapFromSource() methods have exactly the same outputs as mine (I don't know why I thought I need to create them) and so I've deleted my methods and now everything is as it should be... thank you very much! Still, I'll check the Qt code to see what was the difference...

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved