how to use QSortFilterProxyModel for filter a 2d array?

孤人 提交于 2019-12-13 03:23:50

问题


I have been struggling with this for a while, and i am not even sure i am using the right pyqt classes.

I have a QTableView that display a 2d array of integers and i wish to filter it to only show the integer under a value.

This is the example:

from PyQt4 import QtCore, QtGui
import random

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, parent=None):
        super(TableModel, self).__init__(parent)
        self._columns = 3
        self._parent = parent

    def rowCount(self, parent=QtCore.QModelIndex()): 
        return int( float( len( self._parent.data ) ) / float( self._columns ) )+1

    def columnCount(self, parent=QtCore.QModelIndex()): 
        return self._columns

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid(): 
            return None

        if role == QtCore.Qt.DisplayRole: 
            try:
                value = self._parent.data[ index.row() * self._columns + index.column() ]
            except:
                pass
            else:
                return value

        return None


class tableProxyModel(QtGui.QSortFilterProxyModel):

    def __init__(self, parent=None):
        super(tableProxyModel, self).__init__(parent)
        self._parent = parent
        self.filterColumn = []
        self.filterRow = []
        self.maxFilter =0

    def setFilter(self, value):
        #
        #  this will find the 2d position of the datas that need to be kept visible
        #  we need a list of the row and column axis 
        #
        self.maxFilter = value
        self.filterColumn = []
        self.filterRow = []

        for i in self._parent.data:
            if i < self.maxFilter:
                positionInList = [j for j,x in enumerate( self._parent.data ) if x == i][0]
                filterRow, filterColumn = self.getPosition( positionInList )
                self.filterColumn.append(filterColumn)
                self.filterRow.append(filterRow)

        self.invalidateFilter()

    def getPosition(self, value):
        #
        #  convert data list to a 2d array 
        #
        leftOver = value%self._parent._tm._columns
        filterRow = (value-leftOver) / self._parent._tm._columns
        filterColumn = leftOver
        return filterRow, filterColumn

    def filterAcceptsRow(self, row, parent):

        model = self.sourceModel()

        if  row in self.filterRow:
            return True
        else:
            return False

    def filterAcceptsColumn(self, column, parent):
        model = self.sourceModel()

        if column  in self.filterColumn:
            return True
        else:
            return False


if __name__=="__main__":
    from sys import argv, exit

    class Widget(QtGui.QWidget):
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self, parent)

            self.data = [ d for d in range(10) ]
            ###################################################
            ## this line shuffle the list, if you quote this line and keep the list in order , then the example works as expected
            random.shuffle(self.data)
            print self.data

            l=QtGui.QVBoxLayout( self )
            self._tm=TableModel( self )
            self._tv=QtGui.QTableView( )

            self._tpm=tableProxyModel( self )
            self._tpm.setSourceModel( self._tm )
            self._tv.setModel( self._tpm )
            l.addWidget( self._tv )

            #################################################
            ## apply the filter, only show numbers under 5
            self._tpm.setFilter(5)

    a=QtGui.QApplication(argv)
    w=Widget()
    w.show()
    w.raise_()
    exit(a.exec_())

If you execute this example, you can see it doesn't only show the values under 5 ...

How can i filter a QTableView filled with random values ? I wish to use the model/view system here.


回答1:


QSortFilterProxyModel only filters columns or rows, it does not filter elements, so the strategy for this case is to convert the table into a list, then filter the list since filtering a row is equivalent to filtering an element and finally converting the list into a table.

import random
import math
from PyQt4 import QtCore, QtGui


class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data, columns, parent=None):
        super(TableModel, self).__init__(parent)
        self._columns = columns
        self._data = data[:]

    def rowCount(self, parent=QtCore.QModelIndex()): 
        if parent.isValid() or self._columns == 0:
            return 0
        return math.ceil(len(self._data )*1.0/self._columns)

    def columnCount(self, parent=QtCore.QModelIndex()): 
        if parent.isValid():
            return 0
        return self._columns

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid(): 
            return
        if role == QtCore.Qt.DisplayRole: 
            try:
                value = self._data[ index.row() * self._columns + index.column() ]
                return value
            except:
                pass

