如何使用QSortFilterProxyModel做数据聚合

How to use QSortFilterProxyModel to do data aggregation

我使用 QSortFilterProxyModel 来过滤模型数据并将其显示在 tableView 中。 然后我想聚合过滤后的数据,所以我新建了一个tableView2来显示过滤后的数据聚合。

我新建了一个QSortFilterProxyModel来聚合数据,但是不知道如何获取源数据,然后增加数据

但以我目前对QSortFilterProxyModel的了解,我似乎无从下手。

import sys
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtCore import Qt


class FilterProxyModel(QtCore.QSortFilterProxyModel):
    def __init__(self, headers):
        super(FilterProxyModel, self).__init__(parent=None)
        self._headers = headers

    def headerData(self, section, orientation, role):
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                if self.columnCount() < section+1:
                    return QtCore.QVariant()
                else:
                    return self._headers[section]

        return super().headerData(section, orientation, role)

    def data(self, index, role=None):
        if role == Qt.DisplayRole:
            if index.row() < 1:
                return 1
            else:
                return QtCore.QVariant()

    def rowCount(self, parent=None, *args, **kwargs):
        return 1

    def columnCount(self, parent=None, *args, **kwargs):
        return len(self._headers)

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, datas):
        super(TableModel, self).__init__()
        self._datas = datas

    def data(self, index, role=Qt.DisplayRole):
        if role == Qt.DisplayRole:
            return self._datas[index.row()][index.column()]

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self._datas)

    def columnCount(self, parent=QtCore.QModelIndex()):
        if self._datas:
            return len(self._datas[0])
        else:
            return 0

class MainWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MainWidget, self).__init__(parent)
        self.datas = [('a1', 'b1', 'c'), ('a2', 'b1', 'c'), ('a3', 'b1', 'c'), ('a4', 'b1', 'c')]*25

        self.model = TableModel(self.datas)
        self.proxy_model = QtCore.QSortFilterProxyModel()

        self.proxy_model.setSourceModel(self.model)
        self.proxy_model.setFilterRegExp(QtCore.QRegExp('a1|a3'))
        self.proxy_model.setFilterKeyColumn(0)

        self.proxy_model2 = FilterProxyModel(['a1', 'a3'])
        self.proxy_model2.setSourceModel(self.model)
        self.proxy_model2.setFilterRegExp(QtCore.QRegExp('a1|a3'))
        self.proxy_model2.setFilterKeyColumn(0)

        self.treeview = QtWidgets.QTableView()
        self.treeview.setModel(self.proxy_model)

        self.treeview2 = QtWidgets.QTableView()
        self.treeview2.setModel(self.proxy_model2)
        layout = QtWidgets.QVBoxLayout(self)
        hlayout = QtWidgets.QHBoxLayout()
        hlayout.addWidget(self.treeview)
        hlayout.addWidget(self.treeview2)
        layout.addLayout(hlayout)

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    main = MainWidget()
    main.show()
    sys.exit(app.exec_())

这是我要实现的根据指定的head key(a1,a3)进行数据聚合的效果:

代理模型用于映射项目,但在这种情况下,目标是计数,所以使用代理模型对我来说似乎没有必要。相反,使用 match() 来计算和连接指示模型中某些变化的信号以重新计算它会更容易。

import sys

from PyQt5 import QtCore, QtGui, QtWidgets


class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, datas):
        super(TableModel, self).__init__()
        self._datas = datas

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.DisplayRole:
            return self._datas[index.row()][index.column()]

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self._datas)

    def columnCount(self, parent=QtCore.QModelIndex()):
        if self._datas:
            return len(self._datas[0])
        else:
            return 0


class MainWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MainWidget, self).__init__(parent)
        self.datas = [
            ("a1", "b1", "c"),
            ("a2", "b1", "c"),
            ("a3", "b1", "c"),
            ("a4", "b1", "c"),
        ] * 25

        self.model = TableModel(self.datas)
        self.proxy_model = QtCore.QSortFilterProxyModel()

        self.proxy_model.setSourceModel(self.model)
        self.proxy_model.setFilterRegExp(QtCore.QRegExp("a1|a3"))
        self.proxy_model.setFilterKeyColumn(0)

        self.model2 = QtGui.QStandardItemModel()
        self.model2.setHorizontalHeaderLabels(["a1", "a3"])

        self.view1 = QtWidgets.QTableView()
        self.view1.setModel(self.proxy_model)

        self.view2 = QtWidgets.QTableView()
        self.view2.setModel(self.model2)
        layout = QtWidgets.QVBoxLayout(self)
        hlayout = QtWidgets.QHBoxLayout()
        hlayout.addWidget(self.view1)
        hlayout.addWidget(self.view2)
        layout.addLayout(hlayout)

        self.model.dataChanged.connect(self.update_model2)
        self.model.rowsInserted.connect(self.update_model2)
        self.model.rowsRemoved.connect(self.update_model2)
        self.model.columnsInserted.connect(self.update_model2)
        self.model.columnsRemoved.connect(self.update_model2)
        self.model.modelReset.connect(self.update_model2)

        self.update_model2()

    def update_model2(self):
        self.model2.setRowCount(0)
        column = 0
        for i in range(self.model2.columnCount()):
            header_item = self.model2.horizontalHeaderItem(i)
            if header_item is None:
                return
            text = header_item.text()
            indexes = self.model.match(
                self.model.index(0, column),
                QtCore.Qt.DisplayRole,
                text,
                hits=-1,
                flags=QtCore.Qt.MatchExactly,
            )
            item = QtGui.QStandardItem()
            item.setData(len(indexes), QtCore.Qt.DisplayRole)
            self.model2.setItem(0, i, item)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    main = MainWidget()
    main.show()
    sys.exit(app.exec_())