如何过滤掉单个项目而不是仅仅在行和列的基础上?
How to filter out individual items instead of just on a row and column basis?
我正在尝试根据字符串匹配过滤掉 table 中的项目。
我有一个 QTableView 显示代理模型以允许过滤,但是如果 (0,0) 和 (1,1) 中的项目匹配我的字符串但项目 (1,0) 不匹配,它仍会显示。
例如:
from PySide.QtGui import *
from PySide.QtCore import *
class CustomProxyFilter(QSortFilterProxyModel):
def __init__(self):
super(CustomProxyFilter, self).__init__()
def filterAcceptsColumn(self, source_column, parent):
"""Re-implementing built-in to hide columns with non matches."""
model = self.sourceModel()
matched_string = self.filterRegExp().pattern().lower()
for row in range(model.rowCount()):
item = model.item(row, source_column)
if item and matched_string in model.item(row, source_column).text().lower():
return True
return False
class CustomTableView(QTableView):
"""Table view."""
def __init__(self, line_edit):
super(CustomTableView, self).__init__()
custom_model = StandardTableModel()
items = ["apple", "banana", "applebanana"]
for i, item in enumerate(items):
for v, second_item in enumerate(items):
custom_model.setItem(i, v, QStandardItem(item))
self.proxy_model = CustomProxyFilter()
self.proxy_model.setSourceModel(custom_model)
self.setModel(self.proxy_model)
line_edit.textChanged.connect(self.proxy_model.setFilterRegExp)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.setLayout(QVBoxLayout())
self.line_edit = QLineEdit()
self.layout().addWidget(self.line_edit)
self.layout().addWidget(CustomTableView(self.line_edit))
我希望我的 table 看起来像
a|b|c
-----
c|a|b
经过 "a" 过滤后的结果 table 将是
a|a
我当前的解决方案显示。
a|b
---
c|a
其他案例更新
a|a|c
-----
a|x|b
-----
c|b|a
变成
a|a|a
-----
a
这个案例
a|a|y|c
-------
a|a|w|a
-------
c|a|w|w
变成
a|a|a|a
-----
a|a|
基本上每个项目都会在可能的情况下向左上角移动。当他们是不同的名字时,他们会像这样按字母顺序排列自己
1|2|3|4
-------
5|6|7|8
为了实现您的要求,我已经通过以下方式实现了几个连接的代理:
model-->Table2ListProxyModel-->QSortFilterProxyModel-->List2TableProxyModel
想法是将其转换为列表的结构,因为过滤行相当于过滤项目,相同的代理对其进行排序,然后我们将列表转换为table。
import math
from PySide import QtCore, QtGui
class Table2ListProxyModel(QtGui.QSortFilterProxyModel):
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return self.sourceModel().rowCount() * self.sourceModel().columnCount()
def mapFromSource(self, sourceIndex):
if (
sourceIndex.isValid()
and sourceIndex.column() == 0
and sourceIndex.row() < self.rowCount()
):
r = sourceIndex.row()
c = sourceIndex.column()
row = c * sourceIndex.model().columnCount() + r
return self.index(row, 0)
return QtCore.QModelIndex()
def mapToSource(self, proxyIndex):
r = proxyIndex.row() / self.sourceModel().columnCount()
c = proxyIndex.row() % self.sourceModel().columnCount()
return self.sourceModel().index(r, c)
def index(self, row, column, parent=QtCore.QModelIndex()):
return self.createIndex(row, column)
class List2TableProxyModel(QtGui.QSortFilterProxyModel):
def __init__(self, columns=1, parent=None):
super(List2TableProxyModel, self).__init__(parent)
self._columns = columns
def columnCount(self, parent=QtCore.QModelIndex()):
r = self.sourceModel().rowCount()
if r < self._columns:
return r
return self._columns
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
row = math.ceil(self.sourceModel().rowCount()*1.0 / self._columns)
return row
def index(self, row, column, parent=QtCore.QModelIndex()):
return self.createIndex(row, column)
def data(self, index, role=QtCore.Qt.DisplayRole):
r = index.row()
c = index.column()
row = r * self.columnCount() + c
if row < self.sourceModel().rowCount():
return super(List2TableProxyModel, self).data(index, role)
def mapFromSource(self, sourceIndex):
r = math.ceil(sourceIndex.row()*1.0 / self.columnCount())
c = sourceIndex.row() % self.columnCount()
return self.index(r, c)
def mapToSource(self, proxyIndex):
if proxyIndex.isValid():
r = proxyIndex.row()
c = proxyIndex.column()
row = r * self.columnCount() + c
return self.sourceModel().index(row, 0)
return QtCore.QModelIndex()
def setSourceModel(self, model):
model.rowsRemoved.connect(self.reset_model)
model.rowsInserted.connect(self.reset_model)
model.dataChanged.connect(self.reset_model)
model.rowsMoved.connect(self.reset_model)
model.layoutChanged.connect(self.reset_model)
model.modelReset.connect(self.reset_model)
super(List2TableProxyModel, self).setSourceModel(model)
@QtCore.Slot()
def reset_model(self):
QtCore.QTimer.singleShot(0, self.invalidate)
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
return str(section)
return super(List2TableProxyModel, self).headerData(
section, orientation, role
)
class CustomTableView(QtGui.QTableView):
def __init__(self, parent=None):
super(CustomTableView, self).__init__(parent)
custom_model = QtGui.QStandardItemModel()
datas = (("ad", "cd", "ef"), ("ab", "ce", "eg"), ("aa", "cb", "eh"))
for i, data in enumerate(datas):
for v, text in enumerate(data):
custom_model.setItem(i, v, QtGui.QStandardItem(text))
self.proxy_list = Table2ListProxyModel(self)
self.proxy_list.setSourceModel(custom_model)
self.proxy_sort_filter = QtGui.QSortFilterProxyModel(self)
self.proxy_sort_filter.setSourceModel(self.proxy_list)
self.proxy_table = List2TableProxyModel(
columns=custom_model.columnCount(), parent=self
)
self.proxy_table.setSourceModel(self.proxy_sort_filter)
self.setModel(self.proxy_table)
@QtCore.Slot(str)
def setFilter(self, text):
self.proxy_sort_filter.setFilterWildcard(
"*{}*".format(text) if text else ""
)
self.proxy_sort_filter.sort(0 if text else -1, QtCore.Qt.AscendingOrder)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.line_edit = QtGui.QLineEdit()
self.tableview = CustomTableView()
lay = QtGui.QVBoxLayout(self)
lay.addWidget(self.line_edit)
lay.addWidget(self.tableview)
self.line_edit.textChanged.connect(self.tableview.setFilter)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
我正在尝试根据字符串匹配过滤掉 table 中的项目。
我有一个 QTableView 显示代理模型以允许过滤,但是如果 (0,0) 和 (1,1) 中的项目匹配我的字符串但项目 (1,0) 不匹配,它仍会显示。
例如:
from PySide.QtGui import *
from PySide.QtCore import *
class CustomProxyFilter(QSortFilterProxyModel):
def __init__(self):
super(CustomProxyFilter, self).__init__()
def filterAcceptsColumn(self, source_column, parent):
"""Re-implementing built-in to hide columns with non matches."""
model = self.sourceModel()
matched_string = self.filterRegExp().pattern().lower()
for row in range(model.rowCount()):
item = model.item(row, source_column)
if item and matched_string in model.item(row, source_column).text().lower():
return True
return False
class CustomTableView(QTableView):
"""Table view."""
def __init__(self, line_edit):
super(CustomTableView, self).__init__()
custom_model = StandardTableModel()
items = ["apple", "banana", "applebanana"]
for i, item in enumerate(items):
for v, second_item in enumerate(items):
custom_model.setItem(i, v, QStandardItem(item))
self.proxy_model = CustomProxyFilter()
self.proxy_model.setSourceModel(custom_model)
self.setModel(self.proxy_model)
line_edit.textChanged.connect(self.proxy_model.setFilterRegExp)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.setLayout(QVBoxLayout())
self.line_edit = QLineEdit()
self.layout().addWidget(self.line_edit)
self.layout().addWidget(CustomTableView(self.line_edit))
我希望我的 table 看起来像
a|b|c
-----
c|a|b
经过 "a" 过滤后的结果 table 将是
a|a
我当前的解决方案显示。
a|b
---
c|a
其他案例更新
a|a|c
-----
a|x|b
-----
c|b|a
变成
a|a|a
-----
a
这个案例
a|a|y|c
-------
a|a|w|a
-------
c|a|w|w
变成
a|a|a|a
-----
a|a|
基本上每个项目都会在可能的情况下向左上角移动。当他们是不同的名字时,他们会像这样按字母顺序排列自己
1|2|3|4
-------
5|6|7|8
为了实现您的要求,我已经通过以下方式实现了几个连接的代理:
model-->Table2ListProxyModel-->QSortFilterProxyModel-->List2TableProxyModel
想法是将其转换为列表的结构,因为过滤行相当于过滤项目,相同的代理对其进行排序,然后我们将列表转换为table。
import math
from PySide import QtCore, QtGui
class Table2ListProxyModel(QtGui.QSortFilterProxyModel):
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return self.sourceModel().rowCount() * self.sourceModel().columnCount()
def mapFromSource(self, sourceIndex):
if (
sourceIndex.isValid()
and sourceIndex.column() == 0
and sourceIndex.row() < self.rowCount()
):
r = sourceIndex.row()
c = sourceIndex.column()
row = c * sourceIndex.model().columnCount() + r
return self.index(row, 0)
return QtCore.QModelIndex()
def mapToSource(self, proxyIndex):
r = proxyIndex.row() / self.sourceModel().columnCount()
c = proxyIndex.row() % self.sourceModel().columnCount()
return self.sourceModel().index(r, c)
def index(self, row, column, parent=QtCore.QModelIndex()):
return self.createIndex(row, column)
class List2TableProxyModel(QtGui.QSortFilterProxyModel):
def __init__(self, columns=1, parent=None):
super(List2TableProxyModel, self).__init__(parent)
self._columns = columns
def columnCount(self, parent=QtCore.QModelIndex()):
r = self.sourceModel().rowCount()
if r < self._columns:
return r
return self._columns
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
row = math.ceil(self.sourceModel().rowCount()*1.0 / self._columns)
return row
def index(self, row, column, parent=QtCore.QModelIndex()):
return self.createIndex(row, column)
def data(self, index, role=QtCore.Qt.DisplayRole):
r = index.row()
c = index.column()
row = r * self.columnCount() + c
if row < self.sourceModel().rowCount():
return super(List2TableProxyModel, self).data(index, role)
def mapFromSource(self, sourceIndex):
r = math.ceil(sourceIndex.row()*1.0 / self.columnCount())
c = sourceIndex.row() % self.columnCount()
return self.index(r, c)
def mapToSource(self, proxyIndex):
if proxyIndex.isValid():
r = proxyIndex.row()
c = proxyIndex.column()
row = r * self.columnCount() + c
return self.sourceModel().index(row, 0)
return QtCore.QModelIndex()
def setSourceModel(self, model):
model.rowsRemoved.connect(self.reset_model)
model.rowsInserted.connect(self.reset_model)
model.dataChanged.connect(self.reset_model)
model.rowsMoved.connect(self.reset_model)
model.layoutChanged.connect(self.reset_model)
model.modelReset.connect(self.reset_model)
super(List2TableProxyModel, self).setSourceModel(model)
@QtCore.Slot()
def reset_model(self):
QtCore.QTimer.singleShot(0, self.invalidate)
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
return str(section)
return super(List2TableProxyModel, self).headerData(
section, orientation, role
)
class CustomTableView(QtGui.QTableView):
def __init__(self, parent=None):
super(CustomTableView, self).__init__(parent)
custom_model = QtGui.QStandardItemModel()
datas = (("ad", "cd", "ef"), ("ab", "ce", "eg"), ("aa", "cb", "eh"))
for i, data in enumerate(datas):
for v, text in enumerate(data):
custom_model.setItem(i, v, QtGui.QStandardItem(text))
self.proxy_list = Table2ListProxyModel(self)
self.proxy_list.setSourceModel(custom_model)
self.proxy_sort_filter = QtGui.QSortFilterProxyModel(self)
self.proxy_sort_filter.setSourceModel(self.proxy_list)
self.proxy_table = List2TableProxyModel(
columns=custom_model.columnCount(), parent=self
)
self.proxy_table.setSourceModel(self.proxy_sort_filter)
self.setModel(self.proxy_table)
@QtCore.Slot(str)
def setFilter(self, text):
self.proxy_sort_filter.setFilterWildcard(
"*{}*".format(text) if text else ""
)
self.proxy_sort_filter.sort(0 if text else -1, QtCore.Qt.AscendingOrder)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.line_edit = QtGui.QLineEdit()
self.tableview = CustomTableView()
lay = QtGui.QVBoxLayout(self)
lay.addWidget(self.line_edit)
lay.addWidget(self.tableview)
self.line_edit.textChanged.connect(self.tableview.setFilter)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())