Python 项目在QTableView中的可视位置

Python 项目在QTableView中的可视位置,python,pyqt,pyside,Python,Pyqt,Pyside,我正在使用PyQt5/PySide2。我有一个带有QSortFilterProxyModel的QTableView,数据由QStandardItemModel处理 我正在使用QStandardItemModel.findItems()方法在第一个表行中查找一些单元格。结果是QStandardItems的列表。现在我想按GUI表中显示的行(即用户看到它们的方式)对这些项目进行排序。有什么办法可以存档吗?。将代理或模型索引转换为“视图”索引 我认为这可以使用QSortFilterProxyModel

我正在使用PyQt5/PySide2。我有一个带有
QSortFilterProxyModel
QTableView
,数据由
QStandardItemModel
处理

我正在使用
QStandardItemModel.findItems()
方法在第一个表行中查找一些单元格。结果是
QStandardItem
s的列表。现在我想按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_())
从PyQt5.QtWidgets导入*
从PyQt5.QtGui导入*
从PyQt5.QtCore导入*
从集合导入deque
从随机导入randint
类启动(QWidget):
定义初始化(自):
super()。\uuuu init\uuuuu()
#创建模型
self.model=QStandardItemModel(self)
self.model.setHorizontalHeaderLabels([“第1列”、“第2列”])
#创建排序代理
self.proxy=NumberSortModel()
self.proxy.setSourceModel(self.model)
#创建视图
self.table=CustomQTableView(self)
self.table.setGeometry(0,0275575)
self.table.setModel(self.proxy)
self.table.setSortingEnabled(真)
#创建按钮
button=QPushButton('查找包含1'的单元格,self)
按钮。移动(300,70)
按钮。单击。连接(lambda:self.table.search_字符串(“1”))
button1=QPushButton(“下一步”,自我)
按钮1.移动(300100)
按钮1.单击.连接(self.table.\u搜索\u下一步)
button2=QPushButton('上一个',自身)
按钮2.移动(300130)
按钮2。单击。连接(self.table.\u搜索\u上一个)
#填充模型
对于范围(15)内的i:
self.model.appendRow([QStandardItem(str(i)),
QStandardItem(str(randint(1100)))]
self.show()
#负责结果的着色
类\u HighlightDelegate(QStyledItemDelegate):
def uuu init uuu(self,parent=None)->None:
QStyledItemDelegate.\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
self.\u parent=父
def油漆(自身,油漆工:“QPaint”,选项:“QStyleOptionViewItem”,
索引:“QModelIndex”):
保存
如果len(self.\u parent.proxy\u索引)>0:
如果索引==self.\u parent.proxy\u索引[0]:
painter.fillRect(option.rect,Qt.red)
elif索引在self.\u parent.proxy\u索引中:
painter.fillRect(option.rect,option.palete.highlight())
其他:
如果(选择option.state和QStyle.state):
painter.fillRect(option.rect,option.palete.highlight())
elif(option.state和QStyle.state\u None):
painter.fillRect(option.rect,option.palete.base())
painter.drawText(option.rect、Qt.AlignLeft、index.data(Qt.DisplayRole))
恢复
类CustomQTableView(QTableView):
定义初始化(self,*args,**kwargs)->无:
super()
self.real_index=deque()
self.proxy_index=deque()
self.horizontalHeader().sortInderChanged.connect(self.\u re\u sort)
self.setItemDelegate(_HighlightDelegate(self))
定义重新排序(自):
#漂亮的打印索引
定义索引到索引(索引):
py_ind=list()
对于指数中的i:
py_ind.append((i.row()、i.column()、i.data(Qt.DisplayRole)))
返回py_ind
打印(“真实”,索引到副本(self.real索引))
打印(“代理”,索引到副本(自代理索引))
实索引,代理索引=zip(*已排序(zip(self.real\u索引,self.proxy\u索引),
key=lambda x:(x[1]。行(),
x[1]。列())
self.real\u index=deque(real\u ind)
self.proxy\u index=deque(proxy\u ind)
打印(“已排序的实数”,ind_to_py(self.real_索引))
打印(“排序的代理”,索引到副本(自代理索引))
打印(“--------------------------------------------------------------”)
self.re_draw()
@财产
def_型号(自):
返回self.model().sourceModel()
def重新绘制(自):
self.viewport().update()
#我们总是只在第一列搜索
定义搜索字符串(self,字符串:str):
index=self.\u model.findItems(字符串,Qt.MatchContains,0)
#从找到的数据中获取QModelIndex
self.real_index=deque([i.index()表示索引中的i])
self.proxy_index=[QPersistentModelIndex(self.model().mapFromSource(i))
对于我自己来说,真实的指数]
#根据索引的行和列对索引进行排序
self.\u re\u sort()
#更新视图以突出显示数据
self.re_draw()
定义搜索下一步(自我):
自实数索引旋转(-1)
self.proxy_索引旋转(-1)
self.re_draw()
定义搜索上一个(自我):
自实指数旋转(1)
self.proxy_索引旋转(1)
self.re_draw()
#根据数字而不是字符串进行排序的自定义实现
类别编号排序模型(QSortFilterProxyModel):
def lessThan(自左索引:“QModelIndex”,
右索引:“QModelIndex”)->bool:
left_var:str=left_index.data(Qt.EditRole)
right\u var:str=right\u index.data(Qt.EditRole)
尝试:
返回浮点(左变量)<浮点(右变量)
除了(ValueError、TypeError):
通过
尝试:
返回左变量<右变量
类型错误除外:#如果为非类型
返回真值
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
导入系统
app=QApplication(sys.argv)
ex=飞溅()
sys.exit(app.exec_())