如何在 PyQt5/PySide2 中实现可过滤列表?
How to implement a filterable List in PyQt5/PySide2?
如何在 PyQt5/PySide2 中实现一个简单的可过滤列表?
测试代码:
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_()
过滤器本身有效,但 ListView 未正确刷新。当我更改过滤器时,列表不会更新,直到我用鼠标移动到一个条目。
您更改内部列表的信息并不会在视觉上通知某些内容已发生更改,对于每种类型的更改,必须在更改之前或之后调用某些方法。
在这种情况下,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()
另一种更优雅的解决方案是使用 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_()
如何在 PyQt5/PySide2 中实现一个简单的可过滤列表?
测试代码:
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_()
过滤器本身有效,但 ListView 未正确刷新。当我更改过滤器时,列表不会更新,直到我用鼠标移动到一个条目。
您更改内部列表的信息并不会在视觉上通知某些内容已发生更改,对于每种类型的更改,必须在更改之前或之后调用某些方法。
在这种情况下,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()
另一种更优雅的解决方案是使用 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_()