简体   繁体   English

如何在 PyQt5/PySide2 中实现可过滤列表?

[英]How to implement a filterable List in PyQt5/PySide2?

How can I implement a simple filterable List in PyQt5/PySide2?如何在 PyQt5/PySide2 中实现一个简单的可过滤列表?

Test code:测试代码:

import sys

from PySide2.QtCore import QAbstractListModel
from PySide2.QtGui import Qt
from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QLineEdit, QListView


class MyModel(QAbstractListModel):
    def __init__(self):
        QAbstractListModel.__init__(self)
        self.test_data = ['Test','abc', 'Test2', 'aaa', 'bbb']
        self.filtered_data = self.test_data
        self.filter = ''
        self.refresh()

    def refresh(self):
        if self.filter:
            self.filtered_data  = [x for x in self.test_data if self.filter in x]
        else:
            self.filtered_data = self.test_data

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return self.filtered_data[index.row()]

    def rowCount(self, index):
        return len(self.filtered_data)


def text_changed():
    model.filter = le.text()
    model.refresh()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QWidget()

    le = QLineEdit()
    le.textChanged.connect(text_changed)
    model = MyModel()
    lv = QListView(model=model)

    layout = QVBoxLayout()
    layout.addWidget(le)
    layout.addWidget(lv)

    window.setLayout(layout)
    window.show()
    app.exec_()


The Filter itself works, but the ListView is not refreshed properly. Filter 本身可以工作,但 ListView 没有正确刷新。 When I change the filter, the list is not updated, until I move with the mouse to a entry.当我更改过滤器时,列表不会更新,直到我用鼠标移动到一个条目。

That you change the information of an internal list does not notify in sight that something has changed, for each type of change certain methods must be invoked before or after the change.您更改内部列表的信息并不会立即通知某些更改,对于每种类型的更改,必须在更改之前或之后调用某些方法。

In this case, beginResetModel and endResetModel are enough在这种情况下, beginResetModel 和 endResetModel 就足够了

def refresh(self):
    self.beginResetModel()
    if self.filter:
        self.filtered_data  = [x for x in self.test_data if self.filter in x]
    else:
        self.filtered_data = self.test_data
    self.endResetModel()

Instead another more elegant solution is to use a QSortFilterProxyModel:相反,另一个更优雅的解决方案是使用 QSortFilterProxyModel:

class MyModel(QAbstractListModel):
    def __init__(self):
        QAbstractListModel.__init__(self)
        self.test_data = ['Test','abc', 'Test2', 'aaa', 'bbb']
        self.filtered_data = self.test_data

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return self.filtered_data[index.row()]

    def rowCount(self, index):
        return len(self.filtered_data)


def text_changed():
    proxy.filter = le.text()


class SortFilterProxyModel(QSortFilterProxyModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._filter = ""
    def filterAcceptsRow(self, sourceRow, sourceParent):
        index = self.sourceModel().index(sourceRow, 0, sourceParent)
        if self.filter:
            return self.filter in index.data()
        return True

    @property
    def filter(self):
        return self._filter
    
    @filter.setter
    def filter(self, f):
        self._filter = f
        self.invalidateFilter()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QWidget()

    le = QLineEdit()
    le.textChanged.connect(text_changed)
    model = MyModel()
    proxy = SortFilterProxyModel()
    proxy.setSourceModel(model)
    lv = QListView(model=proxy)

    layout = QVBoxLayout()
    layout.addWidget(le)
    layout.addWidget(lv)

    window.setLayout(layout)
    window.show()
    app.exec_()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM