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

Time:05-12

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:

  • I have a QTableView, associated to a QSortFilterProxyModel.

  • I do a search in the table to find the record that contains the searched text, and once found, I select that row.

  • If the QSortFilterProxyModelis not sorted, it works correctly (Option 1).

Option 1 Not Sorted. Works.

  • If QSortFilterProxyModel is sorted don't works (Option 2).

Option 1 sorted

  • I have tried to convert the row that the model gives me through maptoSource and it has not been possible (Option 2).

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_())

CodePudding user response:

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_())
  • Related