QTableView中项目的可视位置

问题描述 投票:1回答:1

我正在使用PyQt5 / PySide2。我有一个QTableViewQSortFilterProxyModel,数据由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_())

简而言之,当我运行搜索并单击下一步时,红色标记的单元格向下移动。单击上一个时,它会向上移动。但是,当我通过单击表标题应用排序时,会弄乱下一个/上一个功能。我希望当下一个与上一个相同单击时,不管应用的排序如何,红色单元格总是会下降。

app minimal example

我正在使用PyQt5 / PySide2。我有一个带有QSortFilterProxyModel的QTableView,数据由QStandardItemModel处理。我正在使用QStandardItemModel.findItems()方法在...

python pyqt pyside
1个回答
2
投票

绘画的逻辑不应直接完成,而应通过代表进行绘画必须使用的角色来完成。

© www.soinside.com 2019 - 2024. All rights reserved.