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. How to reimplement LessThan() method in QSortFIlterProxyModel subclass for Sorting QTableView

How to reimplement LessThan() method in QSortFIlterProxyModel subclass for Sorting QTableView

Scheduled Pinned Locked Moved Solved Qt for Python
10 Posts 2 Posters 2.4k 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.
  • H Offline
    H Offline
    hachbani
    wrote on last edited by hachbani
    #1

    Hello,

    I Have a QTableView, this is my (basic) architecture of my models setup

    class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    
    	def __init__(self, parent=None):
    
    		ProxyModel = ProxyModel() 
    		TableModel = TableModel()
    		ProxyModel.setSourceModel(TableModel)
    
    		self.MyTableView.setModel(ProxyModel)
    

    I've achieved the filtering with success by reimplementing FilterAcceptsrow() in ProxyModel class (QSortFilterProxyModel Subsclass). I want to reimplement lessThan() method in ProxyModel to achieve sorting. However, even though been documenting on it, I still can't figure out how to do it.

    Can someone explain to me the basis of how to reimplement this method so I can sort my QTableView on the 4th column for example.
    Thanks.

    Here's the ProxModel and TableModel implementation:

    class ProxyModel(QtCore.QSortFilterProxyModel):
    	def __init__(self,parent=None):
    		super(ProxyModel, self).__init__()
    		self._filter = "Aucun"
    
    	def setFilterColumn(self, header):
    		if header == "Aucun": 
    			self.Filtre("Aucun")
    			return True
    		for col in range(self.sourceModel().columnCount()):
    			if self.sourceModel().headerData(col) == header:
    				self.setFilterKeyColumn(col)
    				return True
    		return False
    
    	def Filtre(self, valeur):
    		self._filter = str(valeur)
    		self.invalidateFilter()
    	
    	def filterAcceptsRow(self, sourceRow, sourceParent):
    		if self._filter == "Aucun": return True
    
    		sourceModel = self.sourceModel()
    		id = sourceModel.index(sourceRow, self.filterKeyColumn(), sourceParent)
    		if sourceModel.data(id) == self._filter:
    			return True
    		return False
    
    class TableModel(QtCore.QAbstractTableModel):
    
    	def __init__(self, mlist=None):
    		super(TableModel, self).__init__()
    		self._items = [] if mlist == None else mlist
    		self._header = []
    	def rowCount(self, parent = QtCore.QModelIndex):
    		return len(self._items)
    
    	def columnCount(self, parent = QtCore.QModelIndex):
    		return len(self._header)
    
    	def data(self, index, role = QtCore.Qt.DisplayRole):
    		if not index.isValid():
    		   return None
    		if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
    			return self._items[index.row()][index.column()]
    		return None
    	
    	def setData(self, index, value, role = QtCore.Qt.EditRole):
    		if value is not None and role == QtCore.Qt.EditRole:
    			self._items[index.row()-1][index.column()] = value
    			self.dataChanged.emit(index, index)
    			return True
    		return False
    
    	def addRow(self, rowObject, row=False):
    		if not row:
    			row = self.rowCount()
    		self.beginInsertRows(QtCore.QModelIndex(), row, row)
    		self._items.append(rowObject)
    		self.endInsertRows()
    		self.layoutChanged.emit()
    
    
    1 Reply Last reply
    0
    • JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by JonB
      #2
      def lessThan(source_left: QModelIndex, source_right: QModelIndex) -> bool:
          return source_left.data() < source_right.data()
      

      But that's the default implementation anyway if you don't override it. See https://doc-snapshots.qt.io/qtforpython-dev/PySide2/QtCore/QSortFilterProxyModel.html#PySide2.QtCore.PySide2.QtCore.QSortFilterProxyModel.lessThan for what it can handle. You only need to override if you have a more complex data object or criteria for sorting. Since you say nothing about what your "4th column" is I cannot say further.

      You might write something along the lines of:

      def lessThan(source_left: QModelIndex, source_right: QModelIndex) -> bool:
          if source_left.column() == 3:
              return special_comparison(source_left, source_right)
              # or perhaps
              val_left = source_left.data(some_role_returning_native_data_object)
              val_right = source_right.data(some_role_returning_native_data_object)
              return val_left.lessThan(val_right)
          return source_left.data() < source_right.data()
          # or
          return QSortFIlterProxyModel.lessThan(source_left, source_right)
      
      1 Reply Last reply
      2
      • H Offline
        H Offline
        hachbani
        wrote on last edited by
        #3

        Thanks for your answer. Well, What I really want to achieve is sorting on 3 columns, my column 1 hold boolean values, col 2 holds string values and third one holds Integer values. You can see the example in the image attached to get more understanding.

        Capture.PNG

        The aim of my post is to get an understanding about the lessThan() function, (the left and right inputs, what does those mean ? I get that those are indexes, but of what ?)

        If I understand how the sorting according to 1 column works, maybe I can implement the sorting I want.

        JonBJ 1 Reply Last reply
        0
        • H hachbani

          Thanks for your answer. Well, What I really want to achieve is sorting on 3 columns, my column 1 hold boolean values, col 2 holds string values and third one holds Integer values. You can see the example in the image attached to get more understanding.

          Capture.PNG

          The aim of my post is to get an understanding about the lessThan() function, (the left and right inputs, what does those mean ? I get that those are indexes, but of what ?)

          If I understand how the sorting according to 1 column works, maybe I can implement the sorting I want.

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

          @hachbani
          Since the data you show looks perfectly sortable --- a boolean, a string, an integer columns --- I would not epect you to need to override lessThan at all here. Why do you think you need to? You just ought set up sorting by columns on the QTableView (QTableView::setSortingEnabled(true)) and off you go?

          As for the parameters to lessThan(source_left, source_right), if you do choose to implement it. Don't worry about the names, left/right. This function will be called by Qt repeatedly. Each time these parameters are just QModelIndexes into your data. The rows will differ, but the columns will always be the same, your sort column index. Your only job is to return a bool according as you want the data at the first index to be treated as "less than", for the purpose of sorting, the data at the second one.

          1 Reply Last reply
          0
          • H Offline
            H Offline
            hachbani
            wrote on last edited by
            #5

            After adding all the data I want to display to my QtableView, I want to display them in a specific order, hence the urge to wanting to implement a lessThan() method, so I can do it programmatically.

            To my understanding, If I set up the sorting in my TableView (setSortingEnabled(true)), This will give the option to sort the columns by clicking on the header, which is still something that I'd like to have in my app, but it doesn't solve my problem of displaying the data in the order I want.

            I might be wrong, as I'm just trying to learn about sorting in Qt, what do you think about my understanding of this ?

            JonBJ 1 Reply Last reply
            0
            • H hachbani

              After adding all the data I want to display to my QtableView, I want to display them in a specific order, hence the urge to wanting to implement a lessThan() method, so I can do it programmatically.

              To my understanding, If I set up the sorting in my TableView (setSortingEnabled(true)), This will give the option to sort the columns by clicking on the header, which is still something that I'd like to have in my app, but it doesn't solve my problem of displaying the data in the order I want.

              I might be wrong, as I'm just trying to learn about sorting in Qt, what do you think about my understanding of this ?

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

              @hachbani
              OK, then you don't want to enable column click sorting (for now). So just call QSortFilterProxyModel::sort(int column, Qt::SortOrder order = Qt::AscendingOrder) on the 4th column. And implement lessThan() the way I showed above, for your "specific order" that you want on the 4th column.

              I don't know what else there is to say, just do it.

              1 Reply Last reply
              1
              • H Offline
                H Offline
                hachbani
                wrote on last edited by
                #7

                Hey ! I've done that, It was calling the QSortFilterProxyModel::sort(int column, Qt::SortOrder order = Qt::AscendingOrder) That I was missing to get an understanding of how this works ! I've tried to sort on a single column and It worked. Thanks !!

                I've read somewhere that in order to get multiple columns sorting, you need to call the sorting method on every column.

                self.MyTableView.model().sort(2, QtCore.Qt.AscendingOrder)
                self.MyTableView.model().sort(3, QtCore.Qt.AscendingOrder)
                

                I've tried that, and, as expected, the sorting on the second column is no more on.

                I'll look through how to achieve multiple column sorting.

                Thanks again @JonB !

                JonBJ 1 Reply Last reply
                0
                • H Offline
                  H Offline
                  hachbani
                  wrote on last edited by
                  #8

                  @JonB

                  Do you know if there's a way to call only sort method on only a slice of the table: instead of sorting all the table, I want to only apply the sorting on the rows 1 --> 20 for example. Is that possible ?

                  1 Reply Last reply
                  0
                  • H hachbani

                    Hey ! I've done that, It was calling the QSortFilterProxyModel::sort(int column, Qt::SortOrder order = Qt::AscendingOrder) That I was missing to get an understanding of how this works ! I've tried to sort on a single column and It worked. Thanks !!

                    I've read somewhere that in order to get multiple columns sorting, you need to call the sorting method on every column.

                    self.MyTableView.model().sort(2, QtCore.Qt.AscendingOrder)
                    self.MyTableView.model().sort(3, QtCore.Qt.AscendingOrder)
                    

                    I've tried that, and, as expected, the sorting on the second column is no more on.

                    I'll look through how to achieve multiple column sorting.

                    Thanks again @JonB !

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

                    @hachbani
                    You cannot sort by "secondary", multiple columns by calling sort() multiple times. The latest call simply replaces any earlier calls.

                    Here indeed is where you will need to implement lessThan() correctly and in a more complex scenario.

                    Take your apparent example of column #2 is the "primary" sort column and column #3 is the "secondary" sort column, meaning it only comes into play when two rows have the same value in column #2. Now you will need code like:

                    def lessThan(source_left: QModelIndex, source_right: QModelIndex) -> bool:
                        if source_left.column() == 2:
                            if source_left.data() < source_right.data()
                                return True;
                            elif source_left.data() > source_right.data()
                                return False;
                            else
                                return lessThan(source_left.siblingAtColumn(3), source_right.siblingAtColumn(3))
                        return QSortFIlterProxyModel.lessThan(source_left, source_right)
                    

                    To implement "I want to only apply the sorting on the rows 1 --> 20" you would have to do something like look at the row() numbers in the passed in indexes and act accordingly. This would be source (not proxy) model row numbers. And you'll have to return something when asked to compare rows > 20. Plus, don't forget, your rows in the source are also filtered to only include certain ones, so the row numbers will not be what you might expect. You may not be able to preserve the order you would not have seen without sorting. You cannot instruct the sort not to happen on certain rows. It's a bit hokey, a sort is a sort, but you can implement whatever you like.

                    1 Reply Last reply
                    1
                    • H Offline
                      H Offline
                      hachbani
                      wrote on last edited by
                      #10

                      Thanks @JonB for your thoughtful answer. It gave me more insight on the logic of lessThan().
                      I'll give this a big try and see where I'll get.

                      Wish you a good day !

                      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