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. Custom sortable columns in QFileSystemModel
Qt 6.11 is out! See what's new in the release blog

Custom sortable columns in QFileSystemModel

Scheduled Pinned Locked Moved Unsolved General and Desktop
23 Posts 3 Posters 13.1k 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.
  • JonBJ JonB

    @Jo-Jo
    Well, I could rewrite your lessThan() to use fewer lines and smaller expressions, becaue I am like that :) But it's not a big deal.

    You started out with a lessThan which had special code for data comparison in a column. Now you seem to have an implementation which does not look at column data type and just does string comparisons on everything. Is the sufficient? Do you need a more complex "merger" of various approaches depending on data type? Depends what you want.

    J Offline
    J Offline
    Jo Jo
    wrote on last edited by
    #11

    @JonB I'm thinking about how to correctly implement comparison taking into account types but have some problems. Currently my code looks like this but not workiing properly. Maybe you can suggest solution?

    class CustomSortFilterProxyModel : public QSortFilterProxyModel
    {
        QList<int> m_dtCols{3,4};
    public:
        explicit CustomSortFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {}
    
    protected:
        bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
        {
            auto model = (QFileSystemModel*)sourceModel();
            bool isLeftDir = model->isDir(left);
            bool isRightDir = model->isDir(right);
    
            if ((isLeftDir && isRightDir) || (!isLeftDir && !isRightDir))
            {
                auto leftName  = model->data(left,  Qt::DisplayRole);
                auto rightName = model->data(right, Qt::DisplayRole);
                const int compare = m_dtCols.contains(sortColumn()) ?
                    leftName.toDateTime() < rightName.toDateTime() :
                    QString::localeAwareCompare(leftName.toString(), rightName.toString());
    
                if(compare != 0) {
                    return compare < 0;
                }
            }
            else if (isLeftDir)
            {
                return true;
            }
    
            return false;
        }
    };
    
    1 Reply Last reply
    0
    • JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by
      #12

      I'm not sure what "not workiing properly" means? What does not work right? Have you put a break or qDebug() statements here to see/verify what it is comparing and returning?

      J 1 Reply Last reply
      0
      • JonBJ JonB

        I'm not sure what "not workiing properly" means? What does not work right? Have you put a break or qDebug() statements here to see/verify what it is comparing and returning?

        J Offline
        J Offline
        Jo Jo
        wrote on last edited by Jo Jo
        #13

        @JonB Sorry, sorting by "Date Modified" and "Date Created" columns does not work properly (yes, i use qDebug() for tests)

        UPDATE:
        I think it should be implemented like this

        class CustomSortFilterProxyModel : public QSortFilterProxyModel
        {
            QList<int> m_dtCols{3,4};
        public:
            explicit CustomSortFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {}
        
        protected:
            bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
            {
                auto model = (QFileSystemModel*)sourceModel();
                bool isLeftDir = model->isDir(left);
                bool isRightDir = model->isDir(right);
        
                if ((isLeftDir && isRightDir) || (!isLeftDir && !isRightDir))
                {
                    auto leftName  = model->data(left,  Qt::DisplayRole);
                    auto rightName = model->data(right, Qt::DisplayRole);
        
                    if (m_dtCols.contains(sortColumn()))
                    {
                        return (leftName.toDateTime() < rightName.toDateTime());
                    }
        
                    const int compare = QString::localeAwareCompare(leftName.toString(), rightName.toString());
        
                    if(compare != 0) {
                        return compare < 0;
                    }
                }
                else if (isLeftDir)
                {
                    return true;
                }
        
                return false;
            }
        };
        

        But any improvement is appreciated

        JonBJ 1 Reply Last reply
        0
        • J Jo Jo

          @JonB Sorry, sorting by "Date Modified" and "Date Created" columns does not work properly (yes, i use qDebug() for tests)

          UPDATE:
          I think it should be implemented like this

          class CustomSortFilterProxyModel : public QSortFilterProxyModel
          {
              QList<int> m_dtCols{3,4};
          public:
              explicit CustomSortFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {}
          
          protected:
              bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
              {
                  auto model = (QFileSystemModel*)sourceModel();
                  bool isLeftDir = model->isDir(left);
                  bool isRightDir = model->isDir(right);
          
                  if ((isLeftDir && isRightDir) || (!isLeftDir && !isRightDir))
                  {
                      auto leftName  = model->data(left,  Qt::DisplayRole);
                      auto rightName = model->data(right, Qt::DisplayRole);
          
                      if (m_dtCols.contains(sortColumn()))
                      {
                          return (leftName.toDateTime() < rightName.toDateTime());
                      }
          
                      const int compare = QString::localeAwareCompare(leftName.toString(), rightName.toString());
          
                      if(compare != 0) {
                          return compare < 0;
                      }
                  }
                  else if (isLeftDir)
                  {
                      return true;
                  }
          
                  return false;
              }
          };
          

          But any improvement is appreciated

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

          @Jo-Jo
          Well, start by checking that goes through the toDateTime() path, what their values are and that is getting it right. I don't know what is wrong just by looking at it.

          Btw, check whether the QString::localeAwareCompare(0 is case insensitive or case sensitive? Windows, but not Linux, sorts case insensitively, yours might not.

          You have just updated your code: I'm not sure it is any different from what you first wrote, but if it is and works that's fine.

          J 1 Reply Last reply
          0
          • JonBJ JonB

            @Jo-Jo
            Well, start by checking that goes through the toDateTime() path, what their values are and that is getting it right. I don't know what is wrong just by looking at it.

            Btw, check whether the QString::localeAwareCompare(0 is case insensitive or case sensitive? Windows, but not Linux, sorts case insensitively, yours might not.

            You have just updated your code: I'm not sure it is any different from what you first wrote, but if it is and works that's fine.

            J Offline
            J Offline
            Jo Jo
            wrote on last edited by Jo Jo
            #15

            @JonB Everything worked except for the "Date Modified" column. It turned out that Qt returns this date as a QString. I had to convert this string to QDateTime. Now the code looks like this:

            class CustomSortFilterProxyModel : public QSortFilterProxyModel
            {
                QList<int> m_dtCols{3,4};
            public:
                explicit CustomSortFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {}
            
            protected:
                bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
                {
                    auto model = (QFileSystemModel*)sourceModel();
                    bool isLeftDir = model->isDir(left);
                    bool isRightDir = model->isDir(right);
            
                    if ((isLeftDir && isRightDir) || (!isLeftDir && !isRightDir))
                    {
                        auto leftName  = model->data(left,  Qt::DisplayRole);
                        auto rightName = model->data(right, Qt::DisplayRole);
            
                        //return (leftName.toDateTime() < rightName.toDateTime()); // OK, works fine for dates
            
                        if (m_dtCols.contains(sortColumn()))
                        {
                            if (sortColumn() == 3)
                            {
                                QDateTime dt1 = QDateTime::fromString(leftName.toString(), "M/d/yyyy h:mm AP");
                                QDateTime dt2 = QDateTime::fromString(rightName.toString(), "M/d/yyyy h:mm AP");
                                return dt1 < dt2;
                            }
            
                            return (leftName.toDateTime() < rightName.toDateTime());
                        }
            
                        const int compare = QString::localeAwareCompare(leftName.toString(), rightName.toString());
            
                        if(compare != 0) {
                            return compare < 0;
                        }
                    }
                    else if (isLeftDir)
                    {
                        return true;
                    }
            
                    return false;
                }
            };
            

            However, I'm not sure that I'm setting the date format correctly. It can change depending on the OS settings, or am I wrong?

            Christian EhrlicherC JonBJ 2 Replies Last reply
            0
            • J Jo Jo

              @JonB Everything worked except for the "Date Modified" column. It turned out that Qt returns this date as a QString. I had to convert this string to QDateTime. Now the code looks like this:

              class CustomSortFilterProxyModel : public QSortFilterProxyModel
              {
                  QList<int> m_dtCols{3,4};
              public:
                  explicit CustomSortFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {}
              
              protected:
                  bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
                  {
                      auto model = (QFileSystemModel*)sourceModel();
                      bool isLeftDir = model->isDir(left);
                      bool isRightDir = model->isDir(right);
              
                      if ((isLeftDir && isRightDir) || (!isLeftDir && !isRightDir))
                      {
                          auto leftName  = model->data(left,  Qt::DisplayRole);
                          auto rightName = model->data(right, Qt::DisplayRole);
              
                          //return (leftName.toDateTime() < rightName.toDateTime()); // OK, works fine for dates
              
                          if (m_dtCols.contains(sortColumn()))
                          {
                              if (sortColumn() == 3)
                              {
                                  QDateTime dt1 = QDateTime::fromString(leftName.toString(), "M/d/yyyy h:mm AP");
                                  QDateTime dt2 = QDateTime::fromString(rightName.toString(), "M/d/yyyy h:mm AP");
                                  return dt1 < dt2;
                              }
              
                              return (leftName.toDateTime() < rightName.toDateTime());
                          }
              
                          const int compare = QString::localeAwareCompare(leftName.toString(), rightName.toString());
              
                          if(compare != 0) {
                              return compare < 0;
                          }
                      }
                      else if (isLeftDir)
                      {
                          return true;
                      }
              
                      return false;
                  }
              };
              

              However, I'm not sure that I'm setting the date format correctly. It can change depending on the OS settings, or am I wrong?

              Christian EhrlicherC Offline
              Christian EhrlicherC Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by
              #16

              @Jo-Jo said in Custom sortable columns in QFileSystemModel:

              I had to convert this string to QDateTime

              You already converted the QVariant to a QDateTime above - so why do you use the way through QString now again?

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

              J 1 Reply Last reply
              0
              • Christian EhrlicherC Christian Ehrlicher

                @Jo-Jo said in Custom sortable columns in QFileSystemModel:

                I had to convert this string to QDateTime

                You already converted the QVariant to a QDateTime above - so why do you use the way through QString now again?

                J Offline
                J Offline
                Jo Jo
                wrote on last edited by Jo Jo
                #17

                @Christian-Ehrlicher said in Custom sortable columns in QFileSystemModel:

                You already converted the QVariant to a QDateTime above - so why do you use the way through QString now again?

                Because "Date Modified" column is created by Qt and Qt using QString for this column. If I convert QVariant to QDateTime, in this case comparison does not work properly

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

                  Then you should debug why ... e.g. with a qDebug() statement as @JonB already wrote some time ago....

                  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
                  0
                  • J Jo Jo

                    @JonB Everything worked except for the "Date Modified" column. It turned out that Qt returns this date as a QString. I had to convert this string to QDateTime. Now the code looks like this:

                    class CustomSortFilterProxyModel : public QSortFilterProxyModel
                    {
                        QList<int> m_dtCols{3,4};
                    public:
                        explicit CustomSortFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {}
                    
                    protected:
                        bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
                        {
                            auto model = (QFileSystemModel*)sourceModel();
                            bool isLeftDir = model->isDir(left);
                            bool isRightDir = model->isDir(right);
                    
                            if ((isLeftDir && isRightDir) || (!isLeftDir && !isRightDir))
                            {
                                auto leftName  = model->data(left,  Qt::DisplayRole);
                                auto rightName = model->data(right, Qt::DisplayRole);
                    
                                //return (leftName.toDateTime() < rightName.toDateTime()); // OK, works fine for dates
                    
                                if (m_dtCols.contains(sortColumn()))
                                {
                                    if (sortColumn() == 3)
                                    {
                                        QDateTime dt1 = QDateTime::fromString(leftName.toString(), "M/d/yyyy h:mm AP");
                                        QDateTime dt2 = QDateTime::fromString(rightName.toString(), "M/d/yyyy h:mm AP");
                                        return dt1 < dt2;
                                    }
                    
                                    return (leftName.toDateTime() < rightName.toDateTime());
                                }
                    
                                const int compare = QString::localeAwareCompare(leftName.toString(), rightName.toString());
                    
                                if(compare != 0) {
                                    return compare < 0;
                                }
                            }
                            else if (isLeftDir)
                            {
                                return true;
                            }
                    
                            return false;
                        }
                    };
                    

                    However, I'm not sure that I'm setting the date format correctly. It can change depending on the OS settings, or am I wrong?

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

                    @Jo-Jo said in Custom sortable columns in QFileSystemModel:

                    Everything worked except for the "Date Modified" column. It turned out that Qt returns this date as a QString.

                    I am surprised at this, but untested. You are now talking about a "Date Modified" column inbuilt into QFileSystemModel, and not the "creation date" column you added for your birthTime()? But that is QDateTime QFileSystemModel::lastModified(). So I don't understand where you are getting any string for a datetime which you need to convert.

                    J 1 Reply Last reply
                    0
                    • JonBJ JonB

                      @Jo-Jo said in Custom sortable columns in QFileSystemModel:

                      Everything worked except for the "Date Modified" column. It turned out that Qt returns this date as a QString.

                      I am surprised at this, but untested. You are now talking about a "Date Modified" column inbuilt into QFileSystemModel, and not the "creation date" column you added for your birthTime()? But that is QDateTime QFileSystemModel::lastModified(). So I don't understand where you are getting any string for a datetime which you need to convert.

                      J Offline
                      J Offline
                      Jo Jo
                      wrote on last edited by
                      #20

                      @JonB said in Custom sortable columns in QFileSystemModel:

                      You are now talking about a "Date Modified" column inbuilt into QFileSystemModel, and not the "creation date" column you added for your birthTime()?

                      Yes. This column value is QString, not QDateTime. You may check sources: https://codebrowser.dev/qt5/qtbase/src/widgets/dialogs/qfilesystemmodel.cpp.html#808

                      JonBJ 1 Reply Last reply
                      0
                      • Christian EhrlicherC Offline
                        Christian EhrlicherC Offline
                        Christian Ehrlicher
                        Lifetime Qt Champion
                        wrote on last edited by
                        #21

                        Thast's why https://doc.qt.io/qt-6/qfilesystemmodel.html#lastModified is there.

                        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
                        2
                        • J Jo Jo

                          @JonB said in Custom sortable columns in QFileSystemModel:

                          You are now talking about a "Date Modified" column inbuilt into QFileSystemModel, and not the "creation date" column you added for your birthTime()?

                          Yes. This column value is QString, not QDateTime. You may check sources: https://codebrowser.dev/qt5/qtbase/src/widgets/dialogs/qfilesystemmodel.cpp.html#808

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

                          @Jo-Jo
                          I now see that, and see from https://codebrowser.dev/qt5/qtbase/src/widgets/dialogs/qfilesystemmodel.cpp.html#737 that it is called in the implementation of QFileSystemModel::data() for column #3, which is what I needed to know.

                          I also see it is formatted as a string with QLocale::ShortFormat. I don't know what that looks like, I don't know how "precise" it is (e.g. down to, say, milliseconds or likely not?), I don't know what the locale-aware formatting does with it. Putting all these together it would not surprise me if it is unsuitable for sorting, and/or for converting back (accurately) to a datetime/comparing. Whereas the birthTime() you use for your extra column is indeed a datetime type and is suitable.

                          Hence, as both I and @Christian-Ehrlicher have said, I would use lastModified() just as you use birthDate() for this column in your own code for data() method.

                          1 Reply Last reply
                          1
                          • J Offline
                            J Offline
                            Jo Jo
                            wrote on last edited by
                            #23

                            @Christian-Ehrlicher @JonB agree with you guys about lastModified, thanks!

                            1 Reply Last reply
                            1

                            • Login

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