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. How to sort original data structure of QAbstractTableModel?
Forum Updated to NodeBB v4.3 + New Features

How to sort original data structure of QAbstractTableModel?

Scheduled Pinned Locked Moved Solved General and Desktop
20 Posts 4 Posters 3.0k 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.
  • C CuriousPan

    Hello!
    I'm using QAbstractTableModel for representing data in table. Also, I'm using QSortFilterProxyModel as a wrapper for sorting. Hence, I implement lessThan method for comparison.
    Unfortunately, this sorts only the view, but I also need to sort the data structre my table takes data from. Is there a way to do it?

    eyllanescE Offline
    eyllanescE Offline
    eyllanesc
    wrote on last edited by
    #2

    @CuriousPan Qt doesn't do that but if you want to implement that feature then override the model's QAbstractItemModel::sort method and implement the sorting there.

    If you want me to help you develop some work then you can write to my email: e.yllanescucho@gmal.com.

    C 1 Reply Last reply
    1
    • eyllanescE eyllanesc

      @CuriousPan Qt doesn't do that but if you want to implement that feature then override the model's QAbstractItemModel::sort method and implement the sorting there.

      C Offline
      C Offline
      CuriousPan
      wrote on last edited by
      #3

      @eyllanesc, well, I implemented sort method in QAbstractTableModel, but it's not even called.

      eyllanescE JonBJ 2 Replies Last reply
      0
      • C CuriousPan

        @eyllanesc, well, I implemented sort method in QAbstractTableModel, but it's not even called.

        eyllanescE Offline
        eyllanescE Offline
        eyllanesc
        wrote on last edited by
        #4

        @CuriousPan How are they calling him or what action should he be called upon? You should no longer use QSortFilterProxyModel.

        If you want me to help you develop some work then you can write to my email: e.yllanescucho@gmal.com.

        1 Reply Last reply
        0
        • C CuriousPan

          @eyllanesc, well, I implemented sort method in QAbstractTableModel, but it's not even called.

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by
          #5

          @CuriousPan said in How to sort original data structure of QAbstractTableModel?:

          well, I implemented sort method in QAbstractTableModel, but it's not even called.

          You have to call it if you want it to sort.

          Also, I'm using QSortFilterProxyModel as a wrapper for sorting.
          [...]
          Unfortunately, this sorts only the view,

          No "view" involved here. QSortFilterProxyModel is a proxy for your model, it's a model itself. You have to access via the proxy model if you want to see things sorted, it does not sort the underlying model.

          C 1 Reply Last reply
          3
          • JonBJ JonB

            @CuriousPan said in How to sort original data structure of QAbstractTableModel?:

            well, I implemented sort method in QAbstractTableModel, but it's not even called.

            You have to call it if you want it to sort.

            Also, I'm using QSortFilterProxyModel as a wrapper for sorting.
            [...]
            Unfortunately, this sorts only the view,

            No "view" involved here. QSortFilterProxyModel is a proxy for your model, it's a model itself. You have to access via the proxy model if you want to see things sorted, it does not sort the underlying model.

            C Offline
            C Offline
            CuriousPan
            wrote on last edited by
            #6

            @JonB, now I'm calling sort method of source model in lessThan method. The datastructre is getting sorted, as well as the view, but view is sorted incorrectly. Isn't it strange?
            Here's the view:
            5dc5d9d5-cc29-4a32-a120-7dc7dc321677-image.png

            Printout of the situation inside the datastructure:
            21cf4fb7-d690-41b6-a331-34618bd83a72-image.png

            JonBJ 1 Reply Last reply
            0
            • C CuriousPan

              @JonB, now I'm calling sort method of source model in lessThan method. The datastructre is getting sorted, as well as the view, but view is sorted incorrectly. Isn't it strange?
              Here's the view:
              5dc5d9d5-cc29-4a32-a120-7dc7dc321677-image.png

              Printout of the situation inside the datastructure:
              21cf4fb7-d690-41b6-a331-34618bd83a72-image.png

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #7

              @CuriousPan said in How to sort original data structure of QAbstractTableModel?:

              now I'm calling sort method of source model in lessThan method.

              Pardon? That is not going to be a good thing to do....

              but view is sorted incorrectly.

              Now I'm not sure whether your view is connected to your QSortFilterProxyModel or your underlying (sorted) model.

              You had better show code --- preferably minimal --- for whatever you are doing which illustrates problem.

              P.S.
              If you are really calling a sort() inside a lessThan() comparison, and that changes the order of the data, your results e.g. in a view are liable to be rubbish. You must not do that! Any lessThan() method will be const, and you should regard it as forbidden to change any state inside it.

              1 Reply Last reply
              1
              • C Offline
                C Offline
                CuriousPan
                wrote on last edited by CuriousPan
                #8

                Okay, here's the code:

                //m_model is a ForecastingProxyModel class inheriting from QAbstractTableModel
                ForecastingProxyModel *proxyModel = new ForecastingProxyModel(this);
                proxyModel->setSourceModel(m_model);
                ui->forecastingTableView->setModel(proxyModel);
                ui->forecastingTableView->setSortingEnabled(true);
                

                Here's the code of the lessThan of the QSortFilterProxyModel:

                bool ForecastingProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
                {
                    const QVariant &leftData = sourceModel()->data(sourceLeft);
                    const QVariant &rightData = sourceModel()->data(sourceRight);
                    //sourceModel()->sort(sourceLeft.column(), sortOrder());  you said it's a bad idea to call it hear.
                
                    if (leftData.userType() == QMetaType::QDate) {
                        return leftData.toDate() < rightData.toDate();
                    } else if (leftData.userType() == QMetaType::QString) {
                        return leftData.toString() < rightData.toString();
                    } else if (leftData.userType() == QMetaType::Double) {
                        return leftData.toDouble()  < rightData.toDouble();
                    } else if (leftData.userType() == QMetaType::Bool) {
                        return leftData.toBool() < rightData.toBool();
                    }
                
                    return QSortFilterProxyModel::lessThan(sourceLeft, sourceRight);
                }
                

                Here's a sort method of the source model:

                void ForecastingModel::sort(int column, Qt::SortOrder order)
                {
                    switch(column) {
                    case Name:
                        if (order == Qt::AscendingOrder) {
                            std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                      [](const Forecast &a, const Forecast &b) { return a.name < b.name; });
                        } else {
                            std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                      [](const Forecast &a, const Forecast &b) { return a.name > b.name; });
                        }
                        break;
                
                    case Price:
                        if (order == Qt::AscendingOrder) {
                            std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                      [](const Forecast &a, const Forecast &b) { return a.price < b.price; });
                        } else {
                            std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                      [](const Forecast &a, const Forecast &b) { return a.price > b.price; });
                        }
                        break;
                    }
                }
                

                Is it enough or something more is required?

                JonBJ Christian EhrlicherC 2 Replies Last reply
                0
                • C CuriousPan

                  Okay, here's the code:

                  //m_model is a ForecastingProxyModel class inheriting from QAbstractTableModel
                  ForecastingProxyModel *proxyModel = new ForecastingProxyModel(this);
                  proxyModel->setSourceModel(m_model);
                  ui->forecastingTableView->setModel(proxyModel);
                  ui->forecastingTableView->setSortingEnabled(true);
                  

                  Here's the code of the lessThan of the QSortFilterProxyModel:

                  bool ForecastingProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
                  {
                      const QVariant &leftData = sourceModel()->data(sourceLeft);
                      const QVariant &rightData = sourceModel()->data(sourceRight);
                      //sourceModel()->sort(sourceLeft.column(), sortOrder());  you said it's a bad idea to call it hear.
                  
                      if (leftData.userType() == QMetaType::QDate) {
                          return leftData.toDate() < rightData.toDate();
                      } else if (leftData.userType() == QMetaType::QString) {
                          return leftData.toString() < rightData.toString();
                      } else if (leftData.userType() == QMetaType::Double) {
                          return leftData.toDouble()  < rightData.toDouble();
                      } else if (leftData.userType() == QMetaType::Bool) {
                          return leftData.toBool() < rightData.toBool();
                      }
                  
                      return QSortFilterProxyModel::lessThan(sourceLeft, sourceRight);
                  }
                  

                  Here's a sort method of the source model:

                  void ForecastingModel::sort(int column, Qt::SortOrder order)
                  {
                      switch(column) {
                      case Name:
                          if (order == Qt::AscendingOrder) {
                              std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                        [](const Forecast &a, const Forecast &b) { return a.name < b.name; });
                          } else {
                              std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                        [](const Forecast &a, const Forecast &b) { return a.name > b.name; });
                          }
                          break;
                  
                      case Price:
                          if (order == Qt::AscendingOrder) {
                              std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                        [](const Forecast &a, const Forecast &b) { return a.price < b.price; });
                          } else {
                              std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                        [](const Forecast &a, const Forecast &b) { return a.price > b.price; });
                          }
                          break;
                      }
                  }
                  

                  Is it enough or something more is required?

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by JonB
                  #9

                  @CuriousPan
                  Well, you certainly do not want to have called sourceModel()->sort() inside ForecastingProxyModel::lessThan(), so that's an improvement!

                  You don't call (or show you have called) your ForecastingModel::sort(), so I think that is irrelevant.

                  Then your view is attached to your QSortFilterProxyModel. I would start by debugging/printing from within lessThan() to see which column is in the QModelIndexes to compare so you know what it is sorting by, and maybe check the values. I don't think your "Printout of the situation inside the datastructure:" is relevant because you are accessing via the QSortFilterProxyModel, so the ordering comes from there.

                  While I think of it: I don't think your lessThan() handles any QVariant types not already catered for in https://doc.qt.io/qt-5/qsortfilterproxymodel.html#lessThan (except maybe Bool, but that probably degenerates correctly anyway, or isn't relevant to your data), so don't think your lessThan() does anything which is actually needed?

                  C 1 Reply Last reply
                  0
                  • JonBJ JonB

                    @CuriousPan
                    Well, you certainly do not want to have called sourceModel()->sort() inside ForecastingProxyModel::lessThan(), so that's an improvement!

                    You don't call (or show you have called) your ForecastingModel::sort(), so I think that is irrelevant.

                    Then your view is attached to your QSortFilterProxyModel. I would start by debugging/printing from within lessThan() to see which column is in the QModelIndexes to compare so you know what it is sorting by, and maybe check the values. I don't think your "Printout of the situation inside the datastructure:" is relevant because you are accessing via the QSortFilterProxyModel, so the ordering comes from there.

                    While I think of it: I don't think your lessThan() handles any QVariant types not already catered for in https://doc.qt.io/qt-5/qsortfilterproxymodel.html#lessThan (except maybe Bool, but that probably degenerates correctly anyway, or isn't relevant to your data), so don't think your lessThan() does anything which is actually needed?

                    C Offline
                    C Offline
                    CuriousPan
                    wrote on last edited by CuriousPan
                    #10

                    @JonB,
                    After suggested debugging I found out following things:

                    1. lessThan method considers Date as String, which I find quite suprising. I can fix it by checking the data behind QModelIndex using not QMetaType, but rather column number.
                    2. lessThan doesn't recognize bool type too.
                    3. lessThan considers columns correctly.

                    While I think of it: I don't think your lessThan() handles any QVariant types not already catered for in https://doc.qt.io/qt-5/qsortfilterproxymodel.html#lessThan (except maybe Bool, but that probably degenerates correctly anyway, or isn't relevant to your data), so don't think your lessThan() does anything which is actually needed?

                    What do you exactly mean by this?

                    You don't call (or show you have called) your ForecastingModel::sort(), so I think that is irrelevant.

                    Where am I supposed to call it? Isn't it a slot wihich is called autimatically?

                    JonBJ 1 Reply Last reply
                    0
                    • C CuriousPan

                      Okay, here's the code:

                      //m_model is a ForecastingProxyModel class inheriting from QAbstractTableModel
                      ForecastingProxyModel *proxyModel = new ForecastingProxyModel(this);
                      proxyModel->setSourceModel(m_model);
                      ui->forecastingTableView->setModel(proxyModel);
                      ui->forecastingTableView->setSortingEnabled(true);
                      

                      Here's the code of the lessThan of the QSortFilterProxyModel:

                      bool ForecastingProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
                      {
                          const QVariant &leftData = sourceModel()->data(sourceLeft);
                          const QVariant &rightData = sourceModel()->data(sourceRight);
                          //sourceModel()->sort(sourceLeft.column(), sortOrder());  you said it's a bad idea to call it hear.
                      
                          if (leftData.userType() == QMetaType::QDate) {
                              return leftData.toDate() < rightData.toDate();
                          } else if (leftData.userType() == QMetaType::QString) {
                              return leftData.toString() < rightData.toString();
                          } else if (leftData.userType() == QMetaType::Double) {
                              return leftData.toDouble()  < rightData.toDouble();
                          } else if (leftData.userType() == QMetaType::Bool) {
                              return leftData.toBool() < rightData.toBool();
                          }
                      
                          return QSortFilterProxyModel::lessThan(sourceLeft, sourceRight);
                      }
                      

                      Here's a sort method of the source model:

                      void ForecastingModel::sort(int column, Qt::SortOrder order)
                      {
                          switch(column) {
                          case Name:
                              if (order == Qt::AscendingOrder) {
                                  std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                            [](const Forecast &a, const Forecast &b) { return a.name < b.name; });
                              } else {
                                  std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                            [](const Forecast &a, const Forecast &b) { return a.name > b.name; });
                              }
                              break;
                      
                          case Price:
                              if (order == Qt::AscendingOrder) {
                                  std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                            [](const Forecast &a, const Forecast &b) { return a.price < b.price; });
                              } else {
                                  std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(),
                                            [](const Forecast &a, const Forecast &b) { return a.price > b.price; });
                              }
                              break;
                          }
                      }
                      

                      Is it enough or something more is required?

                      Christian EhrlicherC Online
                      Christian EhrlicherC Online
                      Christian Ehrlicher
                      Lifetime Qt Champion
                      wrote on last edited by
                      #11

                      @CuriousPan said in How to sort original data structure of QAbstractTableModel?:

                      bool ForecastingProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
                      {
                      const QVariant &leftData = sourceModel()->data(sourceLeft);
                      const QVariant &rightData = sourceModel()->data(sourceRight);
                      //sourceModel()->sort(sourceLeft.column(), sortOrder()); you said it's a bad idea to call it hear.

                      if (leftData.userType() == QMetaType::QDate) {
                          return leftData.toDate() < rightData.toDate();
                      } else if (leftData.userType() == QMetaType::QString) {
                          return leftData.toString() < rightData.toString();
                      } else if (leftData.userType() == QMetaType::Double) {
                          return leftData.toDouble()  < rightData.toDouble();
                      } else if (leftData.userType() == QMetaType::Bool) {
                          return leftData.toBool() < rightData.toBool();
                      }
                      
                      return QSortFilterProxyModel::lessThan(sourceLeft, sourceRight);
                      

                      This is not needed. QSortFilterProxyModel does exact the same (and honors more meta types than your implementation)

                      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                      Visit the Qt Academy at https://academy.qt.io/catalog

                      C 1 Reply Last reply
                      1
                      • Christian EhrlicherC Christian Ehrlicher

                        @CuriousPan said in How to sort original data structure of QAbstractTableModel?:

                        bool ForecastingProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
                        {
                        const QVariant &leftData = sourceModel()->data(sourceLeft);
                        const QVariant &rightData = sourceModel()->data(sourceRight);
                        //sourceModel()->sort(sourceLeft.column(), sortOrder()); you said it's a bad idea to call it hear.

                        if (leftData.userType() == QMetaType::QDate) {
                            return leftData.toDate() < rightData.toDate();
                        } else if (leftData.userType() == QMetaType::QString) {
                            return leftData.toString() < rightData.toString();
                        } else if (leftData.userType() == QMetaType::Double) {
                            return leftData.toDouble()  < rightData.toDouble();
                        } else if (leftData.userType() == QMetaType::Bool) {
                            return leftData.toBool() < rightData.toBool();
                        }
                        
                        return QSortFilterProxyModel::lessThan(sourceLeft, sourceRight);
                        

                        This is not needed. QSortFilterProxyModel does exact the same (and honors more meta types than your implementation)

                        C Offline
                        C Offline
                        CuriousPan
                        wrote on last edited by
                        #12

                        @Christian-Ehrlicher, do you suggest totally removing the method?

                        1 Reply Last reply
                        0
                        • C CuriousPan

                          @JonB,
                          After suggested debugging I found out following things:

                          1. lessThan method considers Date as String, which I find quite suprising. I can fix it by checking the data behind QModelIndex using not QMetaType, but rather column number.
                          2. lessThan doesn't recognize bool type too.
                          3. lessThan considers columns correctly.

                          While I think of it: I don't think your lessThan() handles any QVariant types not already catered for in https://doc.qt.io/qt-5/qsortfilterproxymodel.html#lessThan (except maybe Bool, but that probably degenerates correctly anyway, or isn't relevant to your data), so don't think your lessThan() does anything which is actually needed?

                          What do you exactly mean by this?

                          You don't call (or show you have called) your ForecastingModel::sort(), so I think that is irrelevant.

                          Where am I supposed to call it? Isn't it a slot wihich is called autimatically?

                          JonBJ Offline
                          JonBJ Offline
                          JonB
                          wrote on last edited by JonB
                          #13

                          @CuriousPan
                          If you followed my link it lists:

                          This function is used as the < operator when sorting, and handles the following QVariant types:

                          So you see how it says which QVariant types it handles and that includes the ones you handle. It also includes QMetaType::QDate so I don't know why your "lessThan method considers Date as String, which I find quite suprising". I don't believe you need your own override.

                          EDIT I can see @Christian-Ehrlicher agrees.

                          Put qDebug() statements inside your sort() to see whether it's called. Your view is attached to the QSortFilterProxyModel rather than the underlying model. I'm not sure, but find out which models' sorting are getting used/called. I don't think QSortFilterProxyModel::sort() will call sourceModel->sort(). I think it's confusing to have sorting on your source model and also to be using QSortFilterProxyModel against it.

                          Where am I supposed to call it?

                          I would normally expect you to sort a (source) model immediately after you populate it if you want it sorted. So wherever that is in your code.

                          1 Reply Last reply
                          0
                          • C Offline
                            C Offline
                            CuriousPan
                            wrote on last edited by CuriousPan
                            #14

                            After removing lessThan method I have following behavior:

                            1. QSortFilterProxyModel::sort() is getting called, but not the source one, hence I have to call source::sort() inside it. It sorts source data, but view isn't changing.
                            JonBJ 1 Reply Last reply
                            0
                            • C CuriousPan

                              After removing lessThan method I have following behavior:

                              1. QSortFilterProxyModel::sort() is getting called, but not the source one, hence I have to call source::sort() inside it. It sorts source data, but view isn't changing.
                              JonBJ Offline
                              JonBJ Offline
                              JonB
                              wrote on last edited by
                              #15

                              @CuriousPan
                              I have said before: I do not know why you are using a QSortFilterProxyModel and also doing sourceModel()->sort(). If you want to sort the underlying model I don't know why you want the proxy too. But there you are.

                              Meanwhile, since again you do not show your code for "hence I have to call source::sort() inside it", we do not know what you are doing. Are you/do you need to take QSortFilterProxyModel::sortColumn() & QSortFilterProxyModel::sortOrder() into account?

                              It sorts source data, but view isn't changing.

                              I have said before: the view is bound to the QSortFilterProxyModel, not the source model directly, so I don't know that any change you might or might not make in the source model's rows' order would be reflected in either the QSortFilterProxyModel or any view bound to it.

                              I would get it working with either the QSortFilterProxyModel and no source model sort() or source model sort() and no QSortFilterProxyModel --- and possibly compare the two --- before I tried to understand what was happening with both in play.

                              That's all I know to say.

                              1 Reply Last reply
                              1
                              • C Offline
                                C Offline
                                CuriousPan
                                wrote on last edited by
                                #16

                                After searching the internet I guess I found a solution. After sorting the data it's important to emit dataChanged.
                                Thanks everyone who tried to help me.

                                JonBJ 1 Reply Last reply
                                1
                                • C CuriousPan

                                  After searching the internet I guess I found a solution. After sorting the data it's important to emit dataChanged.
                                  Thanks everyone who tried to help me.

                                  JonBJ Offline
                                  JonBJ Offline
                                  JonB
                                  wrote on last edited by
                                  #17

                                  @CuriousPan
                                  That is a good point, which I should have thought about! :)

                                  I still don't know why you want both to sort the model and to have a sort proxy on top of it. You sure you want the QSortFilterProxyModel at all, given your desire to sort the underlying model? Best of luck.

                                  1 Reply Last reply
                                  1
                                  • Christian EhrlicherC Online
                                    Christian EhrlicherC Online
                                    Christian Ehrlicher
                                    Lifetime Qt Champion
                                    wrote on last edited by
                                    #18

                                    @JonB said in How to sort original data structure of QAbstractTableModel?:

                                    I still don't know why you want both to sort the model and to have a sort proxy on top of it.

                                    Because he does not understand what a QSortFilterProxyModel is doing...

                                    Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                                    Visit the Qt Academy at https://academy.qt.io/catalog

                                    C 1 Reply Last reply
                                    0
                                    • Christian EhrlicherC Christian Ehrlicher

                                      @JonB said in How to sort original data structure of QAbstractTableModel?:

                                      I still don't know why you want both to sort the model and to have a sort proxy on top of it.

                                      Because he does not understand what a QSortFilterProxyModel is doing...

                                      C Offline
                                      C Offline
                                      CuriousPan
                                      wrote on last edited by
                                      #19

                                      @Christian-Ehrlicher, yeah, you are right. I was given the example with QSortFilterProxyModel so I thought it's mandatory to use it. I've removed it.

                                      1 Reply Last reply
                                      1
                                      • Christian EhrlicherC Online
                                        Christian EhrlicherC Online
                                        Christian Ehrlicher
                                        Lifetime Qt Champion
                                        wrote on last edited by Christian Ehrlicher
                                        #20

                                        Sorting the base model is normally much more expansive than using a QSortFilterProxyModel so it should be avoided.

                                        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                                        Visit the Qt Academy at https://academy.qt.io/catalog

                                        1 Reply Last reply
                                        3

                                        • Login

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