Я работаю с PyQt5 / PySide2. У меня есть QTableView
с QSortFilterProxyModel
, а данные обрабатываются с помощью QStandardItemModel
.
. Я использую метод QStandardItemModel.findItems()
, чтобы найти несколько ячеек в первой строке таблицы. Результатом является список QStandardItem
с. Теперь я хочу упорядочить эти элементы по строкам, в которых они отображаются в таблице GUI, то есть так, как их видит пользователь. Есть ли способ архивировать это? Чтобы перевести индексы прокси или модели в «просмотр» индексов.
Я думал, что это можно сделать с помощью метода QSortFilterProxyModel.mapFromSource()
, но, похоже, индексы прокси не имеют желаемого порядка.
здесь это минимальный воспроизводимый пример, написанный на PyQt5:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from collections import deque
from random import randint
class Splash(QWidget):
def __init__(self):
super().__init__()
# create model
self.model = QStandardItemModel(self)
self.model.setHorizontalHeaderLabels(["column 1", "column 2"])
# create sort proxy
self.proxy = NumberSortModel()
self.proxy.setSourceModel(self.model)
# create view
self.table = CustomQTableView(self)
self.table.setGeometry(0, 0, 275, 575)
self.table.setModel(self.proxy)
self.table.setSortingEnabled(True)
# create buttons
button = QPushButton('Find cells containing 1', self)
button.move(300, 70)
button.clicked.connect(lambda: self.table.search_string("1"))
button1 = QPushButton('next', self)
button1.move(300, 100)
button1.clicked.connect(self.table._search_next)
button2 = QPushButton('previous', self)
button2.move(300, 130)
button2.clicked.connect(self.table._search_previous)
# fill model
for i in range(15):
self.model.appendRow([QStandardItem(str(i)),
QStandardItem(str(randint(1, 100)))])
self.show()
# takes care of the coloring of results
class _HighlightDelegate(QStyledItemDelegate):
def __init__(self, parent=None) -> None:
QStyledItemDelegate.__init__(self, parent)
self._parent = parent
def paint(self, painter: "QPainter", option: "QStyleOptionViewItem",
index: "QModelIndex"):
painter.save()
if len(self._parent.proxy_indices) > 0:
if index == self._parent.proxy_indices[0]:
painter.fillRect(option.rect, Qt.red)
elif index in self._parent.proxy_indices:
painter.fillRect(option.rect, option.palette.highlight())
else:
if (option.state & QStyle.State_Selected):
painter.fillRect(option.rect, option.palette.highlight())
elif (option.state & QStyle.State_None):
painter.fillRect(option.rect, option.palette.base())
painter.drawText(option.rect, Qt.AlignLeft, index.data(Qt.DisplayRole))
painter.restore()
class CustomQTableView(QTableView):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.real_indices = deque()
self.proxy_indices = deque()
self.horizontalHeader().sortIndicatorChanged.connect(self._re_sort)
self.setItemDelegate(_HighlightDelegate(self))
def _re_sort(self):
# pretty print indices
def ind_to_py(indices):
py_ind = list()
for i in indices:
py_ind.append((i.row(), i.column(), i.data(Qt.DisplayRole)))
return py_ind
print("real ", ind_to_py(self.real_indices))
print("proxy ", ind_to_py(self.proxy_indices))
real_ind, proxy_ind = zip(*sorted(zip(self.real_indices, self.proxy_indices),
key=lambda x: (x[1].row(),
x[1].column())))
self.real_indices = deque(real_ind)
self.proxy_indices = deque(proxy_ind)
print("sorted real ", ind_to_py(self.real_indices))
print("sorted proxy", ind_to_py(self.proxy_indices))
print("---------------------------------------------------")
self.re_draw()
@property
def _model(self):
return self.model().sourceModel()
def re_draw(self):
self.viewport().update()
# we are always searching only in first column
def search_string(self, string: str):
indices = self._model.findItems(string, Qt.MatchContains, 0)
# get QModelIndex from found data
self.real_indices = deque([i.index() for i in indices])
self.proxy_indices = [QPersistentModelIndex(self.model().mapFromSource(i))
for i in self.real_indices]
# sort indeces according to their row and column
self._re_sort()
# update the view to highlight data
self.re_draw()
def _search_next(self):
self.real_indices.rotate(-1)
self.proxy_indices.rotate(-1)
self.re_draw()
def _search_previous(self):
self.real_indices.rotate(1)
self.proxy_indices.rotate(1)
self.re_draw()
# custom implementation to sort according to numbers not strings
class NumberSortModel(QSortFilterProxyModel):
def lessThan(self, left_index: "QModelIndex",
right_index: "QModelIndex") -> bool:
left_var: str = left_index.data(Qt.EditRole)
right_var: str = right_index.data(Qt.EditRole)
try:
return float(left_var) < float(right_var)
except (ValueError, TypeError):
pass
try:
return left_var < right_var
except TypeError: # in case of NoneType
return True
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
ex = Splash()
sys.exit(app.exec_())
Короче говоря, когда я запускаю поиск и щелкаю рядом, красная отмеченная ячейка перемещается вниз. Он перемещается вверх при нажатии предыдущей. Но когда я применяю сортировку, нажимая на заголовок таблицы, это портит следующие / предыдущие функции. Я хочу, чтобы красная ячейка всегда была на go вниз независимо от примененной сортировки, если щелкнуть по следующему так же, как и к предыдущему.
![app minimal example](https://i.stack.imgur.com/2NdQg.jpg)