Skip to content
Advertisement

How to use QSortFilterProxyModel to do data aggregation

I used QSortFilterProxyModel to filter the model data and display it in a tableView. Then I want to aggregate the filtered data, so I created a new tableView2 to display the filtered data aggregation.

I created a new QSortFilterProxyModel to aggregate the data, but I don’t know how to get the source data, and then increase the data

But with my current knowledge of QSortFilterProxyModel, I seem to be unable to start.

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_())

This is the effect I want to achieve according to the specified head key (a1, a3) to perform data aggregation:

enter image description here

Advertisement

Answer

A proxymodel is used to map items but in this case the goal is to count, so using a proxymodel seems unnecessary to me. Instead it is easier to use match() to count and connect the signals that indicate some change in the model to recalculate it.

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_())
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement