Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. QAbstractTableModel, QTableView : how to sort ?
Forum Updated to NodeBB v4.3 + New Features

QAbstractTableModel, QTableView : how to sort ?

Scheduled Pinned Locked Moved Unsolved Qt for Python
qt for python
9 Posts 3 Posters 8.5k Views 1 Watching
  • 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.
  • A Offline
    A Offline
    anto1ne
    wrote on last edited by anto1ne
    #1

    Hi,

    I am looking for help about sorting data in a QTableView.
    I use a QAbstractTableModel model to set data in the table and I set "setSortingEnabled" as true but it does not work.

    Here is an exemple to describe this situation.

    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    
    headers = ["Scientist name", "Birthdate", "Contribution"]
    rows =    [("Newton", "1643-01-04", "Classical mechanics"),
               ("Einstein", "1879-03-14", "Relativity"),
               ("Darwin", "1809-02-12", "Evolution")]
    
    class TableModel(QAbstractTableModel):
        def get_items(self):
            return rows
        def rowCount(self, parent):
            # How many rows are there?
            return len(rows)
        def columnCount(self, parent):
            # How many columns?
            return len(headers)
        def data(self, index, role):
            if role != Qt.DisplayRole:
                return QVariant()
            # What's the value of the cell at the given index?
            return rows[index.row()][index.column()]
        def headerData(self, section, orientation, role):
            if role != Qt.DisplayRole or orientation != Qt.Horizontal:
                return QVariant()
            # What's the header for the given column?
            return headers[section]
    
    app = QApplication([])
    model = TableModel()
    view = QTableView()
    view.setModel(model)
    view.setSortingEnabled(True)
    
    def get_obj():
        try:
            if view.currentIndex().row() != -1:
                return view.model().get_items()[view.currentIndex().row()]
            return None
        except:
            return None
    def print_row():
        print(get_obj())
        
    view.doubleClicked.connect(print_row)
    
    view.show()
    app.exec_()
    

    Do you have an idea of what is wrong ?
    I would like to continue working with QAbstractTableModel and QTableView if possible.

    Thank you

    JonBJ 1 Reply Last reply
    0
    • A anto1ne

      Hi,

      I am looking for help about sorting data in a QTableView.
      I use a QAbstractTableModel model to set data in the table and I set "setSortingEnabled" as true but it does not work.

      Here is an exemple to describe this situation.

      from PyQt5.QtWidgets import *
      from PyQt5.QtCore import *
      
      headers = ["Scientist name", "Birthdate", "Contribution"]
      rows =    [("Newton", "1643-01-04", "Classical mechanics"),
                 ("Einstein", "1879-03-14", "Relativity"),
                 ("Darwin", "1809-02-12", "Evolution")]
      
      class TableModel(QAbstractTableModel):
          def get_items(self):
              return rows
          def rowCount(self, parent):
              # How many rows are there?
              return len(rows)
          def columnCount(self, parent):
              # How many columns?
              return len(headers)
          def data(self, index, role):
              if role != Qt.DisplayRole:
                  return QVariant()
              # What's the value of the cell at the given index?
              return rows[index.row()][index.column()]
          def headerData(self, section, orientation, role):
              if role != Qt.DisplayRole or orientation != Qt.Horizontal:
                  return QVariant()
              # What's the header for the given column?
              return headers[section]
      
      app = QApplication([])
      model = TableModel()
      view = QTableView()
      view.setModel(model)
      view.setSortingEnabled(True)
      
      def get_obj():
          try:
              if view.currentIndex().row() != -1:
                  return view.model().get_items()[view.currentIndex().row()]
              return None
          except:
              return None
      def print_row():
          print(get_obj())
          
      view.doubleClicked.connect(print_row)
      
      view.show()
      app.exec_()
      

      Do you have an idea of what is wrong ?
      I would like to continue working with QAbstractTableModel and QTableView if possible.

      Thank you

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

      @anto1ne said in QAbstractTableModel, QTableView : how to sort ?:
      QTableView.setSortingEnabled() enables it as far as the view is concerned --- you get the sort-click-indicators on the column headings --- but it doesn't do the model sorting for you!

      In a word, by far the best way is to interpose a QSortFilterProxyModel between your TableModel and the QTableView. Not only do you get sorting, but you optionally get filtering for free too, if you want to use it :)

      That page even shows you the example:

              QTreeView *treeView = new QTreeView;
              MyItemModel *sourceModel = new MyItemModel(this);
              QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
      
              proxyModel->setSourceModel(sourceModel);
              treeView->setModel(proxyModel);
      

      Easy :)

      Oh, here it for PySide2/Python: https://doc.qt.io/qtforpython/PySide2/QtCore/QSortFilterProxyModel.html

      treeView =  QTreeView()
      sourceModel =  MyItemModel(self)
      proxyModel =  QSortFilterProxyModel(self)
      
      proxyModel.setSourceModel(sourceModel)
      treeView.setModel(proxyModel)
      
      1 Reply Last reply
      1
      • A Offline
        A Offline
        anto1ne
        wrote on last edited by
        #3

        thank you @JonB

        I added the QSortFilterProxyModel between the TableModel and the QTableView and now sorting is working well.

        Nevertheless, the get_obj function is no longer working.
        I don't know how to get the "object" double clicked
        After sorting, items index and tableview index are not the same. I hope you understand what I mean.

        from PyQt5.QtWidgets import *
        from PyQt5.QtCore import *
        
        headers = ["Scientist name", "Birthdate", "Contribution"]
        rows =    [("Newton", "1643-01-04", "Classical mechanics"),
                   ("Einstein", "1879-03-14", "Relativity"),
                   ("Darwin", "1809-02-12", "Evolution")]
        
        class TableModel(QAbstractTableModel):
            def get_items(self):
                return rows
            def rowCount(self, parent):
                # How many rows are there?
                return len(rows)
            def columnCount(self, parent):
                # How many columns?
                return len(headers)
            def data(self, index, role):
                if role != Qt.DisplayRole:
                    return QVariant()
                # What's the value of the cell at the given index?
                return rows[index.row()][index.column()]
            def headerData(self, section, orientation, role):
                if role != Qt.DisplayRole or orientation != Qt.Horizontal:
                    return QVariant()
                # What's the header for the given column?
                return headers[section]
        
        app = QApplication([])
        model = TableModel()
        
        proxymodel = QSortFilterProxyModel()
        proxymodel.setSourceModel(model)
        
        view = QTableView()
        view.setModel(proxymodel)
        view.setSortingEnabled(True)
        
        def get_obj():
            try:
                print(view.currentIndex().row())
                if view.currentIndex().row() != -1:
                    #return view.model().get_items()[view.currentIndex().row()]
                    #return rows[view.currentIndex().row()]
                    return proxymodel.sourceModel().get_items()[view.currentIndex().row()]
                return None
            except:
                return None
        def print_row():
            print(get_obj())
            
        view.doubleClicked.connect(print_row)
        
        view.show()
        app.exec_()
        
        JonBJ 1 Reply Last reply
        0
        • A anto1ne

          thank you @JonB

          I added the QSortFilterProxyModel between the TableModel and the QTableView and now sorting is working well.

          Nevertheless, the get_obj function is no longer working.
          I don't know how to get the "object" double clicked
          After sorting, items index and tableview index are not the same. I hope you understand what I mean.

          from PyQt5.QtWidgets import *
          from PyQt5.QtCore import *
          
          headers = ["Scientist name", "Birthdate", "Contribution"]
          rows =    [("Newton", "1643-01-04", "Classical mechanics"),
                     ("Einstein", "1879-03-14", "Relativity"),
                     ("Darwin", "1809-02-12", "Evolution")]
          
          class TableModel(QAbstractTableModel):
              def get_items(self):
                  return rows
              def rowCount(self, parent):
                  # How many rows are there?
                  return len(rows)
              def columnCount(self, parent):
                  # How many columns?
                  return len(headers)
              def data(self, index, role):
                  if role != Qt.DisplayRole:
                      return QVariant()
                  # What's the value of the cell at the given index?
                  return rows[index.row()][index.column()]
              def headerData(self, section, orientation, role):
                  if role != Qt.DisplayRole or orientation != Qt.Horizontal:
                      return QVariant()
                  # What's the header for the given column?
                  return headers[section]
          
          app = QApplication([])
          model = TableModel()
          
          proxymodel = QSortFilterProxyModel()
          proxymodel.setSourceModel(model)
          
          view = QTableView()
          view.setModel(proxymodel)
          view.setSortingEnabled(True)
          
          def get_obj():
              try:
                  print(view.currentIndex().row())
                  if view.currentIndex().row() != -1:
                      #return view.model().get_items()[view.currentIndex().row()]
                      #return rows[view.currentIndex().row()]
                      return proxymodel.sourceModel().get_items()[view.currentIndex().row()]
                  return None
              except:
                  return None
          def print_row():
              print(get_obj())
              
          view.doubleClicked.connect(print_row)
          
          view.show()
          app.exec_()
          
          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by JonB
          #4

          @anto1ne
          Once you start using any QAbstractProxyModel you may have to map between indexes into the proxy model and indexes into the source model. You cannot use one where the other is required! QTableView->QSortFilterProxyModel<->QAbstractTableModel. Depending on which you have and which one you want. Look at these 4 methods:

          def mapFromSource (sourceIndex)
          def mapSelectionFromSource (selection)
          def mapSelectionToSource (selection)
          def mapToSource (proxyIndex)
          

          on this PySide2 page: https://doc.qt.io/qtforpython/PySide2/QtCore/QAbstractProxyModel.html

          1 Reply Last reply
          0
          • SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #5

            Hi,

            Why are you using get_items at all ?
            You should request the data from your model rather that working around it. With index.data you can directly get it.

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            A 1 Reply Last reply
            1
            • SGaistS SGaist

              Hi,

              Why are you using get_items at all ?
              You should request the data from your model rather that working around it. With index.data you can directly get it.

              A Offline
              A Offline
              anto1ne
              wrote on last edited by
              #6

              @SGaist
              I use "get_items" because I want to return the tuple from the row I clicked, not the data in the cell

              1 Reply Last reply
              0
              • SGaistS Offline
                SGaistS Offline
                SGaist
                Lifetime Qt Champion
                wrote on last edited by
                #7

                Then use a custom role to retrieve the tuple from the model.

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  anto1ne
                  wrote on last edited by
                  #8

                  Hi @SGaist ,

                  I am not familiar with role.
                  I used the Qt.UserRole to return the tuple and it works, is it the right thing to do ?

                  from PyQt5.QtWidgets import *
                  from PyQt5.QtCore import *
                  
                  headers = ["Scientist name", "Birthdate", "Contribution"]
                  rows =    [("Newton", "1643-01-04", "Classical mechanics"),
                             ("Einstein", "1879-03-14", "Relativity"),
                             ("Darwin", "1809-02-12", "Evolution")]
                  
                  class TableModel(QAbstractTableModel):
                  #    def get_items(self):
                  #        return rows
                      def rowCount(self, parent):
                          # How many rows are there?
                          return len(rows)
                      def columnCount(self, parent):
                          # How many columns?
                          return len(headers)
                      def data(self, index, role):
                          if role == Qt.UserRole:
                              return rows[index.row()]
                          if role != Qt.DisplayRole:
                              return QVariant()
                          # What's the value of the cell at the given index?
                          return rows[index.row()][index.column()]
                      def headerData(self, section, orientation, role):
                          if role != Qt.DisplayRole or orientation != Qt.Horizontal:
                              return QVariant()
                          # What's the header for the given column?
                          return headers[section]
                  
                  app = QApplication([])
                  model = TableModel()
                  
                  proxymodel = QSortFilterProxyModel()
                  proxymodel.setSourceModel(model)
                  #proxymodel.mapFromSource(view.model())
                  
                  view = QTableView()
                  view.setModel(proxymodel)
                  view.setSortingEnabled(True)
                  view.sortByColumn(0,Qt.AscendingOrder)
                  
                  def get_obj():
                      try:
                          print(view.currentIndex().row())
                          if view.currentIndex().row() != -1:
                              #return view.model().get_items()[view.currentIndex().row()]
                              #return rows[view.currentIndex().row()]
                              #return proxymodel.sourceModel().get_items()[view.currentIndex().row()]
                              return view.currentIndex().data(Qt.UserRole)
                          return None
                      except:
                          return None
                  def print_row():
                      print(get_obj())
                      
                  view.doubleClicked.connect(print_row)
                  
                  view.show()
                  app.exec_()
                  
                  1 Reply Last reply
                  0
                  • SGaistS Offline
                    SGaistS Offline
                    SGaist
                    Lifetime Qt Champion
                    wrote on last edited by
                    #9

                    Yes you can, that's the starting value of the custom roles. What is usually done is to use a custom enumeration start at that value so you have better names when requesting custom data.

                    Interested in AI ? www.idiap.ch
                    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                    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