Как перечислить только видимые элементы в QTableView в pyqt
У меня есть код ниже, чтобы получить фильтры для QTableView. Но я не могу отфильтровать несколько столбцов одновременно.
т.е. если фильтр столбец 2 с row 0 col 0
и попробуйте отфильтровать столбец 2, он должен показывать только видимые уникальные значения column 2
(вероятно, это должно показать row 0 col 1
только), но теперь он показывает все элементы column 2
(row 0 col 1
, row 1 col 1
, row 2 col 1
)
#-*- coding:utf-8 -*-
from PyQt4 import QtCore, QtGui
class myWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(myWindow, self).__init__(parent)
self.centralwidget = QtGui.QWidget(self)
self.lineEdit = QtGui.QLineEdit(self.centralwidget)
self.view = QtGui.QTableView(self.centralwidget)
self.comboBox = QtGui.QComboBox(self.centralwidget)
self.label = QtGui.QLabel(self.centralwidget)
self.gridLayout = QtGui.QGridLayout(self.centralwidget)
self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
self.gridLayout.addWidget(self.view, 1, 0, 1, 3)
self.gridLayout.addWidget(self.comboBox, 0, 2, 1, 1)
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.setCentralWidget(self.centralwidget)
self.label.setText("Regex Filter")
self.model = QtGui.QStandardItemModel(self)
for rowName in range(3) * 5:
self.model.invisibleRootItem().appendRow(
[ QtGui.QStandardItem("row {0} col {1}".format(rowName, column))
for column in range(3)
]
)
self.proxy = QtGui.QSortFilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.view.setModel(self.proxy)
self.comboBox.addItems(["Column {0}".format(x) for x in range(self.model.columnCount())])
self.lineEdit.textChanged.connect(self.on_lineEdit_textChanged)
self.comboBox.currentIndexChanged.connect(self.on_comboBox_currentIndexChanged)
self.horizontalHeader = self.view.horizontalHeader()
self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked)
@QtCore.pyqtSlot(int)
def on_view_horizontalHeader_sectionClicked(self, logicalIndex):
self.logicalIndex = logicalIndex
self.menuValues = QtGui.QMenu(self)
self.signalMapper = QtCore.QSignalMapper(self)
self.comboBox.blockSignals(True)
self.comboBox.setCurrentIndex(self.logicalIndex)
self.comboBox.blockSignals(True)
valuesUnique = [ self.model.item(row, self.logicalIndex).text()
for row in range(self.model.rowCount())
]
actionAll = QtGui.QAction("All", self)
actionAll.triggered.connect(self.on_actionAll_triggered)
self.menuValues.addAction(actionAll)
self.menuValues.addSeparator()
for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))):
action = QtGui.QAction(actionName, self)
self.signalMapper.setMapping(action, actionNumber)
action.triggered.connect(self.signalMapper.map)
self.menuValues.addAction(action)
self.signalMapper.mapped.connect(self.on_signalMapper_mapped)
headerPos = self.view.mapToGlobal(self.horizontalHeader.pos())
posY = headerPos.y() + self.horizontalHeader.height()
posX = headerPos.x() + self.horizontalHeader.sectionPosition(self.logicalIndex)
self.menuValues.exec_(QtCore.QPoint(posX, posY))
@QtCore.pyqtSlot()
def on_actionAll_triggered(self):
filterColumn = self.logicalIndex
filterString = QtCore.QRegExp( "",
QtCore.Qt.CaseInsensitive,
QtCore.QRegExp.RegExp
)
self.proxy.setFilterRegExp(filterString)
self.proxy.setFilterKeyColumn(filterColumn)
@QtCore.pyqtSlot(int)
def on_signalMapper_mapped(self, i):
stringAction = self.signalMapper.mapping(i).text()
filterColumn = self.logicalIndex
filterString = QtCore.QRegExp( stringAction,
QtCore.Qt.CaseSensitive,
QtCore.QRegExp.FixedString
)
self.proxy.setFilterRegExp(filterString)
self.proxy.setFilterKeyColumn(filterColumn)
@QtCore.pyqtSlot(str)
def on_lineEdit_textChanged(self, text):
search = QtCore.QRegExp( text,
QtCore.Qt.CaseInsensitive,
QtCore.QRegExp.RegExp
)
self.proxy.setFilterRegExp(search)
@QtCore.pyqtSlot(int)
def on_comboBox_currentIndexChanged(self, index):
self.proxy.setFilterKeyColumn(index)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
main = myWindow()
main.show()
main.resize(400, 600)
sys.exit(app.exec_())
Когда я запускаю приведенный выше код, я получаю следующий вывод
Когда я нажимаю на заголовок столбца 2, отображается список фильтров, показанный ниже, и он отображается правильно (уникальные значения в этом столбце)...
Когда я выбираю row 0 col 1
в отображаемом фильтре я получаю следующий отфильтрованный список
Но снова, когда я нажимаю на заголовок столбца 2 для фильтра, он показывает тот же список, что и мое первое изображение. Все уникальные элементы столбца 2 (из вида модели), а не из проксифильтра. На самом деле это должно показать только row 0 col 1
как уникальные предметы в столбце 2 только row 0 col 1
1 ответ
Это потому, что вы все еще используете исходную модель для поиска строк. Данные в исходной модели не меняются. Только модель прокси фильтра отражает отфильтрованные изменения.
Так что вам просто нужно изменить свой поиск в вашем (очень долго с именем) on_view_horizontalHeader_sectionClicked
слот:
valuesUnique = [
self.proxy.index(row, self.logicalIndex).data().toString()
for row in xrange(self.proxy.rowCount())
]
Вы также можете удалить несколько преобразований для уникального набора:
valuesUnique = set(
self.proxy.index(row, self.logicalIndex).data().toString()
for row in xrange(self.proxy.rowCount())
)
...
for actionNumber, actionName in enumerate(sorted(valuesUnique)):
...
И еще одна маленькая вещь, на которую я хотел обратить внимание. Вы держите вокруг несколько временных объектов, которые никогда не удаляются. В этом же слоте вы создаете новый QMenu
а также QSignalMapper
каждый раз, но ты никогда не убираешь старые. Со временем вы просто зарабатываете все больше и больше.
Для QMenu
просто сделайте его локальной переменной и не используйте его в качестве родителя self
, Таким образом, он будет очищен после того, как исчезнет. И для QSignalMapper
Вы можете просто использовать deleteLater
позвоните, прежде чем создавать новый:
# local variable, and no parent
menuValues = QtGui.QMenu()
# delete the previous one
try:
self.signalMapper.deleteLater()
except:
pass
self.signalMapper = QtCore.QSignalMapper(self)