qtpyqtqtableviewqsortfilterproxymodel

QTableView with QSortFilterProxyModel don't find correct row because is ordered


I have an application in which I am experiencing a problem.

In order to explain the problem I have simplified it to the maximum to be able to expose it here.

My problem is the following:

Option 1 Not Sorted. Works.

Option 1 sorted

The error is:

QSortFilterProxyModel: index from wrong model passed to mapToSource

Option 2. MaptoSource

I am clear that the model row must be converted to a QSortFilterProxyModel row, but I don't know how.

Any ideas?

Thank you very much.

Sample Code:

from PyQt5.QtWidgets import (QApplication, QPushButton,QComboBox,QMainWindow)
from PyQt5.QtWidgets import (QTableView,QAbstractItemView,QVBoxLayout,QLineEdit,QWidget)
from PyQt5.QtGui import QStandardItemModel,QStandardItem
from PyQt5.QtCore import QSortFilterProxyModel,Qt

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setup_gui()
        
    def setup_gui(self):
        widget=QWidget(self)
        self.setCentralWidget(widget)
        
        self.qlineedit = QLineEdit()
        self.qlineedit.textChanged.connect(self.find_text)
        self.table_view = QTableView()
        
        self.main_layout = QVBoxLayout(widget)
        self.main_layout.addWidget(self.qlineedit)
        self.main_layout.addWidget(self.table_view)
        
        self.table_model = QStandardItemModel()
        
        row=0
        for b in ['Fast','Medium','Cool','Double']:
            item=QStandardItem(b)
            self.table_model.setItem(row, 0, item)
            row+=1
                                
        self.model_filter_proxy = QSortFilterProxyModel()
        self.model_filter_proxy.setSourceModel(self.table_model)
        self.model_filter_proxy.sort(0)
        self.table_view.setModel(self.model_filter_proxy)
        
        # Setup Table view
        # Selection one row at same time
        self.table_view.setSelectionMode(QAbstractItemView.SingleSelection)
        # Seleccionar toda la fila
        self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table_view.setStyleSheet("QTableView::item:selected{ background-color: green ; selection-color: white; }")
        
    def find_text(self,text):
        column=0
        self.table_view.clearSelection()                                    
        start = self.table_model.index(0, column)
        matches = self.table_model.match(
            start, 
            Qt.DisplayRole,
            text, 
            hits=1, 
            flags=Qt.MatchContains
        )

        if matches:
            # Option 1
            index = matches[0]
            row=index.row()
            # Option 2
            mapped_index = self.model_filter_proxy.mapToSource(matches[0])
            row=mapped_index.row()
            self.table_view.selectRow(row)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    app.setStyle("fusion")
    w=MainWindow()
    w.show()

    sys.exit(app.exec_())

Solution

  • With @musicamante comments, the correct code is:

    from PyQt5.uic import loadUiType, loadUi
    from PyQt5.QtWidgets import (QApplication, QPushButton,QComboBox,QMainWindow)
    from PyQt5.QtWidgets import (QTableView,QAbstractItemView,QVBoxLayout,QLineEdit,QWidget)
    from PyQt5.QtGui import QStandardItemModel,QStandardItem
    from PyQt5.QtCore import QSortFilterProxyModel,Qt
    
    class MainWindow(QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
            self.setup_gui()
            
        def setup_gui(self):
            widget=QWidget(self)
            self.setCentralWidget(widget)
            
            self.qlineedit = QLineEdit()
            self.qlineedit.textChanged.connect(self.find_text)
            self.table_view = QTableView()
            
            self.main_layout = QVBoxLayout(widget)
            self.main_layout.addWidget(self.qlineedit)
            self.main_layout.addWidget(self.table_view)
            
            self.table_model = QStandardItemModel()
            
            row=0
            for b in ['Fast','Medium','Cool','Double']:
                item=QStandardItem(b)
                self.table_model.setItem(row, 0, item)
                row+=1
                                    
            self.model_filter_proxy = QSortFilterProxyModel()
            self.model_filter_proxy.setSourceModel(self.table_model)
            self.model_filter_proxy.sort(0)
            self.table_view.setModel(self.model_filter_proxy)
            
            # Setup Table view
            # Selection one row at same time
            self.table_view.setSelectionMode(QAbstractItemView.SingleSelection)
            # Seleccionar toda la fila
            self.table_view.setSelectionBehavior(QAbstractItemView.SelectRows)
            self.table_view.setStyleSheet("QTableView::item:selected{ background-color: green ; selection-color: white; }")
            
        def find_text(self,text):
            column=0
            self.table_view.clearSelection()                                    
            start = self.table_model.index(0, column)
            matches = self.table_model.match(
                start, 
                Qt.DisplayRole,
                text, 
                hits=1, 
                flags=Qt.MatchContains
            )
    
            if matches:
                # Option 1
                #index = matches[0]
                #row=index.row()
                # Option 2
                #mapped_index = self.model_filter_proxy.mapToSource(matches[0])
                mapped_index = self.model_filter_proxy.mapFromSource(matches[0])
                row=mapped_index.row()
                self.table_view.selectRow(row)
    
    if __name__ == '__main__':
        import sys
        app = QApplication(sys.argv)
        app.setStyle("fusion")
        w=MainWindow()
        w.show()
    
        sys.exit(app.exec_())