class Table2ListProxyModel(QtGui.QIdentityProxyModel):
    def columnCount(self, parent=QtCore.QModelIndex()):
        return 1

    def rowCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return self.sourceModel().rowCount() * self.sourceModel().columnCount()

    def mapFromSource(self, sourceIndex):
        if sourceIndex.isValid() and sourceIndex.column() == 0 \
                and sourceIndex.row() < self.rowCount():
            r = sourceIndex.row()
            c = sourceIndex.column()
            row = c * sourceIndex.model().columnCount() + r
            return self.index(row, 0)
        return QtCore.QModelIndex()

    def mapToSource(self, proxyIndex):
        r = proxyIndex.row() / self.sourceModel().columnCount()
        c = proxyIndex.row() % self.sourceModel().columnCount()
        return self.sourceModel().index(r, c)

    def index(self, row, column, parent=QtCore.QModelIndex()):
        return self.createIndex(row, column)


class ListFilterProxyModel(QtGui.QSortFilterProxyModel):
    def setThreshold(self, value):
        setattr(self, "threshold", value)
        self.invalidateFilter()

    def filterAcceptsRow(self, row, parent):
        if hasattr(self, "threshold"):
            ix = self.sourceModel().index(row, 0)
            val = ix.data()
            if val is None:
                return False
            return val < getattr(self, "threshold")
        return True


class List2TableProxyModel(QtGui.QIdentityProxyModel):
    def __init__(self, columns=1, parent=None):
        super(List2TableProxyModel, self).__init__(parent)
        self._columns = columns

    def columnCount(self, parent=QtCore.QModelIndex()):
        return self._columns

    def rowCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return math.ceil(self.sourceModel().rowCount()/self._columns)

    def index(self, row, column, parent=QtCore.QModelIndex()):
        return self.createIndex(row, column)

    def data(self, index, role=QtCore.Qt.DisplayRole):
        r = index.row()
        c = index.column()
        row = r * self.columnCount() + c
        if row < self.sourceModel().rowCount():
            return super(List2TableProxyModel, self).data(index, role)

    def mapFromSource(self, sourceIndex):
        r = math.ceil(sourceIndex.row() / self.columnCount())
        c = sourceIndex.row() % self.columnCount()
        return self.index(r, c)

    def mapToSource(self, proxyIndex):
        if proxyIndex.isValid():
            r = proxyIndex.row()
            c = proxyIndex.column()
            row = r * self.columnCount() + c
            return self.sourceModel().index(row, 0)
        return QtCore.QModelIndex()


class Widget(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        data = [random.choice(range(10)) for i in range(20)]

        l = QtGui.QHBoxLayout(self)
        splitter = QtGui.QSplitter()
        l.addWidget(splitter)

        tv = QtGui.QTableView()
        lv = QtGui.QListView()
        lvf = QtGui.QListView()
        tvf = QtGui.QTableView()

        model = TableModel(data, 3, self)
        proxy1 = Table2ListProxyModel(self)
        proxy1.setSourceModel(model)
        proxy2 = ListFilterProxyModel(self)
        proxy2.setSourceModel(proxy1)
        proxy2.setThreshold(5)
        proxy3 = List2TableProxyModel(3, self)
        proxy3.setSourceModel(proxy2)

        tv.setModel(model)
        lv.setModel(proxy1)
        lvf.setModel(proxy2)
        tvf.setModel(proxy3)

        splitter.addWidget(tv)
        splitter.addWidget(lv)
        splitter.addWidget(lvf)
        splitter.addWidget(tvf)


if __name__=="__main__":
    import sys
    a=QtGui.QApplication(sys.argv)
    w=Widget()
    w.show()
    sys.exit(a.exec_())


来源:https://stackoverflow.com/questions/52416449/how-to-use-qsortfilterproxymodel-for-filter-a-2d-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!