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. Filtering QTableview with QSortFilterProxyModel very slow
Forum Updated to NodeBB v4.3 + New Features

Filtering QTableview with QSortFilterProxyModel very slow

Scheduled Pinned Locked Moved Unsolved Qt for Python
7 Posts 2 Posters 1.2k Views 2 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.
  • J Offline
    J Offline
    Johnson9070
    wrote on last edited by
    #1

    Hi All,

    I want to do some filtering on a pandas dataframe displayd in a QTableview. There about 4500 rows and 15 columns.
    When i type the results are filtered immediately. Pretty fast for 200 rows but very slow for large datasets.
    Any idea how to improve the results ?

    Here is the code:

    class PandasTableModel(QAbstractTableModel):
        def __init__(self, data, parent=None):
            QAbstractItemModel.__init__(self, parent)
            self._data = data
            self.colors = dict()
    
        def rowCount(self, parent=None):
            return self._data.index.size
        def columnCount(self, parent=None):
            return self._data.columns.size
    
        def setData(self, index, value, role):
    
            if role == Qt.EditRole:
                # Set the value into the frame.
                self._data.iloc[index.row(), index.column()] = value
                return True
    
        def data(self, index, role=Qt.DisplayRole):
            if index.isValid():
                if role == Qt.DisplayRole:
                    return str(self._data.iloc[index.row(), index.column()])
                if role == Qt.EditRole:
                    return str(self._data.iloc[index.row(), index.column()])
                if role == Qt.BackgroundRole:
                    color = self.colors.get((index.row(), index.column()))
                    if color is not None:
                        return color
            return None
    
        def headerData(self, rowcol, orientation, role):
            if orientation == Qt.Horizontal and role == Qt.DisplayRole:
                return self._data.columns[rowcol]
            if orientation == Qt.Vertical and role == Qt.DisplayRole:
                return self._data.index[rowcol]
            return None
    
        def change_color(self, row, column, color):
            ix = self.index(row, column)
            self.colors[(row, column)] = color
            self.dataChanged.emit(ix, ix, (Qt.BackgroundRole,))
    
    class TableViewer(QtWidgets.QMainWindow):
    
        def __init__(self):
            super(TableViewer, self).__init__()
    
            self.ui = loadUi("QTableViewForm.ui", self)
            self.ui.cmdRun1.clicked.connect(self.RunFunction1)
            self.ui.cmdRun2.clicked.connect(self.RunFunction2)
            self.ui.inputFilter.textChanged.connect(self.SetFilteredView)
            self.showdata()
    
        def showdata(self):
            start = timeit.default_timer()
            print("Start LoadData")
    
            data = pd.read_pickle("productdata.pkl")
    
            self.model = PandasTableModel(data)
            self.ui.tableData.setModel(self.model)
            self.proxy_model = QSortFilterProxyModel()
            self.proxy_model.setFilterKeyColumn(-1)  # Search all columns.
            self.proxy_model.setSourceModel(self.model)
            self.proxy_model.sort(0, Qt.AscendingOrder)
            self.proxy_model.setFilterCaseSensitivity(False)
            self.ui.tableData.setModel(self.proxy_model)
    
            print("Stop LoadData")
            end = timeit.default_timer()
            print("Process Time: ", (end - start))
    
        def SetFilteredView(self):
    
            print("Start set_filter")
            filter_text = self.ui.inputFilter.text()
            self.proxy_model.setFilterFixedString(filter_text)
            filter_result = self.proxy_model.rowCount()
            self.ui.lblResult.setText("(" + str(filter_result) + " records)")
            print("Stop setFilteredView")
    if __name__ == "__main__":
        import sys
        app = QtWidgets.QApplication(sys.argv)
        win = TableViewer()
        win.show()
        sys.exit(app.exec_())
    
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      Do you really need to filter on all columns ?

      That said, since you have a pandas data frame, did you consider using its filtering capabilities ? It might better suit your needs.

      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
      • J Offline
        J Offline
        Johnson9070
        wrote on last edited by
        #3

        Hi SGaist , thx for your reply.

        The filter on all columns is exactly wat i need ;-)
        But not at this speed.

        The code below works great and filters really fast (>4500rows) but when i display the data using the "data" function of the model it becomes slow again. And i need the "data" function to do some formatting on the data in the table.
        I need to be able to filter semi-large tables (10000rows) at real time using the direct filter function.
        Here is the fast code:

        class PandasTableModel(QtGui.QStandardItemModel):
        
            def __init__(self, data, parent=None):
        
                QtGui.QStandardItemModel.__init__(self, parent)
                self._data = data
        
                for col in data.columns:
                    data_col = [QtGui.QStandardItem("{}".format(x)) for x in data[col].values]
                    self.appendColumn(data_col)
        
                return
        
            def rowCount(self, parent=None):
                return len(self._data.values)
        
            def columnCount(self, parent=None):
                return self._data.columns.size
        
            def headerData(self, x, orientation, role):
        
                if orientation == Qt.Horizontal and role == Qt.DisplayRole:
                    return self._data.columns[x]
                if orientation == Qt.Vertical and role == Qt.DisplayRole:
                    return self._data.index[x]
        
            def flags(self, index):
                if not index.isValid():
                    return Qt.ItemIsEnabled
        
                return super().flags(index) | Qt.ItemIsEditable  # add editable flag.
        
            def setData(self, index, value, role):
        
                if role == Qt.EditRole:
                    # Set the value into the frame.
                    self._data.iloc[index.row(), index.column()] = value
                    return True
        
                return False
        
        class TableViewer(QtWidgets.QMainWindow):
        
            def __init__(self):
                super(TableViewer, self).__init__()
        
                self.ui = loadUi("QTableViewForm.ui", self)
                self.ui.cmdRun1.clicked.connect(self.RunFunction1)
                self.ui.cmdRun2.clicked.connect(self.RunFunction2)
                self.ui.inputFilter.textChanged.connect(self.SetFilteredView)
        
                self.showdata()
        
            def showdata(self):
                start = timeit.default_timer()
                print("Start LoadData")
        
                data = pd.read_pickle("productdata.pkl")
        
                self.model = PandasTableModel(data)
                self.ui.tableData.setModel(self.model)
                self.proxy_model = QSortFilterProxyModel()
                self.proxy_model.setFilterKeyColumn(-1)  # Search all columns.
                self.proxy_model.setSourceModel(self.model)
                self.proxy_model.sort(0, Qt.AscendingOrder)
                self.proxy_model.setFilterCaseSensitivity(False)
                self.ui.tableData.setModel(self.proxy_model)
        
                print("Stop LoadData")
                end = timeit.default_timer()
                print("Process Time: ", (end - start))
        
            def SetFilteredView(self):
        
                # print("Start set_filter")
                filter_text = self.ui.inputFilter.text()
                self.proxy_model.setFilterFixedString(filter_text)
                filter_result = self.proxy_model.rowCount()
                self.ui.lblResult.setText("(" + str(filter_result) + " records)")
        
        
        if __name__ == "__main__":
            import sys
            app = QtWidgets.QApplication(sys.argv)
            win = TableViewer()
            win.show()
            sys.exit(app.exec_())
        
        1 Reply Last reply
        0
        • SGaistS Offline
          SGaistS Offline
          SGaist
          Lifetime Qt Champion
          wrote on last edited by
          #4

          Do you need to do a lot of data type conversions ?

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

          J 1 Reply Last reply
          0
          • SGaistS SGaist

            Do you need to do a lot of data type conversions ?

            J Offline
            J Offline
            Johnson9070
            wrote on last edited by
            #5

            @SGaist The idea is that i reed in a data file (csv , xls , ...) containing : url , string , date , email , phone numbers , .... so a bit of a mix. For the moment i have data showing 10.0 instead of 10 and 324758855252.0 instead of 0032.... or +32.....
            Also i would like to add some icons and so. I use face reco in this application so for example a user with a trained profile would have the faceId icon next to his name for example.

            1 Reply Last reply
            0
            • J Offline
              J Offline
              Johnson9070
              wrote on last edited by
              #6

              SGaist : Can i control the decorationrole through a function after a button click ?

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

                You can update the model as you wish, as long as you call the corresponding changed signal it will trigger the UI update.

                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