Search code examples
pythonfilteringpysideqtreeview

Search Filter in QTreeview Pyside


I have a working sample below that adds the search filter functionality. I feel like how I'm doing it is overkill. It was pieced together from projects I've found online. Do i really have to create this entire SearchProxyFilter to do a simple string filtering search on my list of items?

I have a feeling that I'm doing something wrong or more than i need to. Any suggestions would be appreciated.

enter image description here

#######
# Imports
#######
import sys
import os
from PySide import QtGui, QtCore

#######
# Main
#######
class SortModel(QtGui.QSortFilterProxyModel):

    def __init__(self, *args, **kwargs):
        super(SortModel, self).__init__(*args, **kwargs)
        self._pattern = ''

    @property
    def pattern(self):
        return self._pattern

    def filterAcceptsRow(self, sourceRow, sourceParent):
        if not self.pattern:
            return True

        sm = self.sourceModel()
        modelIdx = sm.index(sourceRow, 0)
        if modelIdx.isValid():
            txt = modelIdx.data(role=QtCore.Qt.DisplayRole)
            if not self.pattern.lower() in txt.lower():
                return False

        return True

    def lessThan(self, left, right):
        leftData = self.sourceModel().data(left)
        rightData = self.sourceModel().data(right)

        if leftData:
            leftData = leftData.lower()
        if rightData:
            rightData = rightData.lower()

        return leftData < rightData

class UserBrowser(QtGui.QDialog):

    def __init__(self, parent=None):
        super(UserBrowser, self).__init__(parent)
        self.resize(300, 500)
        self.setWindowTitle('Users')
        self.setModal(True)
        self.initUI()

    def initUI(self):
        self.results = ''
        self.filepath = ''

        self.ui_search = QtGui.QLineEdit()
        self.ui_search.setPlaceholderText('Search...')

        self.source_model = QtGui.QStandardItemModel()
        self.user_model = SortModel(self)
        self.user_model.setSourceModel(self.source_model)
        self.user_model.setDynamicSortFilter(True)
        self.user_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)

        self.ui_items = QtGui.QTreeView()
        self.ui_items.setModel(self.user_model)
        self.ui_items.setAlternatingRowColors(False)
        self.ui_items.setSortingEnabled(True)
        self.ui_items.sortByColumn(0, QtCore.Qt.AscendingOrder)
        self.ui_items.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.ui_items.header().setResizeMode(QtGui.QHeaderView.ResizeToContents)
        self.ui_items.setHeaderHidden(True)
        self.ui_items.setRootIsDecorated(False)

        gdl = QtGui.QGridLayout()
        gdl.setContentsMargins(10, 10, 10, 10)
        gdl.addWidget(self.ui_search, 0, 0)
        gdl.addWidget(self.ui_items, 1, 0)
        self.setLayout(gdl)
        self.create_model()

        self.ui_search.textChanged.connect(self.changed_text)

    # Methods
    def create_model(self):
        model = self.source_model
        model.clear()
        model.setHorizontalHeaderLabels(['Names'])

        users = ['kevin','Marry','Doug','Leslie','Michelle','John']
        for x in users:
            item = QtGui.QStandardItem()
            item.setData(x , role=QtCore.Qt.DisplayRole)
            model.appendRow(item)

        self.ui_items.sortByColumn(0, QtCore.Qt.AscendingOrder)

    # Actions
    def changed_text(self, text):
        self.user_model._pattern = text
        self.user_model.invalidateFilter()

def main():
    app = QtGui.QApplication(sys.argv)
    ex = UserBrowser()
    ex.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

Solution

  • This can be done much more simply with a QCompleter:

    class UserBrowser(QtGui.QDialog):
        ...    
        def initUI(self):
            ...
            self.completer = QtGui.QCompleter(self)
            self.completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
            self.completer.setModelSorting(
                QtGui.QCompleter.CaseInsensitivelySortedModel)
    
            self.ui_items = QtGui.QTreeView()
            self.ui_items.setModel(self.completer.completionModel())
            ...
            self.create_model()
            ...    
            self.ui_search.textChanged.connect(self.completer.setCompletionPrefix)
    
        # Methods
        def create_model(self):
            users = ['kevin','Marry','Doug','Leslie','Michelle','John']
            users.sort(key=str.lower)
            self.completer.setModel(QtGui.QStringListModel(users))